......@@ -32,6 +32,10 @@ export default {
type: String,
required: true,
},
issueProjectPath: {
type: String,
required: true,
},
},
computed: {
...mapState('details', ['error', 'loading', 'loadingStacktrace', 'stacktraceData']),
......@@ -82,9 +86,9 @@ export default {
<div v-else-if="showDetails" class="error-details">
<div class="top-area align-items-center justify-content-between py-3">
<span v-if="!loadingStacktrace && stacktrace" v-html="reported"></span>
<!-- <gl-button class="my-3 ml-auto" variant="success">
{{ __('Create Issue') }}
</gl-button>-->
<gl-button variant="success" :href="issueProjectPath">
{{ __('Create issue') }}
</gl-button>
</div>
<div>
<tooltip-on-truncate :title="error.title" truncate-target="child" placement="top">
......
......
......@@ -12,12 +12,13 @@ export default () => {
store,
render(createElement) {
const domEl = document.querySelector(this.$options.el);
const { issueDetailsPath, issueStackTracePath } = domEl.dataset;
const { issueDetailsPath, issueStackTracePath, issueProjectPath } = domEl.dataset;
return createElement('error-details', {
props: {
issueDetailsPath,
issueStackTracePath,
issueProjectPath,
},
});
},
......
......
......@@ -18,6 +18,7 @@ module Projects::ErrorTrackingHelper
opts = [project, issue_id, { format: :json }]
{
'issue-project-path' => new_project_issue_path(project),
'issue-details-path' => details_project_error_tracking_index_path(*opts),
'issue-stack-trace-path' => stack_trace_project_error_tracking_index_path(*opts)
}
......
......
......@@ -4,4 +4,4 @@
= password_field_tag :password, nil, class: 'form-control', required: true, title: _('This field is required.'), data: { qa_selector: 'password_field' }
.submit-container.move-submit-down
= submit_tag _('Enter Admin Mode'), class: 'btn btn-success', data: { qa_selector: 'sign_in_button' }
= submit_tag _('Enter Admin Mode'), class: 'btn btn-success', data: { qa_selector: 'enter_admin_mode_button' }
---
title: Add ability to create new issue from sentry error detail page
merge_request: 20337
author:
type: added
......@@ -6,9 +6,10 @@
variables:
SAST_ANALYZER_IMAGE_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers"
SAST_DEFAULT_ANALYZERS: "bandit, brakeman, gosec, spotbugs, flawfinder, phpcs-security-audit, security-code-scan, nodejs-scan, eslint, tslint, secrets, sobelow, pmd-apex"
SAST_DEFAULT_ANALYZERS: "bandit, brakeman, gosec, spotbugs, flawfinder, phpcs-security-audit, security-code-scan, nodejs-scan, eslint, tslint, secrets, sobelow, pmd-apex, kubesec"
SAST_ANALYZER_IMAGE_TAG: 2
SAST_DISABLE_DIND: "false"
SCAN_KUBERNETES_MANIFESTS: "false"
sast:
stage: test
......@@ -98,6 +99,16 @@ flawfinder-sast:
$SAST_DEFAULT_ANALYZERS =~ /flawfinder/ &&
$CI_PROJECT_REPOSITORY_LANGUAGES =~ /\b(c\+\+|c)\b/
kubesec-sast:
extends: .analyzer
image:
name: "$SAST_ANALYZER_IMAGE_PREFIX/kubesec:$SAST_ANALYZER_IMAGE_TAG"
only:
variables:
- $GITLAB_FEATURES =~ /\bsast\b/ &&
$SAST_DEFAULT_ANALYZERS =~ /kubesec/ &&
$SCAN_KUBERNETES_MANIFESTS == 'true'
gosec-sast:
extends: .analyzer
image:
......
......
......@@ -4892,6 +4892,9 @@ msgstr ""
msgid "Create group label"
msgstr ""
msgid "Create issue"
msgstr ""
msgid "Create lists from labels. Issues with that label appear in that list."
msgstr ""
......
......
......@@ -331,6 +331,7 @@ module QA
module Admin
autoload :Menu, 'qa/page/admin/menu'
autoload :NewSession, 'qa/page/admin/new_session'
module Settings
autoload :Repository, 'qa/page/admin/settings/repository'
......
......
# frozen_string_literal: true
module QA
module Page
module Admin
class NewSession < Page::Base
view 'app/views/admin/sessions/_new_base.html.haml' do
element :enter_admin_mode_button
element :password_field
end
def set_password(password)
fill_element :password_field, password
end
def click_enter_admin_mode
click_element :enter_admin_mode_button
end
end
end
end
end
......@@ -86,11 +86,19 @@ module QA
end
def check_element(name)
find_element(name).set(true)
retry_until(sleep_interval: 1) do
find_element(name).set(true)
find_element(name).checked?
end
end
def uncheck_element(name)
find_element(name).set(false)
retry_until(sleep_interval: 1) do
find_element(name).set(false)
!find_element(name).checked?
end
end
# replace with (..., page = self.class)
......@@ -125,8 +133,8 @@ module QA
has_no_css?(element_selector_css(name, kwargs), wait: wait, text: text)
end
def has_text?(text)
page.has_text? text
def has_text?(text, wait: Capybara.default_max_wait_time)
page.has_text?(text, wait: wait)
end
def has_no_text?(text)
......
......
......@@ -60,8 +60,15 @@ module QA
end
end
def click_admin_area
within_top_menu { click_element :admin_area_link }
def go_to_admin_area
click_admin_area
if has_text?('Enter Admin Mode', wait: 1.0)
Admin::NewSession.perform do |new_session|
new_session.set_password(Runtime::User.admin_password)
new_session.click_enter_admin_mode
end
end
end
def signed_in?
......@@ -125,6 +132,10 @@ module QA
end
end
end
def click_admin_area
within_top_menu { click_element :admin_area_link }
end
end
end
end
......
......
......@@ -8,7 +8,7 @@ module QA
raise ArgumentError unless traits.include?(:enabled)
Page::Main::Login.perform(&:sign_in_using_credentials)
Page::Main::Menu.perform(&:click_admin_area)
Page::Main::Menu.perform(&:go_to_admin_area)
Page::Admin::Menu.perform(&:go_to_repository_settings)
Page::Admin::Settings::Repository.perform do |setting|
......
......
......@@ -32,7 +32,7 @@ module QA
Runtime::Browser.visit(@address, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_using_admin_credentials)
Page::Main::Menu.perform(&:click_admin_area)
Page::Main::Menu.perform(&:go_to_admin_area)
Page::Admin::Menu.perform(&:go_to_network_settings)
Page::Admin::Settings::Network.perform do |setting|
......
......
......@@ -6,7 +6,7 @@ module QA
before do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_using_admin_credentials)
Page::Main::Menu.perform(&:click_admin_area)
Page::Main::Menu.perform(&:go_to_admin_area)
Page::Admin::Menu.perform(&:go_to_metrics_and_profiling_settings)
Page::Admin::Settings::MetricsAndProfiling.perform do |setting|
......
......
......@@ -59,6 +59,18 @@ module QA
elements
end
def check_element(name)
log("checking :#{name}")
super
end
def uncheck_element(name)
log("unchecking :#{name}")
super
end
def click_element(name, page = nil, **kwargs)
msg = ["clicking :#{name}"]
msg << ", expecting to be at #{page.class}" if page
......@@ -99,10 +111,10 @@ module QA
found
end
def has_text?(text)
def has_text?(text, **kwargs)
found = super
log(%Q{has_text?('#{text}') returned #{found}})
log(%Q{has_text?('#{text}', wait: #{kwargs[:wait] || Capybara.default_max_wait_time}) returned #{found}})
found
end
......
......
......@@ -117,7 +117,7 @@ describe QA::Support::Page::Logging do
allow(page).to receive(:has_text?).and_return(true)
expect { subject.has_text? 'foo' }
.to output(/has_text\?\('foo'\) returned true/).to_stdout_from_any_process
.to output(/has_text\?\('foo', wait: #{QA::Runtime::Browser::CAPYBARA_MAX_WAIT_TIME}\) returned true/).to_stdout_from_any_process
end
it 'logs has_no_text?' do
......
......
import { createLocalVue, shallowMount } from '@vue/test-utils';
import Vuex from 'vuex';
import { GlLoadingIcon, GlLink } from '@gitlab/ui';
import { GlButton, GlLoadingIcon, GlLink } from '@gitlab/ui';
import Stacktrace from '~/error_tracking/components/stacktrace.vue';
import ErrorDetails from '~/error_tracking/components/error_details.vue';
......@@ -20,6 +20,7 @@ describe('ErrorDetails', () => {
propsData: {
issueDetailsPath: '/123/details',
issueStackTracePath: '/stacktrace',
issueProjectPath: '/test-project/issues/new',
},
});
}
......@@ -82,6 +83,15 @@ describe('ErrorDetails', () => {
expect(wrapper.find(Stacktrace).exists()).toBe(false);
});
it('should allow a blank issue to be created', () => {
store.state.details.loading = false;
store.state.details.error.id = 1;
mountComponent();
const button = wrapper.find(GlButton);
expect(button.exists()).toBe(true);
expect(button.attributes().href).toBe(wrapper.props().issueProjectPath);
});
describe('Stacktrace', () => {
it('should show stacktrace', () => {
store.state.details.loading = false;
......
......
......@@ -81,6 +81,7 @@ describe Projects::ErrorTrackingHelper do
let(:route_params) { [project.owner, project, issue_id, { format: :json }] }
let(:details_path) { details_namespace_project_error_tracking_index_path(*route_params) }
let(:stack_trace_path) { stack_trace_namespace_project_error_tracking_index_path(*route_params) }
let(:issue_project_path) { new_project_issue_path(project) }
let(:result) { helper.error_details_data(project, issue_id) }
......@@ -91,5 +92,9 @@ describe Projects::ErrorTrackingHelper do
it 'returns the correct stack trace path' do
expect(result['issue-stack-trace-path']).to eq stack_trace_path
end
it 'returns the correct path for creating a new issue' do
expect(result['issue-project-path']).to eq issue_project_path
end
end
end
......@@ -506,7 +506,6 @@ describe ProjectsHelper do
it 'returns the command to push to create project over SSH' do
allow(Gitlab::CurrentSettings.current_application_settings).to receive(:enabled_git_access_protocol) { 'ssh' }
allow(Gitlab.config.gitlab_shell).to receive(:ssh_path_prefix).and_return('git@localhost:')
expect(helper.push_to_create_project_command(user)).to eq("git push --set-upstream #{Gitlab.config.gitlab.user}@localhost:john/$(git rev-parse --show-toplevel | xargs basename).git $(git rev-parse --abbrev-ref HEAD)")
end
......
......