Commit fa5af599 authored by Eric Eastwood's avatar Eric Eastwood

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ee into...

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ee into ee-31276-fix-diffs-with-edit-forking-needs

Conflicts:
	app/assets/javascripts/blob/blob_fork_suggestion.js
	app/assets/javascripts/dispatcher.js
	spec/javascripts/blob/blob_fork_suggestion_spec.js
parents 33ef3471 84779047
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.7-phantomjs-2.1-node-7.1" image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.7-phantomjs-2.1-node-7.1-postgresql-9.6"
cache: cache:
key: "ruby-233" key: "ruby-233"
...@@ -11,8 +11,6 @@ variables: ...@@ -11,8 +11,6 @@ variables:
RAILS_ENV: "test" RAILS_ENV: "test"
NODE_ENV: "test" NODE_ENV: "test"
SIMPLECOV: "true" SIMPLECOV: "true"
SETUP_DB: "true"
USE_BUNDLE_INSTALL: "true"
GIT_DEPTH: "20" GIT_DEPTH: "20"
PHANTOMJS_VERSION: "2.1.1" PHANTOMJS_VERSION: "2.1.1"
GET_SOURCES_ATTEMPTS: "3" GET_SOURCES_ATTEMPTS: "3"
...@@ -22,13 +20,9 @@ variables: ...@@ -22,13 +20,9 @@ variables:
ES_JAVA_OPTS: "-Xms600m -Xmx600m" ES_JAVA_OPTS: "-Xms600m -Xmx600m"
before_script: before_script:
- source ./scripts/prepare_build.sh
- cp config/gitlab.yml.example config/gitlab.yml
- bundle --version - bundle --version
- '[ "$USE_BUNDLE_INSTALL" != "true" ] || retry bundle install --without postgres production --jobs $(nproc) --clean $FLAGS' - . scripts/utils.sh
- retry gem install knapsack fog-aws mime-types - ./scripts/prepare_build.sh
- '[ "$SETUP_DB" != "true" ] || bundle exec rake db:drop db:create db:schema:load db:migrate add_limits_mysql'
- '[ "$SETUP_DB" != "true" ] || bundle exec rake geo:db:drop geo:db:create geo:db:schema:load geo:db:migrate'
stages: stages:
- prepare - prepare
...@@ -56,21 +50,36 @@ stages: ...@@ -56,21 +50,36 @@ stages:
paths: paths:
- knapsack/ - knapsack/
.use-db: &use-db .use-pg: &use-pg
services:
- postgres:latest
- redis:alpine
- elasticsearch:5.1
.use-mysql: &use-mysql
services: services:
- mysql:latest - mysql:latest
- redis:alpine - redis:alpine
- elasticsearch:5.1 - elasticsearch:5.1
.only-master-and-ee-or-mysql: &only-master-and-ee-or-mysql
only:
- /\-(?i)mysql$/
- master@gitlab-org/gitlab-ce
- master@gitlab/gitlabhq
- tags@gitlab-org/gitlab-ce
- tags@gitlab/gitlabhq
- //@gitlab-org/gitlab-ee
- //@gitlab/gitlab-ee
.rspec-knapsack: &rspec-knapsack .rspec-knapsack: &rspec-knapsack
stage: test stage: test
<<: *dedicated-runner <<: *dedicated-runner
<<: *use-db
script: script:
- JOB_NAME=( $CI_JOB_NAME ) - JOB_NAME=( $CI_JOB_NAME )
- export CI_NODE_INDEX=${JOB_NAME[1]} - export CI_NODE_INDEX=${JOB_NAME[-2]}
- export CI_NODE_TOTAL=${JOB_NAME[2]} - export CI_NODE_TOTAL=${JOB_NAME[-1]}
- export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json - export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_${JOB_NAME[1]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
- export KNAPSACK_GENERATE_REPORT=true - export KNAPSACK_GENERATE_REPORT=true
- export CACHE_CLASSES=true - export CACHE_CLASSES=true
- cp ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} ${KNAPSACK_REPORT_PATH} - cp ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} ${KNAPSACK_REPORT_PATH}
...@@ -83,15 +92,23 @@ stages: ...@@ -83,15 +92,23 @@ stages:
- knapsack/ - knapsack/
- tmp/capybara/ - tmp/capybara/
.rspec-knapsack-pg: &rspec-knapsack-pg
<<: *rspec-knapsack
<<: *use-pg
.rspec-knapsack-mysql: &rspec-knapsack-mysql
<<: *rspec-knapsack
<<: *use-mysql
<<: *only-master-and-ee-or-mysql
.spinach-knapsack: &spinach-knapsack .spinach-knapsack: &spinach-knapsack
stage: test stage: test
<<: *dedicated-runner <<: *dedicated-runner
<<: *use-db
script: script:
- JOB_NAME=( $CI_JOB_NAME ) - JOB_NAME=( $CI_JOB_NAME )
- export CI_NODE_INDEX=${JOB_NAME[1]} - export CI_NODE_INDEX=${JOB_NAME[-2]}
- export CI_NODE_TOTAL=${JOB_NAME[2]} - export CI_NODE_TOTAL=${JOB_NAME[-1]}
- export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json - export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_${JOB_NAME[1]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
- export KNAPSACK_GENERATE_REPORT=true - export KNAPSACK_GENERATE_REPORT=true
- export CACHE_CLASSES=true - export CACHE_CLASSES=true
- cp ${KNAPSACK_SPINACH_SUITE_REPORT_PATH} ${KNAPSACK_REPORT_PATH} - cp ${KNAPSACK_SPINACH_SUITE_REPORT_PATH} ${KNAPSACK_REPORT_PATH}
...@@ -104,6 +121,15 @@ stages: ...@@ -104,6 +121,15 @@ stages:
- knapsack/ - knapsack/
- tmp/capybara/ - tmp/capybara/
.spinach-knapsack-pg: &spinach-knapsack-pg
<<: *spinach-knapsack
<<: *use-pg
.spinach-knapsack-mysql: &spinach-knapsack-mysql
<<: *spinach-knapsack
<<: *use-mysql
<<: *only-master-and-ee-or-mysql
# Prepare and merge knapsack tests # Prepare and merge knapsack tests
knapsack: knapsack:
<<: *knapsack-state <<: *knapsack-state
...@@ -121,8 +147,8 @@ update-knapsack: ...@@ -121,8 +147,8 @@ update-knapsack:
<<: *dedicated-runner <<: *dedicated-runner
stage: post-test stage: post-test
script: script:
- scripts/merge-reports ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec_node_*.json - scripts/merge-reports ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec_pg_node_*.json
- scripts/merge-reports ${KNAPSACK_SPINACH_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/spinach_node_*.json - scripts/merge-reports ${KNAPSACK_SPINACH_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/spinach_pg_node_*.json
- '[[ -z ${KNAPSACK_S3_BUCKET} ]] || scripts/sync-reports put $KNAPSACK_S3_BUCKET $KNAPSACK_RSPEC_SUITE_REPORT_PATH $KNAPSACK_SPINACH_SUITE_REPORT_PATH' - '[[ -z ${KNAPSACK_S3_BUCKET} ]] || scripts/sync-reports put $KNAPSACK_S3_BUCKET $KNAPSACK_RSPEC_SUITE_REPORT_PATH $KNAPSACK_SPINACH_SUITE_REPORT_PATH'
- rm -f knapsack/${CI_PROJECT_NAME}/*_node_*.json - rm -f knapsack/${CI_PROJECT_NAME}/*_node_*.json
only: only:
...@@ -132,7 +158,7 @@ update-knapsack: ...@@ -132,7 +158,7 @@ update-knapsack:
- master@gitlab/gitlab-ee - master@gitlab/gitlab-ee
setup-test-env: setup-test-env:
<<: *use-db <<: *use-pg
<<: *dedicated-runner <<: *dedicated-runner
stage: prepare stage: prepare
script: script:
...@@ -147,37 +173,69 @@ setup-test-env: ...@@ -147,37 +173,69 @@ setup-test-env:
- public/assets - public/assets
- tmp/tests - tmp/tests
rspec 0 20: *rspec-knapsack rspec pg 0 20: *rspec-knapsack-pg
rspec 1 20: *rspec-knapsack rspec pg 1 20: *rspec-knapsack-pg
rspec 2 20: *rspec-knapsack rspec pg 2 20: *rspec-knapsack-pg
rspec 3 20: *rspec-knapsack rspec pg 3 20: *rspec-knapsack-pg
rspec 4 20: *rspec-knapsack rspec pg 4 20: *rspec-knapsack-pg
rspec 5 20: *rspec-knapsack rspec pg 5 20: *rspec-knapsack-pg
rspec 6 20: *rspec-knapsack rspec pg 6 20: *rspec-knapsack-pg
rspec 7 20: *rspec-knapsack rspec pg 7 20: *rspec-knapsack-pg
rspec 8 20: *rspec-knapsack rspec pg 8 20: *rspec-knapsack-pg
rspec 9 20: *rspec-knapsack rspec pg 9 20: *rspec-knapsack-pg
rspec 10 20: *rspec-knapsack rspec pg 10 20: *rspec-knapsack-pg
rspec 11 20: *rspec-knapsack rspec pg 11 20: *rspec-knapsack-pg
rspec 12 20: *rspec-knapsack rspec pg 12 20: *rspec-knapsack-pg
rspec 13 20: *rspec-knapsack rspec pg 13 20: *rspec-knapsack-pg
rspec 14 20: *rspec-knapsack rspec pg 14 20: *rspec-knapsack-pg
rspec 15 20: *rspec-knapsack rspec pg 15 20: *rspec-knapsack-pg
rspec 16 20: *rspec-knapsack rspec pg 16 20: *rspec-knapsack-pg
rspec 17 20: *rspec-knapsack rspec pg 17 20: *rspec-knapsack-pg
rspec 18 20: *rspec-knapsack rspec pg 18 20: *rspec-knapsack-pg
rspec 19 20: *rspec-knapsack rspec pg 19 20: *rspec-knapsack-pg
spinach 0 10: *spinach-knapsack rspec mysql 0 20: *rspec-knapsack-mysql
spinach 1 10: *spinach-knapsack rspec mysql 1 20: *rspec-knapsack-mysql
spinach 2 10: *spinach-knapsack rspec mysql 2 20: *rspec-knapsack-mysql
spinach 3 10: *spinach-knapsack rspec mysql 3 20: *rspec-knapsack-mysql
spinach 4 10: *spinach-knapsack rspec mysql 4 20: *rspec-knapsack-mysql
spinach 5 10: *spinach-knapsack rspec mysql 5 20: *rspec-knapsack-mysql
spinach 6 10: *spinach-knapsack rspec mysql 6 20: *rspec-knapsack-mysql
spinach 7 10: *spinach-knapsack rspec mysql 7 20: *rspec-knapsack-mysql
spinach 8 10: *spinach-knapsack rspec mysql 8 20: *rspec-knapsack-mysql
spinach 9 10: *spinach-knapsack rspec mysql 9 20: *rspec-knapsack-mysql
rspec mysql 10 20: *rspec-knapsack-mysql
rspec mysql 11 20: *rspec-knapsack-mysql
rspec mysql 12 20: *rspec-knapsack-mysql
rspec mysql 13 20: *rspec-knapsack-mysql
rspec mysql 14 20: *rspec-knapsack-mysql
rspec mysql 15 20: *rspec-knapsack-mysql
rspec mysql 16 20: *rspec-knapsack-mysql
rspec mysql 17 20: *rspec-knapsack-mysql
rspec mysql 18 20: *rspec-knapsack-mysql
rspec mysql 19 20: *rspec-knapsack-mysql
spinach pg 0 10: *spinach-knapsack-pg
spinach pg 1 10: *spinach-knapsack-pg
spinach pg 2 10: *spinach-knapsack-pg
spinach pg 3 10: *spinach-knapsack-pg
spinach pg 4 10: *spinach-knapsack-pg
spinach pg 5 10: *spinach-knapsack-pg
spinach pg 6 10: *spinach-knapsack-pg
spinach pg 7 10: *spinach-knapsack-pg
spinach pg 8 10: *spinach-knapsack-pg
spinach pg 9 10: *spinach-knapsack-pg
spinach mysql 0 10: *spinach-knapsack-mysql
spinach mysql 1 10: *spinach-knapsack-mysql
spinach mysql 2 10: *spinach-knapsack-mysql
spinach mysql 3 10: *spinach-knapsack-mysql
spinach mysql 4 10: *spinach-knapsack-mysql
spinach mysql 5 10: *spinach-knapsack-mysql
spinach mysql 6 10: *spinach-knapsack-mysql
spinach mysql 7 10: *spinach-knapsack-mysql
spinach mysql 8 10: *spinach-knapsack-mysql
spinach mysql 9 10: *spinach-knapsack-mysql
# Other generic tests # Other generic tests
.ruby-static-analysis: &ruby-static-analysis .ruby-static-analysis: &ruby-static-analysis
...@@ -208,24 +266,37 @@ rake flay: *exec ...@@ -208,24 +266,37 @@ rake flay: *exec
license_finder: *exec license_finder: *exec
rake downtime_check: *exec rake downtime_check: *exec
rake db:migrate:reset: .db-migrate-reset: &db-migrate-reset
stage: test stage: test
<<: *use-db
<<: *dedicated-runner <<: *dedicated-runner
script: script:
- bundle exec rake db:migrate:reset - bundle exec rake db:migrate:reset
rake db:rollback: rake pg db:migrate:reset:
<<: *db-migrate-reset
<<: *use-pg
rake mysql db:migrate:reset:
<<: *db-migrate-reset
<<: *use-mysql
.db-rollback: &db-rollback
stage: test stage: test
<<: *use-db
<<: *dedicated-runner <<: *dedicated-runner
script: script:
- bundle exec rake db:rollback STEP=120 - bundle exec rake db:rollback STEP=120
- bundle exec rake db:migrate - bundle exec rake db:migrate
rake db:seed_fu: rake pg db:rollback:
<<: *db-rollback
<<: *use-pg
rake mysql db:rollback:
<<: *db-rollback
<<: *use-mysql
.db-seed_fu: &db-seed_fu
stage: test stage: test
<<: *use-db
<<: *dedicated-runner <<: *dedicated-runner
variables: variables:
SIZE: "1" SIZE: "1"
...@@ -241,6 +312,14 @@ rake db:seed_fu: ...@@ -241,6 +312,14 @@ rake db:seed_fu:
paths: paths:
- log/development.log - log/development.log
rake pg db:seed_fu:
<<: *db-seed_fu
<<: *use-pg
rake mysql db:seed_fu:
<<: *db-seed_fu
<<: *use-mysql
rake gitlab:assets:compile: rake gitlab:assets:compile:
stage: test stage: test
<<: *dedicated-runner <<: *dedicated-runner
...@@ -265,7 +344,7 @@ rake karma: ...@@ -265,7 +344,7 @@ rake karma:
paths: paths:
- vendor/ruby - vendor/ruby
stage: test stage: test
<<: *use-db <<: *use-pg
<<: *dedicated-runner <<: *dedicated-runner
variables: variables:
BABEL_ENV: "coverage" BABEL_ENV: "coverage"
...@@ -322,9 +401,8 @@ bundler:audit: ...@@ -322,9 +401,8 @@ bundler:audit:
script: script:
- "bundle exec bundle-audit check --update --ignore CVE-2016-4658" - "bundle exec bundle-audit check --update --ignore CVE-2016-4658"
migration paths: .migration-paths: &migration-paths
stage: test stage: test
<<: *use-db
<<: *dedicated-runner <<: *dedicated-runner
variables: variables:
SETUP_DB: "false" SETUP_DB: "false"
...@@ -336,13 +414,21 @@ migration paths: ...@@ -336,13 +414,21 @@ migration paths:
script: script:
- git fetch origin v8.14.10 - git fetch origin v8.14.10
- git checkout -f FETCH_HEAD - git checkout -f FETCH_HEAD
- bundle install --without postgres production --jobs $(nproc) $FLAGS --retry=3 - bundle install $BUNDLE_INSTALL_FLAGS
- bundle exec rake db:drop db:create db:schema:load db:seed_fu - bundle exec rake db:drop db:create db:schema:load db:seed_fu
- git checkout $CI_COMMIT_SHA - git checkout $CI_COMMIT_SHA
- bundle install --without postgres production --jobs $(nproc) $FLAGS --retry=3 - bundle install $BUNDLE_INSTALL_FLAGS
- source scripts/prepare_build.sh - . scripts/prepare_build.sh
- bundle exec rake db:migrate - bundle exec rake db:migrate
migration pg paths:
<<: *migration-paths
<<: *use-pg
migration mysql paths:
<<: *migration-paths
<<: *use-mysql
coverage: coverage:
stage: post-test stage: post-test
services: [] services: []
...@@ -389,7 +475,7 @@ trigger_docs: ...@@ -389,7 +475,7 @@ trigger_docs:
before_script: before_script:
- apk update && apk add curl - apk update && apk add curl
variables: variables:
GIT_STRATEGY: none GIT_STRATEGY: "none"
cache: {} cache: {}
artifacts: {} artifacts: {}
script: script:
......
...@@ -16,47 +16,44 @@ const defaults = { ...@@ -16,47 +16,44 @@ const defaults = {
class BlobForkSuggestion { class BlobForkSuggestion {
constructor(options) { constructor(options) {
this.elementMap = Object.assign({}, defaults, options); this.elementMap = Object.assign({}, defaults, options);
this.onClickWrapper = this.onClick.bind(this); this.onOpenButtonClick = this.onOpenButtonClick.bind(this);
this.onCancelButtonClick = this.onCancelButtonClick.bind(this);
document.addEventListener('click', this.onClickWrapper);
} }
showSuggestionSection(forkPath, action = 'edit') { init() {
[].forEach.call(this.elementMap.suggestionSections, (suggestionSection) => { this.bindEvents();
suggestionSection.classList.remove('hidden');
});
[].forEach.call(this.elementMap.forkButtons, (forkButton) => { return this;
forkButton.setAttribute('href', forkPath); }
});
[].forEach.call(this.elementMap.actionTextPieces, (actionTextPiece) => { bindEvents() {
// eslint-disable-next-line no-param-reassign $(this.elementMap.openButtons).on('click', this.onOpenButtonClick);
actionTextPiece.textContent = action; $(this.elementMap.cancelButtons).on('click', this.onCancelButtonClick);
});
} }
hideSuggestionSection() { showSuggestionSection(forkPath, action = 'edit') {
[].forEach.call(this.elementMap.suggestionSections, (suggestionSection) => { $(this.elementMap.suggestionSections).removeClass('hidden');
suggestionSection.classList.add('hidden'); $(this.elementMap.forkButtons).attr('href', forkPath);
}); $(this.elementMap.actionTextPieces).text(action);
} }
onClick(e) { hideSuggestionSection() {
const el = e.target; $(this.elementMap.suggestionSections).addClass('hidden');
}
if ([].includes.call(this.elementMap.openButtons, el)) { onOpenButtonClick(e) {
const { forkPath, action } = el.dataset; const forkPath = $(e.currentTarget).attr('data-fork-path');
this.showSuggestionSection(forkPath, action); const action = $(e.currentTarget).attr('data-action');
} this.showSuggestionSection(forkPath, action);
}
if ([].includes.call(this.elementMap.cancelButtons, el)) { onCancelButtonClick() {
this.hideSuggestionSection(); this.hideSuggestionSection();
}
} }
destroy() { destroy() {
document.removeEventListener('click', this.onClickWrapper); $(this.elementMap.openButtons).off('click', this.onOpenButtonClick);
$(this.elementMap.cancelButtons).off('click', this.onCancelButtonClick);
} }
} }
......
...@@ -103,7 +103,8 @@ const ShortcutsBlob = require('./shortcuts_blob'); ...@@ -103,7 +103,8 @@ const ShortcutsBlob = require('./shortcuts_blob');
cancelButtons: document.querySelectorAll('.js-cancel-fork-suggestion-button'), cancelButtons: document.querySelectorAll('.js-cancel-fork-suggestion-button'),
suggestionSections: document.querySelectorAll('.js-file-fork-suggestion-section'), suggestionSections: document.querySelectorAll('.js-file-fork-suggestion-section'),
actionTextPieces: document.querySelectorAll('.js-file-fork-suggestion-section-action'), actionTextPieces: document.querySelectorAll('.js-file-fork-suggestion-section-action'),
}); })
.init();
} }
switch (page) { switch (page) {
......
<script>
/* eslint-disable no-new, no-undef */ /* eslint-disable no-new, no-undef */
/* global Flash */ /* global Flash */
/** /**
...@@ -22,7 +23,7 @@ import statusCodes from '~/lib/utils/http_status'; ...@@ -22,7 +23,7 @@ import statusCodes from '~/lib/utils/http_status';
import '~/flash'; import '~/flash';
import '~/lib/utils/common_utils'; import '~/lib/utils/common_utils';
import deployBoardSvg from 'empty_states/icons/_deploy_board.svg'; import deployBoardSvg from 'empty_states/icons/_deploy_board.svg';
import instanceComponent from './deploy_board_instance_component'; import instanceComponent from './deploy_board_instance_component.vue';
export default { export default {
...@@ -160,72 +161,81 @@ export default { ...@@ -160,72 +161,81 @@ export default {
return '<projectname>'; 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 v-if="canRenderDeployBoard">
<div class="js-deploy-board deploy-board">
<section class="deploy-board-information">
<div v-if="isLoading"> <span>
<i class="fa fa-spinner fa-spin" aria-hidden="true"></i> <span class="percentage">{{deployBoardData.completion}}%</span>
</div> <span class="text">Complete</span>
</span>
<div v-if="canRenderDeployBoard"> </section>
<section class="deploy-board-information"> <section class="deploy-board-instances">
<span> <p class="text">{{instanceTitle}}</p>
<span class="percentage">{{deployBoardData.completion}}%</span>
<span class="text">Complete</span> <div class="deploy-board-instances-container">
</span> <template v-for="instance in deployBoardData.instances">
</section> <instance-component
:status="instance.status"
<section class="deploy-board-instances"> :tooltip-text="instance.tooltip"
<p class="text">{{instanceTitle}}</p> :stable="instance.stable" />
</template>
<div class="deploy-board-instances-container"> </div>
<template v-for="instance in deployBoardData.instances"> </section>
<instance-component
:status="instance.status" <section
:tooltip-text="instance.tooltip" class="deploy-board-actions"
:stable="instance.stable" /> v-if="deployBoardData.rollback_url || deployBoardData.abort_url">
</template> <a
</div> class="btn"
</section> data-method="post"
rel="nofollow"
<section class="deploy-board-actions" v-if="deployBoardData.rollback_url || deployBoardData.abort_url"> v-if="deployBoardData.rollback_url"
<a class="btn" :href="deployBoardData.rollback_url">
data-method="post" Rollback
rel="nofollow" </a>
v-if="deployBoardData.rollback_url"
:href="deployBoardData.rollback_url"> <a
Rollback class="btn btn-red btn-inverted"
</a> data-method="post"
rel="nofollow"
<a class="btn btn-red btn-inverted" v-if="deployBoardData.abort_url"
data-method="post" :href="deployBoardData.abort_url">
rel="nofollow" Abort
v-if="deployBoardData.abort_url" </a>
:href="deployBoardData.abort_url"> </section>
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> </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: * 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 * https://gitlab.com/gitlab-org/gitlab-ce/uploads/2f655655c0eadf655d0ae7467b53002a/environments__deploy-graphic.png
...@@ -50,14 +51,14 @@ export default { ...@@ -50,14 +51,14 @@ export default {
return cssClassName; 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 @@ ...@@ -3,7 +3,7 @@
* Render environments table. * Render environments table.
*/ */
import EnvironmentTableRowComponent from './environment_item.vue'; import EnvironmentTableRowComponent from './environment_item.vue';
import DeployBoard from './deploy_board_component'; import DeployBoard from './deploy_board_component.vue';
export default { export default {
components: { components: {
......
...@@ -38,9 +38,8 @@ class Projects::DeployKeysController < Projects::ApplicationController ...@@ -38,9 +38,8 @@ class Projects::DeployKeysController < Projects::ApplicationController
deploy_key_project = @project.deploy_keys_projects.find_by(deploy_key_id: params[:id]) deploy_key_project = @project.deploy_keys_projects.find_by(deploy_key_id: params[:id])
return render_404 unless deploy_key_project return render_404 unless deploy_key_project
deploy_key_project.destroy!
load_key load_key
deploy_key_project.destroy!
log_audit_event(@key.title, action: :destroy) log_audit_event(@key.title, action: :destroy)
redirect_to_repository_settings(@project) redirect_to_repository_settings(@project)
......
...@@ -31,3 +31,9 @@ ...@@ -31,3 +31,9 @@
= link_to namespace_project_pages_path(@project.namespace, @project), title: 'Pages' do = link_to namespace_project_pages_path(@project.namespace, @project), title: 'Pages' do
%span %span
Pages Pages
= nav_link(controller: :audit_events) do
= link_to namespace_project_audit_events_path(@project.namespace, @project), title: "Audit Events" do
%span
Audit Events
...@@ -25,6 +25,7 @@ development: ...@@ -25,6 +25,7 @@ development:
pool: 5 pool: 5
username: root username: root
password: "secure password" password: "secure password"
# host: localhost
# socket: /tmp/mysql.sock # socket: /tmp/mysql.sock
# Warning: The database defined as "test" will be erased and # Warning: The database defined as "test" will be erased and
...@@ -39,4 +40,5 @@ test: &test ...@@ -39,4 +40,5 @@ test: &test
pool: 5 pool: 5
username: root username: root
password: password:
# host: localhost
# socket: /tmp/mysql.sock # socket: /tmp/mysql.sock
...@@ -25,6 +25,7 @@ development: ...@@ -25,6 +25,7 @@ development:
pool: 5 pool: 5
username: postgres username: postgres
password: password:
# host: localhost
# #
# Staging specific # Staging specific
...@@ -36,6 +37,7 @@ staging: ...@@ -36,6 +37,7 @@ staging:
pool: 5 pool: 5
username: postgres username: postgres
password: password:
# host: localhost
# Warning: The database defined as "test" will be erased and # Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake". # re-generated from your development database when you run "rake".
...@@ -47,3 +49,4 @@ test: &test ...@@ -47,3 +49,4 @@ test: &test
pool: 5 pool: 5
username: postgres username: postgres
password: password:
# host: localhost
...@@ -28,6 +28,7 @@ development: ...@@ -28,6 +28,7 @@ development:
pool: 5 pool: 5
username: root username: root
password: "secure password" password: "secure password"
# host: localhost
# socket: /tmp/mysql.sock # socket: /tmp/mysql.sock
# Warning: The database defined as "test" will be erased and # Warning: The database defined as "test" will be erased and
...@@ -42,5 +43,6 @@ test: &test ...@@ -42,5 +43,6 @@ test: &test
pool: 5 pool: 5
username: root username: root
password: password:
# host: localhost
# socket: /tmp/mysql.sock # socket: /tmp/mysql.sock
...@@ -21,6 +21,7 @@ development: ...@@ -21,6 +21,7 @@ development:
pool: 5 pool: 5
username: postgres username: postgres
password: password:
# host: localhost
# #
# Staging specific # Staging specific
...@@ -32,6 +33,7 @@ staging: ...@@ -32,6 +33,7 @@ staging:
pool: 5 pool: 5
username: postgres username: postgres
password: password:
# host: localhost
# Warning: The database defined as "test" will be erased and # Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake". # re-generated from your development database when you run "rake".
...@@ -43,3 +45,4 @@ test: &test ...@@ -43,3 +45,4 @@ test: &test
pool: 5 pool: 5
username: postgres username: postgres
password: password:
# host: localhost
...@@ -38,7 +38,7 @@ if Rails.env.test? ...@@ -38,7 +38,7 @@ if Rails.env.test?
end end
end end
if ENV.has_key?('CI') if ENV.has_key?('CI') && ENV['GITLAB_DATABASE'] == 'postgresql'
RspecProfiling::VCS::Git.prepend(RspecProfilingExt::Git) RspecProfiling::VCS::Git.prepend(RspecProfilingExt::Git)
RspecProfiling::Run.prepend(RspecProfilingExt::Run) RspecProfiling::Run.prepend(RspecProfilingExt::Run)
end end
......
...@@ -15,6 +15,9 @@ By default, a protected branch does four simple things: ...@@ -15,6 +15,9 @@ By default, a protected branch does four simple things:
- it prevents **anyone** from force pushing to the branch - it prevents **anyone** from force pushing to the branch
- it prevents **anyone** from deleting the branch - it prevents **anyone** from deleting the branch
**Note**: A GitLab admin is allowed to push to the protected branches.
See the [Changelog](#changelog) section for changes over time. See the [Changelog](#changelog) section for changes over time.
> >
......
#!/bin/sh #!/bin/sh
retry() { . scripts/utils.sh
if eval "$@"; then
return 0 export SETUP_DB=${SETUP_DB:-true}
fi export USE_BUNDLE_INSTALL=${USE_BUNDLE_INSTALL:-true}
export BUNDLE_INSTALL_FLAGS="--without production --jobs $(nproc) --path vendor --retry 3 --quiet"
# Determine the database by looking at the job name.
# For example, we'll get pg if the job is `rspec pg 19 20`
export GITLAB_DATABASE=$(echo $CI_JOB_NAME | cut -f2 -d' ')
# This would make the default database postgresql, and we could also use
# pg to mean postgresql.
if [ "$GITLAB_DATABASE" != 'mysql' ]; then
export GITLAB_DATABASE='postgresql'
fi
cp config/database.yml.$GITLAB_DATABASE config/database.yml
# EE-only
cp config/database_geo.yml.$GITLAB_DATABASE config/database_geo.yml
if [ "$GITLAB_DATABASE" = 'postgresql' ]; then
sed -i 's/# host:.*/host: postgres/g' config/database.yml
# EE-only
sed -i 's/# host:.*/host: postgres/g' config/database_geo.yml
for i in 2 1; do else # Assume it's mysql
sleep 3s sed -i 's/username:.*/username: root/g' config/database.yml
echo "Retrying $i..." sed -i 's/password:.*/password:/g' config/database.yml
if eval "$@"; then sed -i 's/# host:.*/host: mysql/g' config/database.yml
return 0
fi # EE-only
done sed -i 's/username:.*/username: root/g' config/database_geo.yml
return 1 sed -i 's/password:.*/password:/g' config/database_geo.yml
} sed -i 's/# host:.*/host: mysql/g' config/database_geo.yml
fi
cp config/database.yml.mysql config/database.yml
sed -i 's/username:.*/username: root/g' config/database.yml
sed -i 's/password:.*/password:/g' config/database.yml
sed -i 's/# socket:.*/host: mysql/g' config/database.yml
cp config/database_geo.yml.mysql config/database_geo.yml
sed -i 's/username:.*/username: root/g' config/database_geo.yml
sed -i 's/password:.*/password:/g' config/database_geo.yml
sed -i 's/# socket:.*/host: mysql/g' config/database_geo.yml
cp config/resque.yml.example config/resque.yml cp config/resque.yml.example config/resque.yml
sed -i 's/localhost/redis/g' config/resque.yml sed -i 's/localhost/redis/g' config/resque.yml
export FLAGS="--path vendor --retry 3 --quiet" cp config/gitlab.yml.example config/gitlab.yml
if [ "$USE_BUNDLE_INSTALL" != "false" ]; then
retry bundle install --clean $BUNDLE_INSTALL_FLAGS
fi
# Only install knapsack after bundle install! Otherwise oddly some native
# gems could not be found under some circumstance. No idea why, hours wasted.
retry gem install knapsack fog-aws mime-types
if [ "$SETUP_DB" != "false" ]; then
bundle exec rake db:drop db:create db:schema:load db:migrate
if [ "$GITLAB_DATABASE" = "mysql" ]; then
bundle exec rake add_limits_mysql
fi
# EE-only
bundle exec rake geo:db:drop geo:db:create geo:db:schema:load geo:db:migrate
fi
retry() {
if eval "$@"; then
return 0
fi
for i in 2 1; do
sleep 3s
echo "Retrying $i..."
if eval "$@"; then
return 0
fi
done
return 1
}
...@@ -63,4 +63,44 @@ describe Projects::BuildsController do ...@@ -63,4 +63,44 @@ describe Projects::BuildsController do
expect(json_response['favicon']).to eq "/assets/ci_favicons/#{status.favicon}.ico" expect(json_response['favicon']).to eq "/assets/ci_favicons/#{status.favicon}.ico"
end end
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 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 ...@@ -18,10 +18,10 @@ feature 'Groups > Audit Events', js: true, feature: true do
click_link 'Members' click_link 'Members'
group_member = group.members.find_by(user_id: pete) group_member = group.members.find_by(user_id: pete)
page.within "#group_member_#{group_member.id}" do page.within "#group_member_#{group_member.id}" do
click_button 'Edit access level' click_button 'Developer'
select 'Master', from: 'group_member_access_level' click_link 'Master'
click_button 'Save'
end end
# This is to avoid a Capybara::Poltergeist::MouseEventFailed error # This is to avoid a Capybara::Poltergeist::MouseEventFailed error
......
...@@ -36,11 +36,9 @@ feature 'Groups > Pipeline Quota', feature: true do ...@@ -36,11 +36,9 @@ feature 'Groups > Pipeline Quota', feature: true do
let!(:project) { create(:empty_project, namespace: group, shared_runners_enabled: false) } let!(:project) { create(:empty_project, namespace: group, shared_runners_enabled: false) }
it 'is not linked within the group settings dropdown' do 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_link('Pipelines quota')
expect(page).not_to have_selector(:link_or_button, 'Pipeline Quota')
end
end end
it 'shows correct group quota info' do it 'shows correct group quota info' do
...@@ -60,12 +58,10 @@ feature 'Groups > Pipeline Quota', feature: true do ...@@ -60,12 +58,10 @@ feature 'Groups > Pipeline Quota', feature: true do
context 'minutes under quota' do context 'minutes under quota' do
let(:group) { create(:group, :with_not_used_build_minutes_limit) } let(:group) { create(:group, :with_not_used_build_minutes_limit) }
it 'is linked within the group settings dropdown' do it 'is linked within the group settings tab' do
visit group_path(group) visit edit_group_path(group)
page.within('.layout-nav') do expect(page).to have_link('Pipelines quota')
expect(page).to have_selector(:link_or_button, 'Pipeline Quota')
end
end end
it 'shows correct group quota info' do it 'shows correct group quota info' do
...@@ -83,12 +79,10 @@ feature 'Groups > Pipeline Quota', feature: true do ...@@ -83,12 +79,10 @@ feature 'Groups > Pipeline Quota', feature: true do
let(:group) { create(:group, :with_used_build_minutes_limit) } let(:group) { create(:group, :with_used_build_minutes_limit) }
let!(:other_project) { create(:empty_project, namespace: group, shared_runners_enabled: false) } let!(:other_project) { create(:empty_project, namespace: group, shared_runners_enabled: false) }
it 'is linked within the group settings dropdown' do it 'is linked within the group settings tab' do
visit group_path(group) visit edit_group_path(group)
page.within('.layout-nav') do expect(page).to have_link('Pipelines quota')
expect(page).to have_selector(:link_or_button, 'Pipeline Quota')
end
end end
it 'shows correct group quota and projects info' do 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 ...@@ -4,7 +4,7 @@ feature 'Resolve an open discussion in a merge request by creating an issue', fe
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project, only_allow_merge_if_all_discussions_are_resolved: true) } let(:project) { create(:project, only_allow_merge_if_all_discussions_are_resolved: true) }
let(:merge_request) { create(:merge_request, source_project: project) } 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 describe 'As a user with access to the project' do
before do before do
...@@ -74,8 +74,8 @@ feature 'Resolve an open discussion in a merge request by creating an issue', fe ...@@ -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 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}"\ expect(page).to have_content("The discussion at #{merge_request.to_reference}"\
"(discussion #{discussion.first_note.id}) will stay unresolved."\ " (discussion #{discussion.first_note.id}) will stay unresolved."\
"Ask someone with permission to resolve it.") " Ask someone with permission to resolve it.")
end end
end end
end end
...@@ -17,14 +17,17 @@ feature 'Projects > Audit Events', js: true, feature: true do ...@@ -17,14 +17,17 @@ feature 'Projects > Audit Events', js: true, feature: true do
fill_in 'deploy_key_title', with: 'laptop' fill_in 'deploy_key_title', with: 'laptop'
fill_in 'deploy_key_key', with: 'ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAzrEJUIR6Y03TCE9rIJ+GqTBvgb8t1jI9h5UBzCLuK4VawOmkLornPqLDrGbm6tcwM/wBrrLvVOqi2HwmkKEIecVO0a64A4rIYScVsXIniHRS6w5twyn1MD3sIbN+socBDcaldECQa2u1dI3tnNVcs8wi77fiRe7RSxePsJceGoheRQgC8AZ510UdIlO+9rjIHUdVN7LLyz512auAfYsgx1OfablkQ/XJcdEwDNgi9imI6nAXhmoKUm1IPLT2yKajTIC64AjLOnE0YyCh6+7RFMpiMyu1qiOCpdjYwTgBRiciNRZCH8xIedyCoAmiUgkUT40XYHwLuwiPJICpkAzp7Q== user@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) visit namespace_project_audit_events_path(project.namespace, project)
expect(page).to have_content('Add deploy key') expect(page).to have_content('Add deploy key')
visit namespace_project_deploy_keys_path(project.namespace, project) 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) visit namespace_project_audit_events_path(project.namespace, project)
...@@ -38,15 +41,13 @@ feature 'Projects > Audit Events', js: true, feature: true do ...@@ -38,15 +41,13 @@ feature 'Projects > Audit Events', js: true, feature: true do
end end
it "appears in the project's audit events" do it "appears in the project's audit events" do
visit namespace_project_path(project.namespace, project) visit namespace_project_settings_members_path(project.namespace, project)
click_link 'Members'
project_member = project.project_member(pete) project_member = project.project_member(pete)
page.within "#project_member_#{project_member.id}" do page.within "#project_member_#{project_member.id}" do
click_button 'Edit access level' click_button 'Developer'
select 'Master', from: 'project_member_access_level' click_link 'Master'
click_button 'Save'
end end
# This is to avoid a Capybara::Poltergeist::MouseEventFailed error # This is to avoid a Capybara::Poltergeist::MouseEventFailed error
......
...@@ -3,20 +3,21 @@ import BlobForkSuggestion from '~/blob/blob_fork_suggestion'; ...@@ -3,20 +3,21 @@ import BlobForkSuggestion from '~/blob/blob_fork_suggestion';
describe('BlobForkSuggestion', () => { describe('BlobForkSuggestion', () => {
let blobForkSuggestion; let blobForkSuggestion;
const openButtons = [document.createElement('div')]; const openButton = document.createElement('div');
const forkButtons = [document.createElement('a')]; const forkButton = document.createElement('a');
const cancelButtons = [document.createElement('div')]; const cancelButton = document.createElement('div');
const suggestionSections = [document.createElement('div')]; const suggestionSection = document.createElement('div');
const actionTextPieces = [document.createElement('div')]; const actionTextPiece = document.createElement('div');
beforeEach(() => { beforeEach(() => {
blobForkSuggestion = new BlobForkSuggestion({ blobForkSuggestion = new BlobForkSuggestion({
openButtons, openButtons: openButton,
forkButtons, forkButtons: forkButton,
cancelButtons, cancelButtons: cancelButton,
suggestionSections, suggestionSections: suggestionSection,
actionTextPieces, actionTextPieces: actionTextPiece,
}); })
.init();
}); });
afterEach(() => { afterEach(() => {
...@@ -25,13 +26,15 @@ describe('BlobForkSuggestion', () => { ...@@ -25,13 +26,15 @@ describe('BlobForkSuggestion', () => {
it('showSuggestionSection', () => { it('showSuggestionSection', () => {
blobForkSuggestion.showSuggestionSection('/foo', 'foo'); blobForkSuggestion.showSuggestionSection('/foo', 'foo');
expect(suggestionSections[0].classList.contains('hidden')).toEqual(false);
expect(forkButtons[0].getAttribute('href')).toEqual('/foo'); expect(suggestionSection.classList.contains('hidden')).toEqual(false);
expect(actionTextPieces[0].textContent).toEqual('foo'); expect(forkButton.getAttribute('href')).toEqual('/foo');
expect(actionTextPiece.textContent).toEqual('foo');
}); });
it('hideSuggestionSection', () => { it('hideSuggestionSection', () => {
blobForkSuggestion.hideSuggestionSection(); blobForkSuggestion.hideSuggestionSection();
expect(suggestionSections[0].classList.contains('hidden')).toEqual(true);
expect(suggestionSection.classList.contains('hidden')).toEqual(true);
}); });
}); });
import Vue from 'vue'; 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'; import Service from '~/environments/services/environments_service';
const { deployBoardMockData, invalidDeployBoardMockData } = require('./mock_data'); const { deployBoardMockData, invalidDeployBoardMockData } = require('./mock_data');
......
import Vue from 'vue'; 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', () => { describe('Deploy Board Instance', () => {
let DeployBoardInstanceComponent; let DeployBoardInstanceComponent;
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::OtherMarkup, lib: true do describe Gitlab::OtherMarkup, lib: true do
let(:context) { {} }
context "XSS Checks" do context "XSS Checks" do
links = { links = {
'links' => { 'links' => {
file: 'file.rdoc', file: 'file.rdoc',
input: 'XSS[JaVaScriPt:alert(1)]', input: 'XSS[JaVaScriPt:alert(1)]',
output: '<p><a>XSS</a></p>' output: "\n" + '<p><a>XSS</a></p>' + "\n"
} }
} }
links.each do |name, data| 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