Commit 102cf55e authored by Filipa Lacerda's avatar Filipa Lacerda

Merge branch 'master' into 2222-deploy-boards-polling

* master:
  Fix MR diff blob_fork_suggestion after jQuery update
  Refactor deploy boards into vue files
  Bump mysql2 gem from 0.3.16 -> 0.4.5
  Fix diffs with edit-forking needs
  Add Fork/Cancel confirmation to "Replace"/"Delete" buttons
  Fix tests and features to make specs pass
  Fix incorrect EE spec filenames which were ingored by CI
  Fix outdated specs that were not executed due to wrong filename
  Fix incorrect spec filenames which were ingored by CI
  Use jQuery niceness on blob_fork_suggestion
parents 4c72cdb0 c4527291
......@@ -12,7 +12,7 @@ gem 'sprockets', '~> 3.7.0'
gem 'default_value_for', '~> 3.0.0'
# Supported DBs
gem 'mysql2', '~> 0.3.16', group: :mysql
gem 'mysql2', '~> 0.4.5', group: :mysql
gem 'pg', '~> 0.18.2', group: :postgres
gem 'rugged', '~> 0.25.1.1'
......
......@@ -482,7 +482,7 @@ GEM
tool (~> 0.2)
mustermann-grape (0.4.0)
mustermann (= 0.4.0)
mysql2 (0.3.20)
mysql2 (0.4.5)
net-ldap (0.12.1)
net-ssh (3.0.1)
netrc (0.11.0)
......@@ -984,7 +984,7 @@ DEPENDENCIES
method_source (~> 0.8)
minitest (~> 5.7.0)
mousetrap-rails (~> 1.4.6)
mysql2 (~> 0.3.16)
mysql2 (~> 0.4.5)
net-ldap
net-ssh (~> 3.0.1)
nokogiri (~> 1.6.7, >= 1.6.7.2)
......
function BlobForkSuggestion(openButton, cancelButton, suggestionSection) {
if (openButton) {
openButton.addEventListener('click', () => {
suggestionSection.classList.remove('hidden');
});
const defaults = {
// Buttons that will show the `suggestionSections`
// has `data-fork-path`, and `data-action`
openButtons: [],
// Update the href(from `openButton` -> `data-fork-path`)
// whenever a `openButton` is clicked
forkButtons: [],
// Buttons to hide the `suggestionSections`
cancelButtons: [],
// Section to show/hide
suggestionSections: [],
// Pieces of text that need updating depending on the action, `edit`, `replace`, `delete`
actionTextPieces: [],
};
class BlobForkSuggestion {
constructor(options) {
this.elementMap = Object.assign({}, defaults, options);
this.onOpenButtonClick = this.onOpenButtonClick.bind(this);
this.onCancelButtonClick = this.onCancelButtonClick.bind(this);
}
init() {
this.bindEvents();
return this;
}
bindEvents() {
$(this.elementMap.openButtons).on('click', this.onOpenButtonClick);
$(this.elementMap.cancelButtons).on('click', this.onCancelButtonClick);
}
showSuggestionSection(forkPath, action = 'edit') {
$(this.elementMap.suggestionSections).removeClass('hidden');
$(this.elementMap.forkButtons).attr('href', forkPath);
$(this.elementMap.actionTextPieces).text(action);
}
hideSuggestionSection() {
$(this.elementMap.suggestionSections).addClass('hidden');
}
onOpenButtonClick(e) {
const forkPath = $(e.currentTarget).attr('data-fork-path');
const action = $(e.currentTarget).attr('data-action');
this.showSuggestionSection(forkPath, action);
}
onCancelButtonClick() {
this.hideSuggestionSection();
}
if (cancelButton) {
cancelButton.addEventListener('click', () => {
suggestionSection.classList.add('hidden');
});
destroy() {
$(this.elementMap.openButtons).off('click', this.onOpenButtonClick);
$(this.elementMap.cancelButtons).off('click', this.onCancelButtonClick);
}
}
......
// ECMAScript polyfills
import 'core-js/fn/array/find';
import 'core-js/fn/array/from';
import 'core-js/fn/array/includes';
import 'core-js/fn/object/assign';
import 'core-js/fn/promise';
import 'core-js/fn/string/code-point-at';
......
......@@ -97,11 +97,14 @@ const ShortcutsBlob = require('./shortcuts_blob');
fileBlobPermalinkUrl,
});
new BlobForkSuggestion(
document.querySelector('.js-edit-blob-link-fork-toggler'),
document.querySelector('.js-cancel-fork-suggestion'),
document.querySelector('.js-file-fork-suggestion-section'),
);
new BlobForkSuggestion({
openButtons: document.querySelectorAll('.js-edit-blob-link-fork-toggler'),
forkButtons: document.querySelectorAll('.js-fork-suggestion-button'),
cancelButtons: document.querySelectorAll('.js-cancel-fork-suggestion-button'),
suggestionSections: document.querySelectorAll('.js-file-fork-suggestion-section'),
actionTextPieces: document.querySelectorAll('.js-file-fork-suggestion-section-action'),
})
.init();
}
switch (page) {
......
<script>
/* eslint-disable no-new, no-undef */
/* global Flash */
/**
* Renders a deploy board.
......@@ -19,7 +21,7 @@
*/
import Visibility from 'visibilityjs';
import deployBoardSvg from 'empty_states/icons/_deploy_board.svg';
import instanceComponent from './deploy_board_instance_component';
import instanceComponent from './deploy_board_instance_component.vue';
import Poll from '../../lib/utils/poll';
import '../../flash';
......@@ -131,72 +133,81 @@ export default {
return '<projectname>';
},
},
};
</script>
<template>
<div class="js-deploy-board deploy-board">
<div v-if="isLoading">
<i
class="fa fa-spinner fa-spin"
aria-hidden="true" />
</div>
template: `
<div class="js-deploy-board deploy-board">
<div v-if="isLoading">
<i class="fa fa-spinner fa-spin" aria-hidden="true"></i>
</div>
<div v-if="canRenderDeployBoard">
<section class="deploy-board-information">
<span>
<span class="percentage">{{deployBoardData.completion}}%</span>
<span class="text">Complete</span>
</span>
</section>
<section class="deploy-board-instances">
<p class="text">{{instanceTitle}}</p>
<div class="deploy-board-instances-container">
<template v-for="instance in deployBoardData.instances">
<instance-component
:status="instance.status"
:tooltip-text="instance.tooltip"
:stable="instance.stable" />
</template>
</div>
</section>
<section class="deploy-board-actions" v-if="deployBoardData.rollback_url || deployBoardData.abort_url">
<a class="btn"
data-method="post"
rel="nofollow"
v-if="deployBoardData.rollback_url"
:href="deployBoardData.rollback_url">
Rollback
</a>
<a class="btn btn-red btn-inverted"
data-method="post"
rel="nofollow"
v-if="deployBoardData.abort_url"
:href="deployBoardData.abort_url">
Abort
</a>
</section>
</div>
<div v-if="canRenderEmptyState">
<section class="deploy-board-empty-state-svg">
${deployBoardSvg}
</section>
<section class="deploy-board-empty-state-text">
<span class="title">Kubernetes deployment not found</span>
<span>
To see deployment progress for your environments, make sure your deployments are in Kubernetes namespace
<code>{{projectName}}</code> and labeled with <code>app=$CI_ENVIRONMENT_SLUG</code>.
</span>
</section>
</div>
<div v-if="canRenderErrorState" class="deploy-board-error-message">
We can't fetch the data right now. Please try again later.
</div>
<div v-if="canRenderDeployBoard">
<section class="deploy-board-information">
<span>
<span class="percentage">{{deployBoardData.completion}}%</span>
<span class="text">Complete</span>
</span>
</section>
<section class="deploy-board-instances">
<p class="text">{{instanceTitle}}</p>
<div class="deploy-board-instances-container">
<template v-for="instance in deployBoardData.instances">
<instance-component
:status="instance.status"
:tooltip-text="instance.tooltip"
:stable="instance.stable" />
</template>
</div>
</section>
<section
class="deploy-board-actions"
v-if="deployBoardData.rollback_url || deployBoardData.abort_url">
<a
class="btn"
data-method="post"
rel="nofollow"
v-if="deployBoardData.rollback_url"
:href="deployBoardData.rollback_url">
Rollback
</a>
<a
class="btn btn-red btn-inverted"
data-method="post"
rel="nofollow"
v-if="deployBoardData.abort_url"
:href="deployBoardData.abort_url">
Abort
</a>
</section>
</div>
`,
};
<div v-if="canRenderEmptyState">
<section
class="deploy-board-empty-state-svg"
v-html="deployBoardSvg">
</section>
<section class="deploy-board-empty-state-text">
<span class="title">Kubernetes deployment not found</span>
<span>
To see deployment progress for your environments, make sure your deployments are in Kubernetes namespace
<code>{{projectName}}</code> and labeled with <code>app=$CI_ENVIRONMENT_SLUG</code>.
</span>
</section>
</div>
<div
v-if="canRenderErrorState"
class="deploy-board-error-message">
We can't fetch the data right now. Please try again later.
</div>
</div>
</script>
<script>
/**
* An instance in deploy board is represented by a square in this mockup:
* https://gitlab.com/gitlab-org/gitlab-ce/uploads/2f655655c0eadf655d0ae7467b53002a/environments__deploy-graphic.png
......@@ -50,14 +51,14 @@ export default {
return cssClassName;
},
},
template: `
<div
class="deploy-board-instance has-tooltip"
:class="cssClass"
:data-title="tooltipText"
data-toggle="tooltip"
data-placement="top">
</div>
`,
};
</script>
<template>
<div
class="deploy-board-instance has-tooltip"
:class="cssClass"
:data-title="tooltipText"
data-toggle="tooltip"
data-placement="top">
</div>
</template>
......@@ -3,7 +3,7 @@
* Render environments table.
*/
import EnvironmentTableRowComponent from './environment_item.vue';
import DeployBoard from './deploy_board_component';
import DeployBoard from './deploy_board_component.vue';
export default {
components: {
......
......@@ -5,6 +5,7 @@
import Cookies from 'js-cookie';
import './breakpoints';
import './flash';
import BlobForkSuggestion from './blob/blob_fork_suggestion';
/* eslint-disable max-len */
// MergeRequestTabs
......@@ -266,6 +267,17 @@ import './flash';
new gl.Diff();
this.scrollToElement('#diffs');
$('.diff-file').each((i, el) => {
new BlobForkSuggestion({
openButtons: $(el).find('.js-edit-blob-link-fork-toggler'),
forkButtons: $(el).find('.js-fork-suggestion-button'),
cancelButtons: $(el).find('.js-cancel-fork-suggestion-button'),
suggestionSections: $(el).find('.js-file-fork-suggestion-section'),
actionTextPieces: $(el).find('.js-file-fork-suggestion-section-action'),
})
.init();
});
},
});
}
......
......@@ -38,9 +38,8 @@ class Projects::DeployKeysController < Projects::ApplicationController
deploy_key_project = @project.deploy_keys_projects.find_by(deploy_key_id: params[:id])
return render_404 unless deploy_key_project
deploy_key_project.destroy!
load_key
deploy_key_project.destroy!
log_audit_event(@key.title, action: :destroy)
redirect_to_repository_settings(@project)
......
......@@ -14,15 +14,6 @@ module BlobHelper
options[:link_opts])
end
def fork_path(project = @project, ref = @ref, path = @path, options = {})
continue_params = {
to: edit_path,
notice: edit_in_new_fork_notice,
notice_now: edit_in_new_fork_notice_now
}
namespace_project_forks_path(project.namespace, project, namespace_key: current_user.namespace.id, continue: continue_params)
end
def edit_blob_link(project = @project, ref = @ref, path = @path, options = {})
blob = options.delete(:blob)
blob ||= project.repository.blob_at(ref, path) rescue nil
......@@ -37,7 +28,16 @@ module BlobHelper
elsif !current_user || (current_user && can_modify_blob?(blob, project, ref))
link_to 'Edit', edit_path(project, ref, path, options), class: "#{common_classes} btn-sm"
elsif current_user && can?(current_user, :fork_project, project)
button_tag 'Edit', class: "#{common_classes} js-edit-blob-link-fork-toggler"
continue_params = {
to: edit_path(project, ref, path, options),
notice: edit_in_new_fork_notice,
notice_now: edit_in_new_fork_notice_now
}
fork_path = namespace_project_forks_path(project.namespace, project, namespace_key: current_user.namespace.id, continue: continue_params)
button_tag 'Edit',
class: "#{common_classes} js-edit-blob-link-fork-toggler",
data: { action: 'edit', fork_path: fork_path }
end
end
......@@ -48,21 +48,25 @@ module BlobHelper
return unless blob
common_classes = "btn btn-#{btn_class}"
if !on_top_of_branch?(project, ref)
button_tag label, class: "btn btn-#{btn_class} disabled has-tooltip", title: "You can only #{action} files when you are on a branch", data: { container: 'body' }
button_tag label, class: "#{common_classes} disabled has-tooltip", title: "You can only #{action} files when you are on a branch", data: { container: 'body' }
elsif blob.lfs_pointer?
button_tag label, class: "btn btn-#{btn_class} disabled has-tooltip", title: "It is not possible to #{action} files that are stored in LFS using the web interface", data: { container: 'body' }
button_tag label, class: "#{common_classes} disabled has-tooltip", title: "It is not possible to #{action} files that are stored in LFS using the web interface", data: { container: 'body' }
elsif can_modify_blob?(blob, project, ref)
button_tag label, class: "btn btn-#{btn_class}", 'data-target' => "#modal-#{modal_type}-blob", 'data-toggle' => 'modal'
button_tag label, class: "#{common_classes}", 'data-target' => "#modal-#{modal_type}-blob", 'data-toggle' => 'modal'
elsif can?(current_user, :fork_project, project)
continue_params = {
to: request.fullpath,
to: request.fullpath,
notice: edit_in_new_fork_notice + " Try to #{action} this file again.",
notice_now: edit_in_new_fork_notice_now
}
fork_path = namespace_project_forks_path(project.namespace, project, namespace_key: current_user.namespace.id, continue: continue_params)
link_to label, fork_path, class: "btn btn-#{btn_class}", method: :post
button_tag label,
class: "#{common_classes} js-edit-blob-link-fork-toggler",
data: { action: action, fork_path: fork_path }
end
end
......
- if current_user
.js-file-fork-suggestion-section.file-fork-suggestion.hidden
%span.file-fork-suggestion-note
You're not allowed to
%span.js-file-fork-suggestion-section-action
edit
files in this project directly. Please fork this project,
make your changes there, and submit a merge request.
= link_to 'Fork', nil, method: :post, class: 'js-fork-suggestion-button btn btn-grouped btn-inverted btn-new'
%button.js-cancel-fork-suggestion-button.btn.btn-grouped{ type: 'button' }
Cancel
......@@ -40,13 +40,8 @@
- if current_user
= replace_blob_link
= delete_blob_link
- if current_user
.js-file-fork-suggestion-section.file-fork-suggestion.hidden
%span.file-fork-suggestion-note
You don't have permission to edit this file. Try forking this project to edit the file.
= link_to 'Fork', fork_path, method: :post, class: 'btn btn-grouped btn-inverted btn-new'
%button.js-cancel-fork-suggestion.btn.btn-grouped{ type: 'button' }
Cancel
= render 'projects/fork_suggestion'
- if license_allows_file_locks?
:javascript
......
......@@ -18,4 +18,6 @@
= view_file_button(diff_commit.id, diff_file.new_path, project)
= view_on_environment_button(diff_commit.id, diff_file.new_path, environment) if environment
= render 'projects/fork_suggestion'
= render 'projects/diffs/content', diff_file: diff_file, diff_commit: diff_commit, blob: blob, project: project
......@@ -31,3 +31,9 @@
= link_to namespace_project_pages_path(@project.namespace, @project), title: 'Pages' do
%span
Pages
= nav_link(controller: :audit_events) do
= link_to namespace_project_audit_events_path(@project.namespace, @project), title: "Audit Events" do
%span
Audit Events
......@@ -117,6 +117,8 @@ Feature: Project Source Browse Files
And I click on ".gitignore" file in repo
And I see the ".gitignore"
And I click on "Replace"
Then I should see a Fork/Cancel combo
And I click button "Fork"
Then I should see a notice about a new fork having been created
When I click on "Replace"
And I replace it with a text file
......@@ -265,6 +267,8 @@ Feature: Project Source Browse Files
And I click on ".gitignore" file in repo
And I see the ".gitignore"
And I click on "Delete"
Then I should see a Fork/Cancel combo
And I click button "Fork"
Then I should see a notice about a new fork having been created
When I click on "Delete"
And I fill the commit message
......
......@@ -377,7 +377,6 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
step 'I should see a Fork/Cancel combo' do
expect(page).to have_link 'Fork'
expect(page).to have_button 'Cancel'
expect(page).to have_content 'You don\'t have permission to edit this file. Try forking this project to edit the file.'
end
step 'I should see a notice about a new fork having been created' do
......
......@@ -63,4 +63,44 @@ describe Projects::BuildsController do
expect(json_response['favicon']).to eq "/assets/ci_favicons/#{status.favicon}.ico"
end
end
describe 'GET trace.json' do
let(:pipeline) { create(:ci_pipeline, project: project) }
let(:build) { create(:ci_build, pipeline: pipeline) }
let(:user) { create(:user) }
context 'when user is logged in as developer' do
before do
project.add_developer(user)
sign_in(user)
get_trace
end
it 'traces build log' do
expect(response).to have_http_status(:ok)
expect(json_response['id']).to eq build.id
expect(json_response['status']).to eq build.status
end
end
context 'when user is logged in as non member' do
before do
sign_in(user)
get_trace
end
it 'traces build log' do
expect(response).to have_http_status(:ok)
expect(json_response['id']).to eq build.id
expect(json_response['status']).to eq build.status
end
end
def get_trace
get :trace, namespace_id: project.namespace,
project_id: project,
id: build.id,
format: :json
end
end
end
require 'spec_helper'
describe Projects::BuildsController do
include ApiHelpers
let(:project) { create(:empty_project, :public) }
describe 'GET trace.json' do
let(:pipeline) { create(:ci_pipeline, project: project) }
let(:build) { create(:ci_build, pipeline: pipeline) }
let(:user) { create(:user) }
context 'when user is logged in as developer' do
before do
project.add_developer(user)
sign_in(user)
get_trace
end
it 'traces build log' do
expect(response).to have_http_status(:ok)
expect(json_response['id']).to eq build.id
expect(json_response['status']).to eq build.status
end
end
context 'when user is logged in as non member' do
before do
sign_in(user)
get_trace
end
it 'traces build log' do
expect(response).to have_http_status(:ok)
expect(json_response['id']).to eq build.id
expect(json_response['status']).to eq build.status
end
end
def get_trace
get :trace, namespace_id: project.namespace,
project_id: project,
id: build.id,
format: :json
end
end
end
......@@ -18,10 +18,10 @@ feature 'Groups > Audit Events', js: true, feature: true do
click_link 'Members'
group_member = group.members.find_by(user_id: pete)
page.within "#group_member_#{group_member.id}" do
click_button 'Edit access level'
select 'Master', from: 'group_member_access_level'
click_button 'Save'
click_button 'Developer'
click_link 'Master'
end
# This is to avoid a Capybara::Poltergeist::MouseEventFailed error
......
......@@ -36,11 +36,9 @@ feature 'Groups > Pipeline Quota', feature: true do
let!(:project) { create(:empty_project, namespace: group, shared_runners_enabled: false) }
it 'is not linked within the group settings dropdown' do
visit group_path(group)
visit edit_group_path(group)
page.within('.layout-nav') do
expect(page).not_to have_selector(:link_or_button, 'Pipeline Quota')
end
expect(page).not_to have_link('Pipelines quota')
end
it 'shows correct group quota info' do
......@@ -60,12 +58,10 @@ feature 'Groups > Pipeline Quota', feature: true do
context 'minutes under quota' do
let(:group) { create(:group, :with_not_used_build_minutes_limit) }
it 'is linked within the group settings dropdown' do
visit group_path(group)
it 'is linked within the group settings tab' do
visit edit_group_path(group)
page.within('.layout-nav') do
expect(page).to have_selector(:link_or_button, 'Pipeline Quota')
end
expect(page).to have_link('Pipelines quota')
end
it 'shows correct group quota info' do
......@@ -83,12 +79,10 @@ feature 'Groups > Pipeline Quota', feature: true do
let(:group) { create(:group, :with_used_build_minutes_limit) }
let!(:other_project) { create(:empty_project, namespace: group, shared_runners_enabled: false) }
it 'is linked within the group settings dropdown' do
visit group_path(group)
it 'is linked within the group settings tab' do
visit edit_group_path(group)
page.within('.layout-nav') do
expect(page).to have_selector(:link_or_button, 'Pipeline Quota')
end
expect(page).to have_link('Pipelines quota')
end
it 'shows correct group quota and projects info' do
......
......@@ -4,7 +4,7 @@ feature 'Resolve an open discussion in a merge request by creating an issue', fe
let(:user) { create(:user) }
let(:project) { create(:project, only_allow_merge_if_all_discussions_are_resolved: true) }
let(:merge_request) { create(:merge_request, source_project: project) }
let!(:discussion) { Discussion.for_diff_notes([create(:diff_note_on_merge_request, noteable: merge_request, project: project)]).first }
let!(:discussion) { create(:diff_note_on_merge_request, noteable: merge_request, project: project).to_discussion }
describe 'As a user with access to the project' do
before do
......@@ -74,8 +74,8 @@ feature 'Resolve an open discussion in a merge request by creating an issue', fe
it 'Shows a notice to ask someone else to resolve the discussions' do
expect(page).to have_content("The discussion at #{merge_request.to_reference}"\
"(discussion #{discussion.first_note.id}) will stay unresolved."\
"Ask someone with permission to resolve it.")
" (discussion #{discussion.first_note.id}) will stay unresolved."\
" Ask someone with permission to resolve it.")
end
end
end
require 'spec_helper'
feature 'Diffs URL', js: true, feature: true do
before do
login_as :admin
@merge_request = create(:merge_request)
@project = @merge_request.source_project
end
include ApplicationHelper
let(:author_user) { create(:user) }
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
let(:forked_project) { Projects::ForkService.new(project, author_user).execute }
let(:merge_request) { create(:merge_request_with_diffs, source_project: forked_project, target_project: project, author: author_user) }
context 'when visit with */* as accept header' do
before(:each) do
......@@ -13,9 +15,9 @@ feature 'Diffs URL', js: true, feature: true do
end
it 'renders the notes' do
create :note_on_merge_request, project: @project, noteable: @merge_request, note: 'Rebasing with master'
create :note_on_merge_request, project: project, noteable: merge_request, note: 'Rebasing with master'
visit diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)
visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
# Load notes and diff through AJAX
expect(page).to have_css('.note-text', visible: false, text: 'Rebasing with master')
......@@ -28,11 +30,38 @@ feature 'Diffs URL', js: true, feature: true do
allow_any_instance_of(MergeRequestDiff).to receive(:overflow?).and_return(true)
allow(Commit).to receive(:max_diff_options).and_return(max_files: 20, max_lines: 20)
visit diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)
visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
page.within('.alert') do
expect(page).to have_text("Too many changes to show. Plain diff Email patch To preserve
performance only 3 of 3+ files are displayed.")
performance only 3 of 3 files are displayed.")
end
end
end
describe 'when editing file' do
let(:changelog_id) { hexdigest("CHANGELOG") }
context 'as author' do
it 'shows direct edit link' do
login_as(author_user)
visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
# Throws `Capybara::Poltergeist::InvalidSelector` if we try to use `#hash` syntax
expect(page).to have_selector("[id=\"#{changelog_id}\"] a.js-edit-blob")
end
end
context 'as user who needs to fork' do
it 'shows fork/cancel confirmation' do
login_as(user)
visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
# Throws `Capybara::Poltergeist::InvalidSelector` if we try to use `#hash` syntax
find("[id=\"#{changelog_id}\"] .js-edit-blob").click
expect(page).to have_selector('.js-fork-suggestion-button', count: 1)
expect(page).to have_selector('.js-cancel-fork-suggestion-button', count: 1)
end
end
end
......
......@@ -17,14 +17,17 @@ feature 'Projects > Audit Events', js: true, feature: true do
fill_in 'deploy_key_title', with: 'laptop'
fill_in 'deploy_key_key', with: 'ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAzrEJUIR6Y03TCE9rIJ+GqTBvgb8t1jI9h5UBzCLuK4VawOmkLornPqLDrGbm6tcwM/wBrrLvVOqi2HwmkKEIecVO0a64A4rIYScVsXIniHRS6w5twyn1MD3sIbN+socBDcaldECQa2u1dI3tnNVcs8wi77fiRe7RSxePsJceGoheRQgC8AZ510UdIlO+9rjIHUdVN7LLyz512auAfYsgx1OfablkQ/XJcdEwDNgi9imI6nAXhmoKUm1IPLT2yKajTIC64AjLOnE0YyCh6+7RFMpiMyu1qiOCpdjYwTgBRiciNRZCH8xIedyCoAmiUgkUT40XYHwLuwiPJICpkAzp7Q== user@laptop'
click_button 'Create'
click_button 'Add key'
visit namespace_project_audit_events_path(project.namespace, project)
expect(page).to have_content('Add deploy key')
visit namespace_project_deploy_keys_path(project.namespace, project)
click_link 'Remove'
accept_confirm do
click_link 'Remove'
end
visit namespace_project_audit_events_path(project.namespace, project)
......@@ -38,15 +41,13 @@ feature 'Projects > Audit Events', js: true, feature: true do
end
it "appears in the project's audit events" do
visit namespace_project_path(project.namespace, project)
click_link 'Members'
visit namespace_project_settings_members_path(project.namespace, project)
project_member = project.project_member(pete)
page.within "#project_member_#{project_member.id}" do
click_button 'Edit access level'
select 'Master', from: 'project_member_access_level'
click_button 'Save'
click_button 'Developer'
click_link 'Master'
end
# This is to avoid a Capybara::Poltergeist::MouseEventFailed error
......
import BlobForkSuggestion from '~/blob/blob_fork_suggestion';
describe('BlobForkSuggestion', () => {
let blobForkSuggestion;
const openButton = document.createElement('div');
const forkButton = document.createElement('a');
const cancelButton = document.createElement('div');
const suggestionSection = document.createElement('div');
const actionTextPiece = document.createElement('div');
beforeEach(() => {
blobForkSuggestion = new BlobForkSuggestion({
openButtons: openButton,
forkButtons: forkButton,
cancelButtons: cancelButton,
suggestionSections: suggestionSection,
actionTextPieces: actionTextPiece,
})
.init();
});
afterEach(() => {
blobForkSuggestion.destroy();
});
it('showSuggestionSection', () => {
blobForkSuggestion.showSuggestionSection('/foo', 'foo');
expect(suggestionSection.classList.contains('hidden')).toEqual(false);
expect(forkButton.getAttribute('href')).toEqual('/foo');
expect(actionTextPiece.textContent).toEqual('foo');
});
it('hideSuggestionSection', () => {
blobForkSuggestion.hideSuggestionSection();
expect(suggestionSection.classList.contains('hidden')).toEqual(true);
});
});
import Vue from 'vue';
import DeployBoard from '~/environments/components/deploy_board_component';
import DeployBoard from '~/environments/components/deploy_board_component.vue';
import Service from '~/environments/services/environments_service';
const { deployBoardMockData, invalidDeployBoardMockData } = require('./mock_data');
......
import Vue from 'vue';
import DeployBoardInstance from '~/environments/components/deploy_board_instance_component';
import DeployBoardInstance from '~/environments/components/deploy_board_instance_component.vue';
describe('Deploy Board Instance', () => {
let DeployBoardInstanceComponent;
......
require 'spec_helper'
describe Gitlab::OtherMarkup, lib: true do
let(:context) { {} }
context "XSS Checks" do
links = {
'links' => {
file: 'file.rdoc',
input: 'XSS[JaVaScriPt:alert(1)]',
output: '<p><a>XSS</a></p>'
output: "\n" + '<p><a>XSS</a></p>' + "\n"
}
}
links.each do |name, data|
......
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