提交 8f210aeb 编写于 作者: G GitLab Bot

Add latest changes from gitlab-org/gitlab@master

上级 996f7009
......@@ -239,6 +239,7 @@ static-analysis:
dependencies: ["setup-test-env", "compile-assets pull-cache"]
variables:
SETUP_DB: "false"
parallel: 2
script:
- scripts/static-analysis
cache:
......
......@@ -343,6 +343,8 @@ linters:
- 'app/views/projects/triggers/_index.html.haml'
- 'app/views/projects/triggers/_trigger.html.haml'
- 'app/views/projects/triggers/edit.html.haml'
- 'app/views/projects/wikis/_new.html.haml'
- 'app/views/projects/wikis/_pages_wiki_page.html.haml'
- 'app/views/projects/wikis/edit.html.haml'
- 'app/views/projects/wikis/history.html.haml'
- 'app/views/repository_check_mailer/notify.html.haml'
......
......@@ -16,10 +16,25 @@
# Uncomment the following lines to make the configuration take effect.
PreCommit:
AuthorName:
enabled: false
EsLint:
enabled: true
# https://github.com/sds/overcommit/issues/338
command: './node_modules/eslint/bin/eslint.js'
HamlLint:
enabled: true
MergeConflicts:
enabled: true
exclude:
- '**/conflict/file_spec.rb'
- '**/git/conflict/parser_spec.rb'
# prettier? https://github.com/sds/overcommit/issues/614 https://github.com/sds/overcommit/issues/390#issuecomment-495703284
RuboCop:
enabled: true
# on_warn: fail # Treat all warnings as failures
#
ScssLint:
enabled: true
#PostCheckout:
# ALL: # Special hook name that customizes all hooks of this type
# quiet: true # Change all post-checkout hooks to only display output on failure
......
// currently, this controller inherits all behaviors from wikis
import '../wikis/index';
import $ from 'jquery';
import Vue from 'vue';
import Translate from '~/vue_shared/translate';
import csrf from '~/lib/utils/csrf';
import ShortcutsWiki from '~/behaviors/shortcuts/shortcuts_wiki';
import ZenMode from '~/zen_mode';
import GLForm from '~/gl_form';
import deleteWikiModal from '../wikis/components/delete_wiki_modal.vue';
import Wikis from '../wikis/wikis';
document.addEventListener('DOMContentLoaded', () => {
new Wikis(); // eslint-disable-line no-new
new ShortcutsWiki(); // eslint-disable-line no-new
new ZenMode(); // eslint-disable-line no-new
new GLForm($('.wiki-form')); // eslint-disable-line no-new
const deleteWikiModalWrapperEl = document.getElementById('delete-wiki-modal-wrapper');
if (deleteWikiModalWrapperEl) {
Vue.use(Translate);
const { deleteWikiUrl, pageTitle } = deleteWikiModalWrapperEl.dataset;
// eslint-disable-next-line no-new
new Vue({
el: deleteWikiModalWrapperEl,
data: {
deleteWikiUrl: '',
},
render(createElement) {
return createElement(deleteWikiModal, {
props: {
pageTitle,
deleteWikiUrl,
csrfToken: csrf.token,
},
});
},
});
}
});
import $ from 'jquery';
import Vue from 'vue';
import Translate from '~/vue_shared/translate';
import csrf from '~/lib/utils/csrf';
import ShortcutsWiki from '~/behaviors/shortcuts/shortcuts_wiki';
import GLForm from '~/gl_form';
import Wikis from './wikis';
import ZenMode from '../../../zen_mode';
import GLForm from '../../../gl_form';
import deleteWikiModal from './components/delete_wiki_modal.vue';
document.addEventListener('DOMContentLoaded', () => {
new Wikis(); // eslint-disable-line no-new
new ShortcutsWiki(); // eslint-disable-line no-new
new ZenMode(); // eslint-disable-line no-new
new GLForm($('.wiki-form')); // eslint-disable-line no-new
const deleteWikiModalWrapperEl = document.getElementById('delete-wiki-modal-wrapper');
if (deleteWikiModalWrapperEl) {
Vue.use(Translate);
const { deleteWikiUrl, pageTitle } = deleteWikiModalWrapperEl.dataset;
// eslint-disable-next-line no-new
new Vue({
el: deleteWikiModalWrapperEl,
data: {
deleteWikiUrl: '',
},
render(createElement) {
return createElement(deleteWikiModal, {
props: {
pageTitle,
deleteWikiUrl,
csrfToken: csrf.token,
},
});
},
});
}
});
......@@ -12,8 +12,8 @@ export default class Wikis {
}
this.isNewWikiPage = Boolean(document.querySelector('.js-new-wiki-page'));
this.editTitleInput = document.querySelector('form.wiki-form #wiki_page_title');
this.commitMessageInput = document.querySelector('form.wiki-form #wiki_page_message');
this.editTitleInput = document.querySelector('form.wiki-form #wiki_title');
this.commitMessageInput = document.querySelector('form.wiki-form #wiki_message');
this.commitMessageI18n = this.isNewWikiPage
? s__('WikiPageCreate|Create %{pageTitle}')
: s__('WikiPageEdit|Update %{pageTitle}');
......
......@@ -99,18 +99,3 @@
justify-content: center;
color: $gray-700;
}
.svg-icon {
display: inline-flex;
align-self: center;
svg {
height: 1em;
width: 1em;
}
&.svg-baseline svg {
top: 0.125em;
position: relative;
}
}
.new-wiki-page {
.new-wiki-page-slug-tip {
display: inline-block;
max-width: 100%;
margin-top: 5px;
}
}
.wiki-history,
.wiki-page,
.edit-wiki-page {
margin-right: $gutter-width;
}
.edit-wiki-page {
@media only screen and (min-width: map-get($grid-breakpoints, lg) + (2 * $gutter-width)) and (max-width: map-get($grid-breakpoints, lg) + (3 * $gutter-width)) {
margin-right: $gutter-width * 1.5;
}
}
.wiki-form {
.edit-wiki-page-slug-tip {
display: inline-block;
max-width: 100%;
margin-top: 5px;
}
}
.title .edit-wiki-header {
width: 780px;
margin-left: auto;
......@@ -33,15 +5,6 @@
padding-right: 7px;
}
.container-fluid.wiki-page,
.container-fluid.edit-wiki-page {
width: initial;
}
.wiki-history.breadcrumbs {
min-height: (2 * $gl-padding) + 22;
}
.wiki-page-header {
position: relative;
......@@ -116,7 +79,7 @@
}
.sidebar-container {
padding-bottom: $gl-padding;
padding: $gl-padding 0;
width: calc(100% + 100px);
padding-right: 100px;
height: 100%;
......@@ -162,7 +125,7 @@
}
.wiki-sidebar-header {
padding: $gl-padding;
padding: 0 $gl-padding $gl-padding;
.gutter-toggle {
margin-top: 0;
......
......@@ -9,10 +9,11 @@ module PreviewMarkdown
markdown_params =
case controller_name
when 'wikis' then { pipeline: :wiki, project_wiki: @project_wiki, page_slug: params[:id] }
when 'snippets' then { skip_project_check: true }
when 'groups' then { group: group }
when 'projects' then projects_filter_params
else preview_markdown_params
else {}
end
render json: {
......@@ -24,7 +25,6 @@ module PreviewMarkdown
}
}
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
private
......@@ -35,12 +35,8 @@ module PreviewMarkdown
}
end
# Override this method to customise the markdown for your controller
def preview_markdown_params
{}
end
def markdown_service_params
params
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
end
# frozen_string_literal: true
# Controllers that include this concern must provide:
# * project
# * current_user
module ProjectWikiActions
extend ActiveSupport::Concern
included do
before_action :authorize_read_wiki!
before_action :init_wiki_actions
attr_accessor :project_wiki, :sidebar_page, :sidebar_wiki_entries
end
def init_wiki_actions
load_project_wiki
load_wiki_sidebar
rescue ProjectWiki::CouldNotCreateWikiError
flash[:notice] = _("Could not create Wiki Repository at this time. Please try again later.")
redirect_to project_path(project)
end
def load_project_wiki
self.project_wiki = load_wiki
end
def load_wiki_sidebar
self.sidebar_page = project_wiki.find_sidebar(params[:version_id])
return if sidebar_page.present?
# Fallback to default sidebar
self.sidebar_wiki_entries = WikiDirectory.group_by_directory(project_wiki.list_pages(limit: 15))
end
def load_wiki
# Call #wiki to make sure the Wiki Repo is initialized
ProjectWiki.new(project, current_user).tap(&:wiki)
end
end
# frozen_string_literal: true
class Projects::WikiDirectoriesController < Projects::ApplicationController
include ProjectWikiActions
def self.local_prefixes
[controller_path, 'shared/wiki']
end
def show
@wiki_dir = find_dir || WikiDirectory.new(params[:id])
return render('empty') if @wiki_dir.empty?
@wiki_entries = @wiki_pages = Kaminari
.paginate_array(@wiki_dir.pages)
.page(params[:page])
render 'show'
end
private
def find_dir
dir_params = params.permit(:id, :sort, :direction).dup.tap do |h|
Gitlab::Utils.allow_hash_values(h, sort_params_config[:allowed])
end
project_wiki.find_dir(dir_params[:id], dir_params[:sort], dir_params[:direction])
end
end
# frozen_string_literal: true
class Projects::WikiPagesController < Projects::ApplicationController
include ProjectWikiActions
include SendsBlob
include PreviewMarkdown
include Gitlab::Utils::StrongMemoize
def self.local_prefixes
[controller_path, 'shared/wiki']
end
before_action :authorize_create_wiki!, only: [:edit, :create, :update]
before_action :authorize_admin_wiki!, only: :destroy
before_action :load_page, only: [:show, :edit, :update, :history, :destroy]
before_action :valid_encoding?,
if: -> { %w[show edit update].include?(action_name) && load_page }
before_action only: [:edit, :update], unless: :valid_encoding? do
redirect_to(project_wiki_path(@project, @page))
end
def new
redirect_to project_wiki_path(@project, SecureRandom.uuid, random_title: true)
end
# `#show` handles a number of scenarios:
#
# - If `id` matches a WikiPage, then show the wiki page.
# - If `id` is a file in the wiki repository, then send the file.
# - If we know the user wants to create a new page with the given `id`,
# then display a create form.
# - Otherwise show the empty wiki page and invite the user to create a page.
def show
if @page
show_page
elsif file_blob
show_blob
elsif should_create_missing_page?
create_missing_page
else
render 'missing_page'
end
end
def edit
end
def update
@page = WikiPages::UpdateService
.new(@project, current_user, wiki_params)
.execute(@page)
return saved(:updated) if @page.valid?
render 'edit'
rescue WikiPage::PageChangedError, WikiPage::PageRenameError, Gitlab::Git::Wiki::OperationError => e
@error = e
render 'edit'
end
def create
@page = WikiPages::CreateService
.new(@project, current_user, wiki_params)
.execute
return saved(:created) if @page.persisted?
render action: "edit"
rescue Gitlab::Git::Wiki::OperationError => e
@page = project_wiki.build_page(wiki_params)
@error = e
render 'edit'
end
def history
if @page
@page_versions = Kaminari.paginate_array(@page.versions(page: params[:page].to_i),
total_count: @page.count_versions)
.page(params[:page])
else
redirect_to(
project_wiki_path(@project, :home),
notice: _("Page not found")
)
end
end
def destroy
WikiPages::DestroyService.new(@project, current_user).execute(@page)
redirect_to project_wiki_path(@project, :home),
status: 302,
notice: _("Page was successfully deleted")
rescue Gitlab::Git::Wiki::OperationError => e
@error = e
render 'edit'
end
private
# Callback for PreviewMarkdown
def preview_markdown_params
{ pipeline: :wiki, project_wiki: project_wiki, page_slug: params[:id] }
end
def show_page
set_encoding_error unless valid_encoding?
@page_dir = @project_wiki.find_dir(@page.directory) if @page.directory.present?
@show_children = true
render 'show'
end
def show_blob
send_blob(@project_wiki.repository, file_blob)
end
def should_create_missing_page?
view_param = @project_wiki.exists? ? 'create' : params[:view]
view_param == 'create' && can?(current_user, :create_wiki, @project)
end
def create_missing_page
# Assign a title to the WikiPage unless `id` is a randomly generated slug from #new
title = params[:id] unless params[:random_title].present?
@page = project_wiki.build_page(title: title)
render 'edit'
end
def wiki_params
params.require(:wiki_page).permit(:title, :content, :format, :message, :last_commit_sha)
end
def load_page
@page ||= @project_wiki.find_page(*page_params)
end
def page_params
keys = [:id]
keys << :version_id if params[:action] == 'show'
params.values_at(*keys)
end
def valid_encoding?
strong_memoize(:valid_encoding) do
@page.content.encoding == Encoding::UTF_8
end
end
def set_encoding_error
flash.now[:notice] = _("The content of this page is not encoded in UTF-8. Edits can only be made via the Git repository.")
end
def file_blob
strong_memoize(:file_blob) do
commit = @project_wiki.repository.commit(@project_wiki.default_branch)
next unless commit
@project_wiki.repository.blob_at(commit.id, params[:id])
end
end
def saved(action)
msg = case action
when :updated
_('Wiki was successfully updated')
when :created
_('Wiki was successfully created')
end
redirect_to(project_wiki_path(@project, @page), notice: msg)
end
end
# frozen_string_literal: true
class Projects::WikisController < Projects::ApplicationController
include ProjectWikiActions
include WikiHelper
include PreviewMarkdown
include SendsBlob
include Gitlab::Utils::StrongMemoize
def self.local_prefixes
[controller_path, 'shared/wiki']
before_action :authorize_read_wiki!
before_action :authorize_create_wiki!, only: [:edit, :create]
before_action :authorize_admin_wiki!, only: :destroy
before_action :load_project_wiki
before_action :load_page, only: [:show, :edit, :update, :history, :destroy]
before_action :valid_encoding?,
if: -> { %w[show edit update].include?(action_name) && load_page }
before_action only: [:edit, :update], unless: :valid_encoding? do
redirect_to(project_wiki_path(@project, @page))
end
def new
redirect_to project_wiki_path(@project, SecureRandom.uuid, random_title: true)
end
def pages
@nesting = show_children_param
@show_children = @nesting != ProjectWiki::NESTING_CLOSED
@wiki_pages = Kaminari.paginate_array(
project_wiki.list_pages(**sort_params)
@project_wiki.list_pages(sort: params[:sort], direction: params[:direction])
).page(params[:page])
@wiki_entries = case @nesting
when ProjectWiki::NESTING_FLAT
@wiki_pages
else
WikiDirectory.group_by_directory(@wiki_pages)
end
@wiki_entries = WikiPage.group_by_directory(@wiki_pages)
end
# `#show` handles a number of scenarios:
#
# - If `id` matches a WikiPage, then show the wiki page.
# - If `id` is a file in the wiki repository, then send the file.
# - If we know the user wants to create a new page with the given `id`,
# then display a create form.
# - Otherwise show the empty wiki page and invite the user to create a page.
def show
if @page
set_encoding_error unless valid_encoding?
render 'show'
elsif file_blob
send_blob(@project_wiki.repository, file_blob)
elsif show_create_form?
# Assign a title to the WikiPage unless `id` is a randomly generated slug from #new
title = params[:id] unless params[:random_title].present?
@page = build_page(title: title)
render 'edit'
else
render 'empty'
end
end
def edit
end
def update
return render('empty') unless can?(current_user, :create_wiki, @project)
@page = WikiPages::UpdateService.new(@project, current_user, wiki_params).execute(@page)
if @page.valid?
redirect_to(
project_wiki_path(@project, @page),
notice: _('Wiki was successfully updated.')
)
else
render 'edit'
end
rescue WikiPage::PageChangedError, WikiPage::PageRenameError, Gitlab::Git::Wiki::OperationError => e
@error = e
render 'edit'
end
def create
@page = WikiPages::CreateService.new(@project, current_user, wiki_params).execute
if @page.persisted?
redirect_to(
project_wiki_path(@project, @page),
notice: _('Wiki was successfully updated.')
)
else
render action: "edit"
end
rescue Gitlab::Git::Wiki::OperationError => e
@page = build_page(wiki_params)
@error = e
render 'edit'
end
def history
if @page
@page_versions = Kaminari.paginate_array(@page.versions(page: params[:page].to_i),
total_count: @page.count_versions)
.page(params[:page])
else
redirect_to(
project_wiki_path(@project, :home),
notice: _("Page not found")
)
end
end
def destroy
WikiPages::DestroyService.new(@project, current_user).execute(@page)
render 'show'
redirect_to project_wiki_path(@project, :home),
status: 302,
notice: _("Page was successfully deleted")
rescue Gitlab::Git::Wiki::OperationError => e
@error = e
render 'edit'
end
def git_access
......@@ -30,13 +122,74 @@ class Projects::WikisController < Projects::ApplicationController
private
def sort_params
process_params(sort_params_config)
def show_create_form?
can?(current_user, :create_wiki, @project) &&
@page.nil? &&
# Always show the create form when the wiki has had at least one page created.
# Otherwise, we only show the form when the user has navigated from
# the 'empty wiki' page
(@project_wiki.exists? || params[:view] == 'create')
end
def show_children_param
config = nesting_params_config(params[:sort])
def load_project_wiki
@project_wiki = load_wiki
# Call #wiki to make sure the Wiki Repo is initialized
@project_wiki.wiki
@sidebar_page = @project_wiki.find_sidebar(params[:version_id])
unless @sidebar_page # Fallback to default sidebar
@sidebar_wiki_entries = WikiPage.group_by_directory(@project_wiki.list_pages(limit: 15))
end
rescue ProjectWiki::CouldNotCreateWikiError
flash[:notice] = _("Could not create Wiki Repository at this time. Please try again later.")
redirect_to project_path(@project)
false
end
def load_wiki
ProjectWiki.new(@project, current_user)
end
def wiki_params
params.require(:wiki).permit(:title, :content, :format, :message, :last_commit_sha)
end
def build_page(args = {})
WikiPage.new(@project_wiki).tap do |page|
page.update_attributes(args) # rubocop:disable Rails/ActiveRecordAliases
end
end
def load_page
@page ||= @project_wiki.find_page(*page_params)
end
def page_params
keys = [:id]
keys << :version_id if params[:action] == 'show'
params.values_at(*keys)
end
def valid_encoding?
strong_memoize(:valid_encoding) do
@page.content.encoding == Encoding::UTF_8
end
end
def set_encoding_error
flash.now[:notice] = _("The content of this page is not encoded in UTF-8. Edits can only be made via the Git repository.")
end
def file_blob
strong_memoize(:file_blob) do
commit = @project_wiki.repository.commit(@project_wiki.default_branch)
next unless commit
process_params(config)
@project_wiki.repository.blob_at(commit.id, params[:id])
end
end
end
......@@ -290,6 +290,7 @@ module ApplicationSettingsHelper
:snowplow_cookie_domain,
:snowplow_enabled,
:snowplow_site_id,
:snowplow_iglu_registry_url,
:push_event_hooks_limit,
:push_event_activities_limit,
:custom_http_clone_url_root,
......
......@@ -41,12 +41,6 @@ module IconsHelper
ActionController::Base.helpers.image_path('file_icons.svg', host: sprite_base_url)
end
def sprite_icon_with_text(icon_name, content, opts = {})
wrapper_class = opts.delete(:wrapper_class)
icon = sprite_icon(icon_name, opts)
content_tag(:span, [icon, content].join('').html_safe, class: wrapper_class)
end
def sprite_icon(icon_name, size: nil, css_class: nil)
if Gitlab::Sentry.should_raise_for_dev?
unless known_sprites.include?(icon_name)
......
......@@ -48,23 +48,14 @@ module WikiHelper
expose_url(api_v4_projects_wikis_attachments_path(id: @project.id))
end
WIKI_SORT_CSS_CLASSES = 'btn btn-default has-tooltip reverse-sort-btn qa-reverse-sort rspec-reverse-sort'
def wiki_sort_controls(sort_params = {}, &block)
current_sort = sort_params[:sort] || ProjectWiki::TITLE_ORDER
current_direction = (sort_params[:direction] || 'asc').inquiry
reversed_direction = current_direction.desc? ? 'asc' : 'desc'
icon_class = current_direction.desc? ? 'highest' : 'lowest'
sorting = sort_params.merge(sort: current_sort, direction: reversed_direction)
opts = {
type: 'button',
class: WIKI_SORT_CSS_CLASSES,
title: _('Sort direction')
}
link_to(yield(sorting), opts) do
def wiki_sort_controls(project, sort, direction)
sort ||= ProjectWiki::TITLE_ORDER
link_class = 'btn btn-default has-tooltip reverse-sort-btn qa-reverse-sort rspec-reverse-sort'
reversed_direction = direction == 'desc' ? 'asc' : 'desc'
icon_class = direction == 'desc' ? 'highest' : 'lowest'
link_to(project_wikis_pages_path(project, sort: sort, direction: reversed_direction),
type: 'button', class: link_class, title: _('Sort direction')) do
sprite_icon("sort-#{icon_class}", size: 16)
end
end
......@@ -76,86 +67,4 @@ module WikiHelper
s_("Wiki|Title")
end
end
# Render the sprite icon given the current show_children state
def wiki_show_children_icon(nesting)
icon_name, icon_text =
case nesting
when ProjectWiki::NESTING_TREE
['folder-open', s_("Wiki|Show folder contents")]
when ProjectWiki::NESTING_CLOSED
['folder-o', s_("Wiki|Hide folder contents")]
else
['list-bulleted', s_("Wiki|Show files separately")]
end
sprite_icon_with_text(icon_name, icon_text, size: 16)
end
def wiki_page_link(wiki_page, nesting, project)
link = link_to(wiki_page.title,
project_wiki_path(project, wiki_page),
class: 'wiki-page-title')
case nesting
when ProjectWiki::NESTING_FLAT
tags = []
if wiki_page.directory.present?
wiki_dir = WikiDirectory.new(wiki_page.directory)
tags << link_to(wiki_dir.slug, project_wiki_dir_path(project, wiki_dir), class: 'wiki-page-dir-name')
tags << content_tag(:span, '/', class: 'wiki-page-name-separator')
end
tags << link
tags.join.html_safe
else
link
end
end
def sort_params_config
{
keys: [:sort, :direction],
defaults: {
sort: ProjectWiki::TITLE_ORDER, direction: ProjectWiki::DIRECTION_ASC
},
allowed: {
sort: ProjectWiki::SORT_ORDERS, direction: ProjectWiki::SORT_DIRECTIONS
}
}
end
def nesting_params_config(sort_key)
default_val = case sort_key
when ProjectWiki::CREATED_AT_ORDER
ProjectWiki::NESTING_FLAT
else
ProjectWiki::NESTING_CLOSED
end
{
keys: [:show_children],
defaults: { show_children: default_val },
allowed: { show_children: ProjectWiki::NESTINGS }
}
end
def process_params(config)
unprocessed = params.permit(*config[:keys])
processed = unprocessed
.with_defaults(config[:defaults])
.tap { |h| Gitlab::Utils.allow_hash_values(h, config[:allowed]) }
.to_hash
.transform_keys(&:to_sym)
if processed.keys == config[:keys]
processed.size == 1 ? processed.values.first : processed
else
raise ActionController::BadRequest, "illegal parameters: #{unprocessed}"
end
end
def home_page?
params[:id] == 'home'
end
end
......@@ -104,6 +104,11 @@ class ApplicationSetting < ApplicationRecord
hostname: true,
if: :snowplow_enabled
validates :snowplow_iglu_registry_url,
addressable_url: true,
allow_blank: true,
if: :snowplow_enabled
validates :pendo_url,
presence: true,
public_url: true,
......
......@@ -129,6 +129,7 @@ module ApplicationSettingImplementation
snowplow_cookie_domain: nil,
snowplow_enabled: false,
snowplow_site_id: nil,
snowplow_iglu_registry_url: nil,
custom_http_clone_url_root: nil,
pendo_enabled: false,
pendo_url: nil
......
......@@ -75,6 +75,11 @@ class Deployment < ApplicationRecord
find(ids)
end
def self.distinct_on_environment
order('environment_id, deployments.id DESC')
.select('DISTINCT ON (environment_id) deployments.*')
end
def self.find_successful_deployment!(iid)
success.find_by!(iid: iid)
end
......
......@@ -10,6 +10,7 @@ class Environment < ApplicationRecord
has_many :successful_deployments, -> { success }, class_name: 'Deployment'
has_one :last_deployment, -> { success.order('deployments.id DESC') }, class_name: 'Deployment'
has_one :last_visible_deployment, -> { visible.distinct_on_environment }, class_name: 'Deployment'
before_validation :nullify_external_url
before_validation :generate_slug, if: ->(env) { env.slug.blank? }
......
......@@ -17,13 +17,6 @@ class ProjectWiki
CREATED_AT_ORDER = 'created_at'
DIRECTION_DESC = 'desc'
DIRECTION_ASC = 'asc'
SORT_ORDERS = [TITLE_ORDER, CREATED_AT_ORDER].freeze
SORT_DIRECTIONS = [DIRECTION_ASC, DIRECTION_DESC].freeze
NESTING_FLAT = 'flat'
NESTING_TREE = 'tree'
NESTING_CLOSED = 'hidden'
NESTINGS = [NESTING_TREE, NESTING_CLOSED, NESTING_FLAT].freeze
# Returns a string describing what went wrong after
# an operation fails.
......@@ -65,11 +58,7 @@ class ProjectWiki
end
def wiki_base_path
::File.join(project_base_path, 'wikis')
end
def wiki_page_path
::File.join(project_base_path, '-', 'wiki_pages')
[Gitlab.config.gitlab.relative_url_root, '/', @project.full_path, '/wikis'].join('')
end
# Returns the Gitlab::Git::Wiki object.
......@@ -136,23 +125,6 @@ class ProjectWiki
end
end
# Finds directory within the repository based on a slug
#
# dir_name - The directory prefix.
#
# Returns an initialized WikiDirectory instance or nil
def find_dir(dir_name, sort = nil, direction = DIRECTION_ASC)
descending = direction == DIRECTION_DESC
# WikiListPagesRequest currently does not support server-side
# filtering. Ideally this logic should be moved to the gitaly
# side.
pages = wiki
.list_pages(sort: sort, direction_desc: descending)
.map { |page| WikiPage.new(self, page, true) }
.select { |wp| wp.directory == dir_name }
WikiDirectory.new(dir_name, pages) if pages.present?
end
def find_sidebar(version = nil)
find_page(SIDEBAR, version)
end
......@@ -172,12 +144,6 @@ class ProjectWiki
false
end
def build_page(attrs)
WikiPage.new(self).tap do |page|
page.update_attributes(attrs) # rubocop:disable Rails/ActiveRecordAliases
end
end
def update_page(page, content:, title: nil, format: :markdown, message: nil)
commit = commit_details(:updated, message, page.title)
......@@ -205,7 +171,7 @@ class ProjectWiki
title_array = title.split("/")
title = title_array.pop
[title, ::File.join(title_array)]
[title, title_array.join("/")]
end
def repository
......@@ -232,10 +198,6 @@ class ProjectWiki
private
def project_base_path
::File.join(Gitlab.config.gitlab.relative_url_root, @project.full_path)
end
def create_repo!(raw_repository)
gitlab_shell.create_wiki_repository(project)
......
# frozen_string_literal: true
class WikiDirectory
include StaticModel
include ActiveModel::Validations
attr_accessor :slug, :pages
validates :slug, presence: true
# StaticModel overrides and configuration:
def self.primary_key
'slug'
end
def id
"#{slug}@#{last_version&.sha}"
end
def self.model_name
ActiveModel::Name.new(self, nil, 'wiki_dir')
end
alias_method :to_param, :slug
alias_method :title, :slug
# Sorts and groups pages by directory.
#
# pages - an array of WikiPage objects.
#
# Returns an array of WikiPage and WikiDirectory objects.
# The entries are sorted in the order of the input array, where
# directories appear in the position of their first member.
def self.group_by_directory(pages)
grouped = []
dirs = Hash.new do |h, k|
new(k).tap { |dir| grouped << (h[k] = dir) }
end
Array.wrap(pages).each_with_object(grouped) do |page, top_level|
group = page.directory.present? ? dirs[page.directory] : top_level
group << page
end
end
def initialize(slug, pages = [])
@slug = slug
@pages = pages
end
def <<(page)
@pages << page
@last_version = nil
end
def last_version
@last_version ||= @pages.map(&:last_version).max_by(&:authored_date)
end
def page_count
@pages.size
end
def empty?
page_count.zero?
end
def to_partial_path(context = nil)
name = [context, 'wiki_directory'].compact.join('_')
"projects/wiki_directories/#{name}"
# Relative path to the partial to be used when rendering collections
# of this object.
def to_partial_path
'projects/wikis/wiki_directory'
end
end
......@@ -15,7 +15,30 @@ class WikiPage
end
def self.model_name
ActiveModel::Name.new(self, nil, 'wiki_page')
ActiveModel::Name.new(self, nil, 'wiki')
end
# Sorts and groups pages by directory.
#
# pages - an array of WikiPage objects.
#
# Returns an array of WikiPage and WikiDirectory objects. The entries are
# sorted by alphabetical order (directories and pages inside each directory).
# Pages at the root level come before everything.
def self.group_by_directory(pages)
return [] if pages.blank?
pages.each_with_object([]) do |page, grouped_pages|
next grouped_pages << page unless page.directory.present?
directory = grouped_pages.find do |obj|
obj.is_a?(WikiDirectory) && obj.slug == page.directory
end
next directory.pages << page if directory
grouped_pages << WikiDirectory.new(page.directory, [page])
end
end
def self.unhyphenize(name)
......@@ -43,16 +66,6 @@ class WikiPage
Gitlab::HookData::WikiPageBuilder.new(self).build
end
# Create a new WikiPage
#
# == Parameters:
# wiki::
# A `ProjectWiki` model object
# page::
# A `Gitlab::Git::WikiPage` business object, to which this class provides a facade
# persisted::
# Is this page fully saved on disk?
#
def initialize(wiki, page = nil, persisted = false)
@wiki = wiki
@page = page
......@@ -243,10 +256,10 @@ class WikiPage
end
end
def to_partial_path(context = nil)
name = [context, 'wiki_page'].compact.join('_')
"projects/wiki_pages/#{name}"
# Relative path to the partial to be used when rendering collections
# of this object.
def to_partial_path
'projects/wikis/wiki_page'
end
def id
......
......@@ -42,6 +42,10 @@ module Notes
clear_noteable_diffs_cache(note)
Suggestions::CreateService.new(note).execute
increment_usage_counter(note)
if Feature.enabled?(:notes_create_service_tracking, project)
Gitlab::Tracking.event('Notes::CreateService', 'execute', tracking_data_for(note))
end
end
if quick_actions_service.commands_executed_count.to_i > 0
......@@ -59,5 +63,16 @@ module Notes
note
end
private
def tracking_data_for(note)
label = Gitlab.ee? && note.author == User.visual_review_bot ? 'anonymous_visual_review_note' : 'note'
{
label: label,
value: note.id
}
end
end
end
......@@ -26,5 +26,8 @@
.form-group
= f.label :snowplow_cookie_domain, _('Cookie domain'), class: 'label-light'
= f.text_field :snowplow_cookie_domain, class: 'form-control'
.form-group
= f.label :snowplow_iglu_registry_url, _('Iglu registry URL (optional)'), class: 'label-light'
= f.text_field :snowplow_iglu_registry_url, class: 'form-control'
= f.submit _('Save changes'), class: 'btn btn-success'
......@@ -264,7 +264,7 @@
dismiss_endpoint: user_callouts_path } }
- if show_cluster_hint
.feature-highlight-popover-content
= image_tag 'illustrations/cluster_popover.svg', class: 'feature-highlight-illustration'
= image_tag 'illustrations/cluster_popover.svg', class: 'feature-highlight-illustration', lazy: false, alt: _('Kubernetes popover')
.feature-highlight-popover-sub-content
%p= _('Allows you to add and manage Kubernetes clusters.')
%p
......@@ -282,14 +282,14 @@
- if project_nav_tab? :wiki
- wiki_url = project_wiki_path(@project, :home)
= nav_link(controller: [:wikis, :wiki_pages, :wiki_directories]) do
= nav_link(controller: :wikis) do
= link_to wiki_url, class: 'shortcuts-wiki', data: { qa_selector: 'wiki_link' } do
.nav-icon-container
= sprite_icon('book')
%span.nav-item-name
= _('Wiki')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: [:wikis, :wiki_pages, :wiki_directories], html_options: { class: "fly-out-top-item" } ) do
= nav_link(controller: :wikis, html_options: { class: "fly-out-top-item" } ) do
= link_to wiki_url do
%strong.fly-out-top-item-name
= _('Wiki')
......
%li
%span.text-secondary-500.svg-icon.svg-baseline
- if @show_children
= sprite_icon('folder-open', size: 16)
- else
= sprite_icon('folder-o', size: 16)
= link_to wiki_dir.slug, project_wiki_dir_path(@project, wiki_dir)
- unless @show_children
%span.badge.badge-pill.wiki-dir-page-count= wiki_dir.page_count
.float-right
%small= (s_("Last edited %{date}") % { date: time_ago_with_tooltip(wiki_dir.last_version.authored_date) }).html_safe
- if @show_children
%ul
= render wiki_dir.pages, context: context
%li
%span.text-secondary-300.svg-icon.svg-baseline
= sprite_icon('folder-open', size: 16)
= link_to wiki_dir.slug, project_wiki_dir_path(@project, wiki_dir)
%ul= render wiki_dir.pages, context: context
= render wiki_directory.to_partial_path(context), wiki_dir: wiki_directory, context: context
- layout_path = 'shared/empty_states/wikis_layout'
- add_to_breadcrumbs _("Wiki"), project_wiki_path(@project, :home)
- add_to_breadcrumbs s_("Wiki|Pages"), project_wikis_pages_path(@project)
- breadcrumb_title s_(@wiki_dir.slug)
- page_title @wiki_dir.slug
- if can?(current_user, :create_wiki, @project)
- create_path = project_wiki_path(@project, params[:id], { view: 'create', params: { title: "#{params[:id]}/" } })
- create_link = link_to s_('WikiDirEmpty|Create a page in this directory'), create_path, class: 'btn btn-success qa-create-first-page-link', title: s_('WikiDirEmpty|Create a page')
= render layout: layout_path, locals: { image_path: 'illustrations/wiki_login_empty.svg' } do
%h4.text-left
= s_('WikiDirEmpty|This directory has no wiki pages')
%p.text-left
= s_("WikiDirEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on.")
= create_link
- elsif can?(current_user, :read_issue, @project)
- issues_link = link_to s_('WikiEmptyIssueMessage|issue tracker'), project_issues_path(@project)
- new_issue_link = link_to s_('WikiEmpty|Suggest wiki improvement'), new_project_issue_path(@project), class: 'btn btn-success', title: s_('WikiEmptyIssueMessage|Suggest wiki improvement')
= render layout: layout_path, locals: { image_path: 'illustrations/wiki_logout_empty.svg' } do
%h4
= s_('WikiDirEmpty|This directory has no wiki pages')
%p.text-left
= s_('WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}.').html_safe % { issues_link: issues_link }
= new_issue_link
- else
= render layout: layout_path, locals: { image_path: 'illustrations/wiki_logout_empty.svg' } do
%h4
= s_('WikiDirEmpty|This directory has no wiki pages')
%p
= s_('WikiEmpty|You must be a project member in order to add wiki pages.')
- add_to_breadcrumbs _("Wiki"), project_wiki_path(@project, :home)
- add_to_breadcrumbs s_("Wiki|Pages"), project_wikis_pages_path(@project)
- breadcrumb_title s_(@wiki_dir.slug)
- page_title @wiki_dir.slug
= render 'page_listing', { allow_change_nesting: false, wiki_page_title: page_title, page_path: ->(opts) { project_wiki_dir_path(@project, @wiki_dir, opts) } }
= link_to @page.human_title, project_wiki_path(@project, @page)
%span.light
= _('&middot;').html_safe
= subtitle
- is_active = params[:id] == wiki_page.slug
- icon_active_class = is_active ? 'text-secondary-800' : 'text-secondary-300'
%li{ class: active_when(is_active) }
%span.svg-icon.svg-baseline{ class: icon_active_class }
= sprite_icon('book', size: 16)
- if is_active
= wiki_page.human_title
- else
= link_to project_wiki_path(@project, wiki_page) do
= wiki_page.human_title
= render wiki_page.to_partial_path(context), wiki_page: wiki_page
- @content_class = 'wiki-page' + (fluid_layout ? '' : ' limit-container-width')
- breadcrumb_title @page.human_title
- page_title @page.human_title, _("Wiki")
- add_to_breadcrumbs _("Wiki"), project_wiki_path(@project, :home)
- add_to_breadcrumbs s_("Wiki|Pages"), project_wikis_pages_path(@project)
- if @page_dir.present?
- add_to_breadcrumbs _(@page_dir.slug), project_wiki_dir_path(@project, @page_dir)
.wiki-page-header.top-area.has-sidebar-toggle.flex-column.flex-lg-row
%button.btn.btn-default.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" }
= icon('angle-double-left')
.nav-text.flex-fill
%h2.wiki-page-title= @page.human_title
%span.wiki-last-edit-by
- if @page.last_version
= (_("Last edited by %{name}") % { name: "<strong>#{@page.last_version.author_name}</strong>" }).html_safe
#{time_ago_with_tooltip(@page.last_version.authored_date)}
.nav-controls.pb-md-3.pb-lg-0
= render 'main_links'
- if @page.historical?
.warning_message
= s_("WikiHistoricalPage|This is an old version of this page.")
- most_recent_link = link_to s_("WikiHistoricalPage|most recent version"), project_wiki_path(@project, @page)
- history_link = link_to s_("WikiHistoricalPage|history"), project_wiki_history_path(@project, @page)
= (s_("WikiHistoricalPage|You can view the %{most_recent_link} or browse the %{history_link}.") % { most_recent_link: most_recent_link, history_link: history_link }).html_safe
.prepend-top-default.append-bottom-default
.md.md-file.qa-wiki-page-content
= render_wiki_content(@page)
= render 'sidebar'
- form_classes = 'wiki-form common-note-form prepend-top-default js-quick-submit'
- form_classes += ' js-new-wiki-page' unless @page.persisted?
= form_for [@project.namespace.becomes(Namespace), @project, @page],
method: @page.persisted? ? 'put' : 'post',
url: { controller: 'wiki_pages', action: @page.persisted? ? :update : :create },
html: { class: form_classes },
data: { uploads_path: uploads_path } do |f|
= form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post,
html: { class: form_classes },
data: { uploads_path: uploads_path } do |f|
= form_errors(@page)
- if @page.persisted?
......@@ -14,7 +12,7 @@
.form-group.row
.col-sm-12= f.label :title, class: 'control-label-full-width'
.col-sm-12
= f.text_field :title, class: 'form-control qa-wiki-title-textbox', value: @page.title, required: true, autofocus: !@page.persisted?, placeholder: s_('Wiki|Page title')
= f.text_field :title, class: 'form-control qa-wiki-title-textbox', value: @page.title, required: true, autofocus: !@page.persisted?, placeholder: _('Wiki|Page title')
%span.d-inline-block.mw-100.prepend-top-5
= icon('lightbulb-o')
- if @page.persisted?
......
- if (@page && @page.persisted?)
- if can?(current_user, :create_wiki, @project)
= link_to project_wiki_pages_new_path(@project), class: "add-new-wiki btn btn-success", role: "button" do
= link_to project_wikis_new_path(@project), class: "add-new-wiki btn btn-success", role: "button" do
= s_("Wiki|New page")
= link_to project_wiki_history_path(@project, @page), class: "btn", role: "button" do
= s_("Wiki|Page history")
......
%li
%span.text-secondary-500.svg-icon.svg-baseline= sprite_icon('book', size: 16)
= wiki_page_link(wiki_page, @nesting, @project)
= link_to wiki_page.title, project_wiki_path(@project, wiki_page)
%small (#{wiki_page.format})
.float-right
%span.badge.badge-pill.wiki-page-format= _(wiki_page.format)
- if wiki_page.last_version
= '/'
%small= (s_("Last edited %{date}") % { date: time_ago_with_tooltip(wiki_page.last_version.authored_date) }).html_safe
%li{ class: active_when(params[:id] == wiki_page.slug) }
= link_to project_wiki_path(@project, wiki_page) do
= wiki_page.human_title
%li
= wiki_directory.slug
%ul
= render wiki_directory.pages, context: context
= render "#{context}_wiki_page", wiki_page: wiki_page
- @content_class = 'edit-wiki-page' + (fluid_layout ? '' : ' limit-container-width')
- add_to_breadcrumbs _("Wiki"), project_wiki_path(@project, :home)
- add_to_breadcrumbs s_("Wiki|Pages"), project_wikis_pages_path(@project)
- if @page.persisted? && @page_dir.present?
- add_to_breadcrumbs _(@page_dir.slug), project_wiki_dir_path(@project, @page_dir)
- if @page.persisted?
- add_to_breadcrumbs @page.human_title, project_wiki_path(@project, @page)
- @content_class = "limit-container-width" unless fluid_layout
- add_to_breadcrumbs _("Wiki"), project_wiki_path(@project, @page)
- breadcrumb_title @page.persisted? ? _("Edit") : _("New")
- page_title @page.persisted? ? _("Edit") : _("New"), @page.human_title, _("Wiki")
......@@ -17,7 +12,10 @@
.nav-text
%h2.wiki-page-title
- if @page.persisted?
= render partial: 'page_title', locals: { subtitle: s_("Wiki|Edit Page") }
= link_to @page.human_title, project_wiki_path(@project, @page)
%span.light
&middot;
= s_("Wiki|Edit Page")
- else
= s_("Wiki|Create New Page")
......
- @content_class = 'wiki-history'
- page_title _("History"), @page.human_title, _("Wiki")
.wiki-page-header.top-area.has-sidebar-toggle.flex-column.flex-lg-row
......@@ -7,7 +6,10 @@
.nav-text
%h2.wiki-page-title
= render partial: 'page_title', locals: { subtitle: _("History") }
= link_to @page.human_title, project_wiki_path(@project, @page)
%span.light
&middot;
= _("History")
.table-holder
%table.table
......@@ -37,4 +39,4 @@
= version.format
= paginate @page_versions, theme: 'gitlab'
= render 'shared/wiki/sidebar'
= render 'sidebar'
- add_to_breadcrumbs "Wiki", project_wiki_path(@project, :home)
- breadcrumb_title s_("Wiki|Pages")
- page_title s_("Wiki|Pages"), _("Wiki")
- sort_title = wiki_sort_title(params[:sort])
.wiki-page-header.top-area.flex-column.flex-lg-row
.nav-text.flex-fill
%h2.wiki-page-title
= s_("Wiki|Wiki Pages")
.nav-controls.pb-md-3.pb-lg-0
= link_to project_wikis_git_access_path(@project), class: 'btn' do
= icon('cloud-download')
= _("Clone repository")
.dropdown.inline.wiki-sort-dropdown
.btn-group{ role: 'group' }
.btn-group{ role: 'group' }
%button.dropdown-toggle{ type: 'button', data: { toggle: 'dropdown', display: 'static' }, class: 'btn btn-default' }
= sort_title
= icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable.dropdown-menu-sort
%li
= sortable_item(s_("Wiki|Title"), project_wikis_pages_path(@project, sort: ProjectWiki::TITLE_ORDER), sort_title)
= sortable_item(s_("Wiki|Created date"), project_wikis_pages_path(@project, sort: ProjectWiki::CREATED_AT_ORDER), sort_title)
= wiki_sort_controls(@project, params[:sort], params[:direction])
%ul.wiki-pages-list.content-list
= render @wiki_entries, context: 'pages'
= paginate @wiki_pages, theme: 'gitlab'
- add_to_breadcrumbs "Wiki", project_wiki_path(@project, :home)
- breadcrumb_title s_("Wiki|Pages")
- page_title s_("Wiki|Contents"), _("Wiki")
- @content_class = "limit-container-width" unless fluid_layout
- breadcrumb_title @page.human_title
- wiki_breadcrumb_dropdown_links(@page.slug)
- page_title @page.human_title, _("Wiki")
- add_to_breadcrumbs _("Wiki"), project_wiki_path(@project, :home)
= render 'page_listing', { allow_change_nesting: ::Feature.enabled?(:wikis_allow_change_nesting), wiki_page_title: page_title, page_path: ->(opts) { project_wikis_pages_path(@project, opts) } }
.wiki-page-header.top-area.has-sidebar-toggle.flex-column.flex-lg-row
%button.btn.btn-default.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" }
= icon('angle-double-left')
.nav-text.flex-fill
%h2.wiki-page-title= @page.human_title
%span.wiki-last-edit-by
- if @page.last_version
= (_("Last edited by %{name}") % { name: "<strong>#{@page.last_version.author_name}</strong>" }).html_safe
#{time_ago_with_tooltip(@page.last_version.authored_date)}
.nav-controls.pb-md-3.pb-lg-0
= render 'main_links'
- if @page.historical?
.warning_message
= s_("WikiHistoricalPage|This is an old version of this page.")
- most_recent_link = link_to s_("WikiHistoricalPage|most recent version"), project_wiki_path(@project, @page)
- history_link = link_to s_("WikiHistoricalPage|history"), project_wiki_history_path(@project, @page)
= (s_("WikiHistoricalPage|You can view the %{most_recent_link} or browse the %{history_link}.") % { most_recent_link: most_recent_link, history_link: history_link }).html_safe
.prepend-top-default.append-bottom-default
.md.md-file{ data: { qa_selector: 'wiki_page_content' } }
= render_wiki_content(@page)
= render 'sidebar'
- layout_path = 'shared/empty_states/wikis_layout'
- wiki_is_empty = @project_wiki.empty?
- empty_msg = wiki_is_empty ? s_('WikiEmpty|This project has no wiki pages') : s_('WikiEmpty|This page does not exist')
- create_msg = wiki_is_empty ? s_('WikiEmpty|Create your first page') : s_('WikiEmpty|Create this page')
- if can?(current_user, :create_wiki, @project)
- create_path = project_wiki_path(@project, params[:id], { view: 'create' })
- create_link = link_to create_msg, create_path, class: 'btn btn-success qa-create-first-page-link', title: create_msg
- create_link = link_to s_('WikiEmpty|Create your first page'), create_path, class: 'btn btn-success qa-create-first-page-link', title: s_('WikiEmpty|Create your first page')
= render layout: layout_path, locals: { image_path: 'illustrations/wiki_login_empty.svg' } do
%h4.text-left
......@@ -20,7 +17,7 @@
= render layout: layout_path, locals: { image_path: 'illustrations/wiki_logout_empty.svg' } do
%h4
= empty_msg
= s_('WikiEmpty|This project has no wiki pages')
%p.text-left
= s_('WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}.').html_safe % { issues_link: issues_link }
= new_issue_link
......@@ -28,6 +25,6 @@
- else
= render layout: layout_path, locals: { image_path: 'illustrations/wiki_logout_empty.svg' } do
%h4
= empty_msg
= s_('WikiEmpty|This project has no wiki pages')
%p
= s_('WikiEmpty|You must be a project member in order to add wiki pages.')
- @no_container = true
- current_sorting = params.permit(:sort, :direction)
- sort_title = wiki_sort_title(params[:sort])
%div{ class: container_class }
.wiki-page-header.top-area.flex-column.flex-lg-row
.nav-text.flex-fill
%h2.wiki-page-title
= wiki_page_title
.nav-controls.pb-md-3.pb-lg-0
- if can?(current_user, :create_wiki, @project)
= link_to project_wiki_pages_new_path(@project), class: "add-new-wiki btn btn-success" do
= s_("Wiki|New page")
= link_to project_wikis_git_access_path(@project), class: 'btn qa-clone-repository-link' do
= sprite_icon('download', size: 16)
= _("Clone repository")
- if @nesting.present? && allow_change_nesting
.dropdown.inline.wiki-nesting-dropdown
.btn-group{ role: 'group' }
%button.dropdown-toggle{ type: 'button', data: { toggle: 'dropdown', display: 'static' }, class: 'btn btn-default' }
= wiki_show_children_icon(@nesting)
= sprite_icon('chevron-down', size: 16)
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable.dropdown-menu-sort
- ProjectWiki::NESTINGS.each do |choice|
%li= link_to wiki_show_children_icon(choice), page_path.call(current_sorting.merge(show_children: choice)), class: @nesting == choice ? 'is-active' : ''
.dropdown.inline.wiki-sort-dropdown
.btn-group{ role: 'group' }
%button.dropdown-toggle{ type: 'button', data: { toggle: 'dropdown', display: 'static' }, class: 'btn btn-default' }
= sort_title
= sprite_icon('chevron-down', size: 16)
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable.dropdown-menu-sort
%li
= sortable_item(s_("Wiki|Title"), page_path.call(sort: ProjectWiki::TITLE_ORDER), sort_title)
= sortable_item(s_("Wiki|Created date"), page_path.call(sort: ProjectWiki::CREATED_AT_ORDER), sort_title)
= wiki_sort_controls(current_sorting.merge(show_children: @nesting), &page_path)
%ul.wiki-pages-list.content-list
= render @wiki_entries, context: 'pages'
= paginate @wiki_pages, theme: 'gitlab'
---
title: Fix cluster feature highlight popover image
merge_request: 19372
author:
type: fixed
---
title: Add ApplicationSetting for snowplow_iglu_registry_url
merge_request: 18449
author:
type: added
---
title: Sort wiki pages by date
merge_request: 30245
type: added
......@@ -617,7 +617,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end
# Since both wiki and repository routing contains wildcard characters
# its preferable to keep them below all other project routes
# its preferable to keep it below all other project routes
draw :wiki
draw :repository
......
scope(controller: :wikis) do
scope(path: 'wikis/pages', as: :wiki_pages, format: false) do
get :new, to: 'wiki_pages#new'
post '/', to: 'wiki_pages#create'
end
scope(path: 'wikis', as: :wikis) do
get :git_access
get :pages
get '/', to: redirect('%{namespace_id}/%{project_id}/-/wiki_pages/home')
get '/*id', to: redirect('%{namespace_id}/%{project_id}/-/wiki_pages/%{id}')
end
scope(path: '-/wiki_pages', as: :wiki_page, format: false) do
post '/', to: 'wiki_pages#create'
get :new
get '/', to: redirect('%{namespace_id}/%{project_id}/wikis/home')
post '/', to: 'wikis#create'
end
scope(path: '-/wiki_pages/*id', as: :wiki, format: false, controller: :wiki_pages) do
scope(path: 'wikis/*id', as: :wiki, format: false) do
get :edit
get :history
post :preview_markdown
......@@ -23,8 +15,4 @@ scope(controller: :wikis) do
put '/', action: :update
delete '/', action: :destroy
end
scope(path: '-/wiki_dirs/*id', as: :wiki_dir, format: false, controller: :wiki_directories) do
get '/', action: :show
end
end
# frozen_string_literal: true
class AddSnowplowIgluRegistryUrlToApplicationSettings < ActiveRecord::Migration[5.2]
DOWNTIME = false
def change
add_column :application_settings, :snowplow_iglu_registry_url, :string, limit: 255
end
end
# frozen_string_literal: true
class AddIndexToZoomMeetings < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index :zoom_meetings, :issue_status
end
def down
remove_concurrent_index :zoom_meetings, :issue_status if index_exists?(:zoom_meetings, :issue_status)
end
end
......@@ -338,6 +338,7 @@ ActiveRecord::Schema.define(version: 2019_10_26_124116) do
t.boolean "throttle_incident_management_notification_enabled", default: false, null: false
t.integer "throttle_incident_management_notification_period_in_seconds", default: 3600
t.integer "throttle_incident_management_notification_per_period", default: 3600
t.string "snowplow_iglu_registry_url", limit: 255
t.integer "push_event_hooks_limit", default: 3, null: false
t.integer "push_event_activities_limit", default: 3, null: false
t.string "custom_http_clone_url_root", limit: 511
......@@ -4041,6 +4042,7 @@ ActiveRecord::Schema.define(version: 2019_10_26_124116) do
t.string "url", limit: 255
t.index ["issue_id", "issue_status"], name: "index_zoom_meetings_on_issue_id_and_issue_status", unique: true, where: "(issue_status = 1)"
t.index ["issue_id"], name: "index_zoom_meetings_on_issue_id"
t.index ["issue_status"], name: "index_zoom_meetings_on_issue_status"
t.index ["project_id"], name: "index_zoom_meetings_on_project_id"
end
......
......@@ -316,6 +316,7 @@ are listed in the descriptions of the relevant settings.
| `snowplow_cookie_domain` | string | no | The Snowplow cookie domain. (e.g. `.gitlab.com`) |
| `snowplow_enabled` | boolean | no | Enable snowplow tracking. |
| `snowplow_site_id` | string | no | The Snowplow site name / application id. (e.g. `gitlab`) |
| `snowplow_iglu_registry_url` | string | no | The Snowplow base Iglu Schema Registry URL to use for custom context and self describing events'|
| `pendo_url` | string | required by: `pendo_enabled` | The Pendo endpoint url with js snippet. (e.g. `https://cdn.pendo.io/agent/static/your-api-key/pendo.js`) |
| `pendo_enabled` | boolean | no | Enable pendo tracking. |
| `terminal_max_session_time` | integer | no | Maximum time for web terminal websocket connection (in seconds). Set to `0` for unlimited time. |
......
......@@ -135,6 +135,7 @@ module API
optional :local_markdown_version, type: Integer, desc: 'Local markdown version, increase this value when any cached markdown should be invalidated'
optional :allow_local_requests_from_hooks_and_services, type: Boolean, desc: 'Deprecated: Use :allow_local_requests_from_web_hooks_and_services instead. Allow requests to the local network from hooks and services.' # support legacy names, can be removed in v5
optional :snowplow_enabled, type: Grape::API::Boolean, desc: 'Enable Snowplow tracking'
optional :snowplow_iglu_registry_url, type: String, desc: 'The Snowplow base Iglu Schema Registry URL to use for custom context and self describing events'
given snowplow_enabled: ->(val) { val } do
requires :snowplow_collector_hostname, type: String, desc: 'The Snowplow collector hostname'
optional :snowplow_cookie_domain, type: String, desc: 'The Snowplow cookie domain'
......
......@@ -6,7 +6,7 @@ module Banzai
class Rewriter
def initialize(link_string, wiki:, slug:)
@uri = Addressable::URI.parse(link_string)
@wiki_base_path = wiki && wiki.wiki_page_path
@wiki_base_path = wiki && wiki.wiki_base_path
@slug = slug
end
......
......@@ -47,7 +47,8 @@ module Gitlab
cookie_domain: Gitlab::CurrentSettings.snowplow_cookie_domain,
app_id: Gitlab::CurrentSettings.snowplow_site_id,
form_tracking: additional_features,
link_click_tracking: additional_features
link_click_tracking: additional_features,
iglu_registry_url: Gitlab::CurrentSettings.snowplow_iglu_registry_url
}.transform_keys! { |key| key.to_s.camelize(:lower).to_sym }
end
......
......@@ -78,6 +78,8 @@ module Gitlab
in_review_folder: count(::Environment.in_review_folder),
groups: count(Group),
issues: count(Issue),
issues_with_associated_zoom_link: count(ZoomMeeting.added_to_issue),
issues_using_zoom_quick_actions: count(ZoomMeeting.select(:issue_id).distinct),
keys: count(Key),
label_lists: count(List.label),
lfs_objects: count(LfsObject),
......
......@@ -130,15 +130,5 @@ module Gitlab
IPAddr.new(str)
rescue IPAddr::InvalidAddressError
end
# Filter a Hash against a mapping of keys to sets of allowed values.
#
# Keys that do not pass the filter will be removed from the Hash.
# This mutates the input hash.
def allow_hash_values(hash, allowed)
allowed.each do |key, allowed_values|
hash.delete(key) if hash.key?(key) && !allowed_values.include?(hash[key])
end
end
end
end
......@@ -411,9 +411,6 @@ msgstr ""
msgid "%{verb} %{time_spent_value} spent time."
msgstr ""
msgid "&middot;"
msgstr ""
msgid "'%{level}' is not a valid visibility level"
msgstr ""
......@@ -8848,6 +8845,9 @@ msgstr ""
msgid "If your HTTP repository is not publicly accessible, add your credentials."
msgstr ""
msgid "Iglu registry URL (optional)"
msgstr ""
msgid "ImageDiffViewer|2-up"
msgstr ""
......@@ -9543,6 +9543,9 @@ msgstr ""
msgid "Kubernetes error: %{error_code}"
msgstr ""
msgid "Kubernetes popover"
msgstr ""
msgid "LDAP"
msgstr ""
......@@ -18891,10 +18894,7 @@ msgstr ""
msgid "Wiki pages"
msgstr ""
msgid "Wiki was successfully created"
msgstr ""
msgid "Wiki was successfully updated"
msgid "Wiki was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
......@@ -18912,18 +18912,6 @@ msgstr ""
msgid "WikiClone|Start Gollum and edit locally"
msgstr ""
msgid "WikiDirEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
msgstr ""
msgid "WikiDirEmpty|Create a page"
msgstr ""
msgid "WikiDirEmpty|Create a page in this directory"
msgstr ""
msgid "WikiDirEmpty|This directory has no wiki pages"
msgstr ""
msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
msgstr ""
......@@ -18942,9 +18930,6 @@ msgstr ""
msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
msgstr ""
msgid "WikiEmpty|Create this page"
msgstr ""
msgid "WikiEmpty|Create your first page"
msgstr ""
......@@ -18954,9 +18939,6 @@ msgstr ""
msgid "WikiEmpty|The wiki lets you write documentation for your project"
msgstr ""
msgid "WikiEmpty|This page does not exist"
msgstr ""
msgid "WikiEmpty|This project has no wiki pages"
msgstr ""
......@@ -19011,9 +18993,6 @@ msgstr ""
msgid "WikiPage|Write your content or drag files here…"
msgstr ""
msgid "Wiki|Contents"
msgstr ""
msgid "Wiki|Create New Page"
msgstr ""
......@@ -19026,9 +19005,6 @@ msgstr ""
msgid "Wiki|Edit Page"
msgstr ""
msgid "Wiki|Hide folder contents"
msgstr ""
msgid "Wiki|More Pages"
msgstr ""
......@@ -19047,13 +19023,10 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
msgid "Wiki|Show files separately"
msgstr ""
msgid "Wiki|Show folder contents"
msgid "Wiki|Title"
msgstr ""
msgid "Wiki|Title"
msgid "Wiki|Wiki Pages"
msgstr ""
msgid "Will deploy to"
......
......@@ -5,7 +5,7 @@ module QA
module Project
module Wiki
class Edit < Page::Base
view 'app/views/shared/wiki/_main_links.html.haml' do
view 'app/views/projects/wikis/_main_links.html.haml' do
element :new_page_link, 'New page' # rubocop:disable QA/ElementWithPattern
element :page_history_link, 'Page history' # rubocop:disable QA/ElementWithPattern
element :edit_page_link, 'Edit' # rubocop:disable QA/ElementWithPattern
......
......@@ -7,7 +7,7 @@ module QA
class New < Page::Base
include Component::LazyLoader
view 'app/views/projects/wiki_pages/_form.html.haml' do
view 'app/views/projects/wikis/_form.html.haml' do
element :wiki_title_textbox
element :wiki_content_textarea
element :wiki_message_textbox
......
......@@ -7,11 +7,11 @@ module QA
class Show < Page::Base
include Page::Component::LegacyClonePanel
view 'app/views/shared/wiki/_page_listing.html.haml' do
view 'app/views/projects/wikis/pages.html.haml' do
element :clone_repository_link, 'Clone repository' # rubocop:disable QA/ElementWithPattern
end
view 'app/views/projects/wiki_pages/show.html.haml' do
view 'app/views/projects/wikis/show.html.haml' do
element :wiki_page_content
end
......
......@@ -13,7 +13,7 @@ module QA
resource.message = 'Update home'
end
validate_created('My First Wiki Content')
validate_content('My First Wiki Content')
Page::Project::Wiki::Edit.perform(&:click_edit)
Page::Project::Wiki::New.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName
......@@ -21,7 +21,7 @@ module QA
page.save_changes
end
validate_edited('My Second Wiki Content')
validate_content('My Second Wiki Content')
Resource::Repository::WikiPush.fabricate! do |push|
push.wiki = wiki
......@@ -34,12 +34,7 @@ module QA
expect(page).to have_content('My Third Wiki Content')
end
def validate_created(content)
expect(page).to have_content('Wiki was successfully created')
expect(page).to have_content(/#{content}/)
end
def validate_edited(content)
def validate_content(content)
expect(page).to have_content('Wiki was successfully updated')
expect(page).to have_content(/#{content}/)
end
......
......@@ -26,17 +26,35 @@ def emit_errors(static_analysis)
end
end
tasks = [
%w[bin/rake lint:all],
%w[bundle exec license_finder],
%w[yarn run eslint],
%w[yarn run stylelint],
%w[yarn run prettier-all],
%w[bundle exec rubocop --parallel],
%w[scripts/lint-conflicts.sh],
%w[scripts/lint-rugged]
]
def jobs_to_run(node_index, node_total)
all_tasks = [
%w[bin/rake lint:all],
%w[bundle exec license_finder],
%w[yarn run eslint],
%w[yarn run stylelint],
%w[yarn run prettier-all],
%w[bundle exec rubocop --parallel],
%w[scripts/lint-conflicts.sh],
%w[scripts/lint-rugged]
]
case node_total
when 1
all_tasks
when 2
rake_lint_all, *rest_jobs = all_tasks
case node_index
when 1
[rake_lint_all]
else
rest_jobs
end
else
raise "Parallelization > 2 (currently set to #{node_total}) isn't supported yet!"
end
end
tasks = jobs_to_run((ENV['CI_NODE_INDEX'] || 1).to_i, (ENV['CI_NODE_TOTAL'] || 1).to_i)
static_analysis = Gitlab::Popen::Runner.new
static_analysis.run(tasks) do |cmd, &run|
......
# frozen_string_literal: true
require 'spec_helper'
describe Projects::WikiDirectoriesController do
set(:project) { create(:project, :public, :repository) }
let(:user) { project.owner }
let(:project_wiki) { ProjectWiki.new(project, user) }
let(:wiki) { project_wiki.wiki }
let(:dir_slug) { 'the-directory' }
let(:dir_contents) { [create(:wiki_page)] }
let(:the_dir) { WikiDirectory.new(dir_slug, dir_contents) }
before do
allow(controller).to receive(:find_dir).and_return(the_dir)
sign_in(user)
end
describe 'GET #show' do
let(:show_params) do
{
namespace_id: project.namespace,
project_id: project,
id: dir_slug
}
end
before do
get :show, params: show_params
end
context 'the directory is empty' do
let(:the_dir) { nil }
it { is_expected.to render_template('empty') }
end
context 'the directory does exist' do
it { is_expected.to render_template('show') }
it 'sets the wiki_dir attribute' do
expect(assigns(:wiki_dir)).to eq(the_dir)
end
it 'assigns the wiki pages' do
expect(assigns(:wiki_pages)).to eq(dir_contents)
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Projects::WikiPagesController do
set(:project) { create(:project, :public, :repository) }
let(:user) { project.owner }
let(:project_wiki) { ProjectWiki.new(project, user) }
let(:wiki) { project_wiki.wiki }
let(:wiki_title) { 'page-title-test' }
let(:parent_ids) { { namespace_id: project.namespace.path, project_id: project.name } }
let(:redirect_destination) { Rails.application.routes.recognize_path(response.redirect_url) }
before do
create_page(wiki_title, 'hello world')
sign_in(user)
end
after do
destroy_page(wiki_title)
end
def helper
Helper.instance
end
class Helper
include Singleton
include ActionView::Helpers::UrlHelper
end
describe 'GET #new' do
subject { get :new, params: parent_ids }
it 'redirects to #show and appends a `random_title` param' do
subject
expect(response).to have_http_status(302)
expect(redirect_destination)
.to include(parent_ids.merge(controller: 'projects/wiki_pages', action: 'show'))
expect(response.redirect_url).to match(/\?random_title=true\Z/)
end
end
describe 'GET #show' do
render_views
let(:requested_wiki_page) { wiki_title }
let(:random_title) { nil }
subject do
get :show, params: {
namespace_id: project.namespace,
project_id: project,
id: requested_wiki_page,
random_title: random_title
}
end
context 'when the wiki repo cannot be created' do
before do
allow(controller).to receive(:load_wiki) { raise ProjectWiki::CouldNotCreateWikiError }
end
it 'redirects to the project path' do
headers = { 'Location' => a_string_ending_with(Gitlab::Routing.url_helpers.project_path(project)) }
subject
expect(response).to be_redirect
expect(response.header.to_hash).to include(headers)
end
end
context 'when the page exists' do
it 'limits the retrieved pages for the sidebar' do
expect(controller).to receive(:load_wiki).and_return(project_wiki)
# Sidebar entries
expect(project_wiki).to receive(:list_pages).with(limit: 15).and_call_original
subject
expect(response).to have_http_status(:ok)
expect(response.body).to include(wiki_title)
end
context 'when page content encoding is invalid' do
it 'sets flash error' do
allow(controller).to receive(:valid_encoding?).and_return(false)
subject
expect(response).to have_http_status(:ok)
expect(flash[:notice]).to eq 'The content of this page is not encoded in UTF-8. Edits can only be made via the Git repository.'
end
end
end
context 'when the page does not exist' do
let(:requested_wiki_page) { 'this-page-does-not-yet-exist' }
context 'the current user can create wiki pages' do
it { is_expected.to render_template('edit') }
it 'makes a call to see if the wiki is empty' do
expect(controller).to receive(:load_wiki).and_return(project_wiki)
expect(project_wiki).to receive(:list_pages).once.with(limit: anything).and_call_original
expect(project_wiki).to receive(:list_pages).with(limit: 1).and_call_original
subject
end
describe 'assigned title' do
shared_examples :wiki_page_with_correct_title do
it 'assigns the correct title' do
subject
expect(assigns(:page)).to have_attributes(title: assigned_title)
end
end
context 'random_title is absent' do
let(:random_title) { nil }
it_behaves_like :wiki_page_with_correct_title do
let(:assigned_title) { WikiPage.unhyphenize(requested_wiki_page) }
end
end
context 'random_title is present' do
let(:random_title) { true }
it_behaves_like :wiki_page_with_correct_title do
let(:assigned_title) { be_empty }
end
end
end
end
context 'the current user cannot create wiki pages' do
before do
forbid_controller_ability! :create_wiki
end
it { is_expected.to render_template('missing_page') }
end
end
context 'when page is a file' do
include WikiHelpers
let(:path) { upload_file_to_wiki(project, user, file_name) }
before do
get :show, params: { namespace_id: project.namespace, project_id: project, id: path }
end
context 'when file is an image' do
let(:file_name) { 'dk.png' }
it 'delivers the image' do
expect(response.headers['Content-Disposition']).to match(/^inline/)
expect(response.headers[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
end
context 'when file is a svg' do
let(:file_name) { 'unsanitized.svg' }
it 'delivers the image' do
expect(response.headers['Content-Disposition']).to match(/^inline/)
expect(response.headers[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
end
end
end
context 'when file is a pdf' do
let(:file_name) { 'git-cheat-sheet.pdf' }
it 'sets the content type to sets the content response headers' do
expect(response.headers['Content-Disposition']).to match(/^inline/)
expect(response.headers[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
end
end
end
end
describe 'POST #preview_markdown' do
let(:page_id) { 'page/path' }
let(:markdown_text) { '*Markdown* text' }
let(:wiki_page) { create(:wiki_page, wiki: project_wiki, attrs: { title: wiki_title }) }
let(:processed_md) { json_response.fetch('body') }
let(:preview_params) do
{ namespace_id: project.namespace, project_id: project, id: wiki_page.slug, text: markdown_text }
end
before do
post :preview_markdown, params: preview_params
end
it 'renders json in a correct format' do
expect(response).to have_http_status(:ok)
expect(json_response).to include('body' => String, 'references' => Hash)
end
describe 'double brackets within backticks' do
let(:markdown_text) do
<<-HEREDOC
`[[do_not_linkify]]`
```
[[also_do_not_linkify]]
```
HEREDOC
end
it "does not linkify double brackets inside code blocks as expected" do
expect(processed_md).to include('[[do_not_linkify]]', '[[also_do_not_linkify]]')
end
end
describe 'link re-writing' do
let(:links) do
[
{ text: 'regular link', path: 'regular' },
{ text: 'relative link 1', path: '../relative' },
{ text: 'relative link 2', path: './relative' },
{ text: 'relative link 3', path: './e/f/relative' },
{ text: 'spaced link', path: 'title with spaces' }
]
end
shared_examples :wiki_link_rewriter do
let(:markdown_text) { links.map { |text:, path:| "[#{text}](#{path})" }.join("\n") }
let(:expected_links) do
links.zip(paths).map do |(link, path)|
helper.link_to(link[:text], "#{project_wiki.wiki_page_path}/#{path}")
end
end
it 'processes the links correctly' do
expect(processed_md).to include(*expected_links)
end
end
context 'the current page has spaces in its title' do
let(:wiki_title) { 'page a/page b/page c/page d' }
it_behaves_like :wiki_link_rewriter do
let(:paths) do
['regular',
'page-a/page-b/relative',
'page-a/page-b/page-c/relative',
'page-a/page-b/page-c/e/f/relative',
'title%20with%20spaces']
end
end
end
context 'the current page has an unproblematic title' do
let(:wiki_title) { 'a/b/c/d' }
it_behaves_like :wiki_link_rewriter do
let(:paths) do
['regular', 'a/b/relative', 'a/b/c/relative', 'a/b/c/e/f/relative', 'title%20with%20spaces']
end
end
end
context "when there are hyphens in the page name" do
let(:wiki_title) { 'page-a/page-b/page-c/page-d' }
it_behaves_like :wiki_link_rewriter do
let(:paths) do
['regular',
'page-a/page-b/relative',
'page-a/page-b/page-c/relative',
'page-a/page-b/page-c/e/f/relative',
'title%20with%20spaces']
end
end
end
end
end
describe 'GET #edit' do
subject { get(:edit, params: { namespace_id: project.namespace, project_id: project, id: wiki_title }) }
context 'when page content encoding is invalid' do
it 'redirects to show' do
allow(controller).to receive(:valid_encoding?).and_return(false)
subject
expect(response).to redirect_to(project_wiki_path(project, project_wiki.list_pages.first))
end
end
context 'when page content encoding is valid' do
render_views
it 'shows the edit page' do
subject
expect(response).to have_http_status(:ok)
expect(response.body).to include('Edit Page')
end
end
end
describe 'PATCH #update' do
let(:new_title) { 'New title' }
let(:new_content) { 'New content' }
subject do
patch(:update,
params: {
namespace_id: project.namespace,
project_id: project,
id: wiki_title,
wiki_page: { title: new_title, content: new_content }
})
end
context 'when page content encoding is invalid' do
it 'redirects to show' do
allow(controller).to receive(:valid_encoding?).and_return(false)
subject
expect(response).to redirect_to(project_wiki_path(project, project_wiki.list_pages.first))
end
end
context 'when page content encoding is valid' do
render_views
it 'updates the page' do
subject
wiki_page = project_wiki.list_pages(load_content: true).first
expect(wiki_page.title).to eq new_title
expect(wiki_page.content).to eq new_content
end
end
end
describe 'GET #history' do
before do
allow(controller)
.to receive(:can?)
.with(any_args)
.and_call_original
# The :create_wiki permission is irrelevant to reading history.
expect(controller)
.not_to receive(:can?)
.with(anything, :create_wiki, any_args)
allow(controller)
.to receive(:can?)
.with(anything, :read_wiki, any_args)
.and_return(allow_read_wiki)
end
shared_examples 'fetching history' do |expected_status|
before do
get :history, params: { namespace_id: project.namespace, project_id: project, id: wiki_title }
end
it "returns status #{expected_status}" do
expect(response).to have_http_status(expected_status)
end
end
it_behaves_like 'fetching history', :ok do
let(:allow_read_wiki) { true }
it 'assigns @page_versions' do
expect(assigns(:page_versions)).to be_present
end
end
it_behaves_like 'fetching history', :not_found do
let(:allow_read_wiki) { false }
end
end
private
def create_page(name, content)
wiki.write_page(name, :markdown, content, commit_details(name))
end
def commit_details(name)
Gitlab::Git::Wiki::CommitDetails.new(user.id, user.username, user.name, user.email, "created page #{name}")
end
def destroy_page(title, dir = '')
page = wiki.page(title: title, dir: dir)
project_wiki.delete_page(page, "test commit")
end
end
......@@ -4,10 +4,10 @@ require 'spec_helper'
describe Projects::WikisController do
let_it_be(:project) { create(:project, :public, :repository) }
let_it_be(:user) { project.owner }
let_it_be(:project_wiki) { ProjectWiki.new(project, user) }
let_it_be(:wiki) { project_wiki.wiki }
let_it_be(:wiki_title) { 'page title test' }
let(:user) { project.owner }
let(:project_wiki) { ProjectWiki.new(project, user) }
let(:wiki) { project_wiki.wiki }
let(:wiki_title) { 'page title test' }
before do
create_page(wiki_title, 'hello world')
......@@ -19,86 +19,231 @@ describe Projects::WikisController do
destroy_page(wiki_title)
end
describe 'GET #pages' do
subject do
get :pages, params: { namespace_id: project.namespace, project_id: project, id: wiki_title }.merge(extra_params)
describe 'GET #new' do
subject { get :new, params: { namespace_id: project.namespace, project_id: project } }
it 'redirects to #show and appends a `random_title` param' do
subject
expect(response).to have_http_status(302)
expect(Rails.application.routes.recognize_path(response.redirect_url)).to include(
controller: 'projects/wikis',
action: 'show'
)
expect(response.redirect_url).to match(/\?random_title=true\Z/)
end
end
let(:extra_params) { {} }
describe 'GET #pages' do
subject { get :pages, params: { namespace_id: project.namespace, project_id: project, id: wiki_title } }
it 'does not load the pages content' do
expect(controller).to receive(:load_wiki).and_return(project_wiki)
expect(project_wiki).to receive(:list_pages).twice.and_call_original
subject
end
end
describe 'illegal params' do
shared_examples :a_bad_request do
it do
expect { subject }.to raise_error(ActionController::BadRequest)
end
end
describe 'GET #history' do
before do
allow(controller)
.to receive(:can?)
.with(any_args)
.and_call_original
describe ':sort' do
let(:extra_params) { { sort: 'wibble' } }
# The :create_wiki permission is irrelevant to reading history.
expect(controller)
.not_to receive(:can?)
.with(anything, :create_wiki, any_args)
it_behaves_like :a_bad_request
end
allow(controller)
.to receive(:can?)
.with(anything, :read_wiki, any_args)
.and_return(allow_read_wiki)
end
describe ':direction' do
let(:extra_params) { { direction: 'wibble' } }
shared_examples 'fetching history' do |expected_status|
before do
get :history, params: { namespace_id: project.namespace, project_id: project, id: wiki_title }
end
it_behaves_like :a_bad_request
it "returns status #{expected_status}" do
expect(response).to have_http_status(expected_status)
end
end
describe ':show_children' do
let(:extra_params) { { show_children: 'wibble' } }
it_behaves_like 'fetching history', :ok do
let(:allow_read_wiki) { true }
it_behaves_like :a_bad_request
it 'assigns @page_versions' do
expect(assigns(:page_versions)).to be_present
end
end
shared_examples 'sorting-and-nesting' do |sort_key, default_nesting|
context "the user is sorting by #{sort_key}" do
let(:extra_params) { sort_params.merge(nesting_params) }
let(:sort_params) { { sort: sort_key } }
let(:nesting_params) { {} }
it_behaves_like 'fetching history', :not_found do
let(:allow_read_wiki) { false }
end
end
describe 'GET #show' do
render_views
let(:random_title) { nil }
subject { get :show, params: { namespace_id: project.namespace, project_id: project, id: id, random_title: random_title } }
context 'when page exists' do
let(:id) { wiki_title }
it 'limits the retrieved pages for the sidebar' do
expect(controller).to receive(:load_wiki).and_return(project_wiki)
expect(project_wiki).to receive(:list_pages).with(limit: 15).and_call_original
subject
expect(response).to have_http_status(:ok)
expect(assigns(:page).title).to eq(wiki_title)
end
context 'when page content encoding is invalid' do
it 'sets flash error' do
allow(controller).to receive(:valid_encoding?).and_return(false)
before do
subject
expect(response).to have_http_status(:ok)
expect(flash[:notice]).to eq('The content of this page is not encoded in UTF-8. Edits can only be made via the Git repository.')
end
end
end
context 'when the page does not exist' do
let(:id) { 'does not exist' }
it "sets nesting to #{default_nesting} by default" do
expect(assigns :nesting).to eq default_nesting
before do
subject
end
it 'builds a new wiki page with the id as the title' do
expect(assigns(:page).title).to eq(id)
end
context 'when a random_title param is present' do
let(:random_title) { true }
it 'builds a new wiki page with no title' do
expect(assigns(:page).title).to be_empty
end
end
end
it 'hides children if the default requires it' do
expect(assigns :show_children).to be(default_nesting != ProjectWiki::NESTING_CLOSED)
context 'when page is a file' do
include WikiHelpers
let(:id) { upload_file_to_wiki(project, user, file_name) }
before do
subject
end
context 'when file is an image' do
let(:file_name) { 'dk.png' }
it 'delivers the image' do
expect(response.headers['Content-Disposition']).to match(/^inline/)
expect(response.headers[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
end
ProjectWiki::NESTINGS.each do |nesting|
context "the user explicitly passes show_children = #{nesting}" do
let(:nesting_params) { { show_children: nesting } }
context 'when file is a svg' do
let(:file_name) { 'unsanitized.svg' }
it 'sets nesting to the provided value' do
expect(assigns :nesting).to eq nesting
end
it 'delivers the image' do
expect(response.headers['Content-Disposition']).to match(/^inline/)
expect(response.headers[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
end
end
end
context 'the user wants children hidden' do
let(:nesting_params) { { show_children: 'hidden' } }
context 'when file is a pdf' do
let(:file_name) { 'git-cheat-sheet.pdf' }
it 'hides children' do
expect(assigns :show_children).to be false
end
it 'sets the content type to sets the content response headers' do
expect(response.headers['Content-Disposition']).to match(/^inline/)
expect(response.headers[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
end
end
end
end
describe 'POST #preview_markdown' do
it 'renders json in a correct format' do
post :preview_markdown, params: { namespace_id: project.namespace, project_id: project, id: 'page/path', text: '*Markdown* text' }
expect(json_response.keys).to match_array(%w(body references))
end
end
describe 'GET #edit' do
subject { get(:edit, params: { namespace_id: project.namespace, project_id: project, id: wiki_title }) }
context 'when page content encoding is invalid' do
it 'redirects to show' do
allow(controller).to receive(:valid_encoding?).and_return(false)
subject
expect(response).to redirect_to(project_wiki_path(project, project_wiki.list_pages.first))
end
end
include_examples 'sorting-and-nesting', ProjectWiki::CREATED_AT_ORDER, ProjectWiki::NESTING_FLAT
include_examples 'sorting-and-nesting', ProjectWiki::TITLE_ORDER, ProjectWiki::NESTING_CLOSED
context 'when page content encoding is valid' do
render_views
it 'shows the edit page' do
subject
expect(response).to have_http_status(:ok)
expect(response.body).to include('Edit Page')
end
end
end
describe 'PATCH #update' do
let(:new_title) { 'New title' }
let(:new_content) { 'New content' }
subject do
patch(:update,
params: {
namespace_id: project.namespace,
project_id: project,
id: wiki_title,
wiki: { title: new_title, content: new_content }
})
end
context 'when page content encoding is invalid' do
it 'redirects to show' do
allow(controller).to receive(:valid_encoding?).and_return(false)
subject
expect(response).to redirect_to(project_wiki_path(project, project_wiki.list_pages.first))
end
end
context 'when page content encoding is valid' do
render_views
it 'updates the page' do
subject
wiki_page = project_wiki.list_pages(load_content: true).first
expect(wiki_page.title).to eq new_title
expect(wiki_page.content).to eq new_content
end
end
end
def create_page(name, content)
......
......@@ -18,12 +18,12 @@ FactoryBot.define do
association :wiki, factory: :project_wiki, strategy: :build
initialize_with { new(wiki, page, true) }
before(:create) do |wiki_page, evaluator|
wiki_page.attributes = evaluator.attrs.with_indifferent_access
before(:create) do |page, evaluator|
page.attributes = evaluator.attrs
end
to_create do |wiki_page|
wiki_page.create
to_create do |page|
page.create
end
end
end
......@@ -3,9 +3,8 @@
require 'spec_helper'
describe 'Edit Project Settings' do
set(:project) { create(:project, :public, :repository) }
let(:member) { create(:user) }
let!(:project) { create(:project, :public, :repository) }
let!(:issue) { create(:issue, project: project) }
let(:non_member) { create(:user) }
......@@ -82,88 +81,85 @@ describe 'Edit Project Settings' do
end
describe 'project features visibility pages' do
set(:pipeline) { create(:ci_empty_pipeline, project: project) }
set(:job) { create(:ci_build, pipeline: pipeline) }
where(:method_name, :build_url) do
[
[:builds, -> { project_job_path(project, job) }],
[:issues, -> { project_issues_path(project) }],
[:wiki, -> { project_wiki_path(project, :home) }],
[:snippets, -> { project_snippets_path(project) }],
[:merge_requests, -> { project_merge_requests_path(project) }]
]
let(:pipeline) { create(:ci_empty_pipeline, project: project) }
let(:job) { create(:ci_build, pipeline: pipeline) }
let(:tools) do
{
builds: project_job_path(project, job),
issues: project_issues_path(project),
wiki: project_wiki_path(project, :home),
snippets: project_snippets_path(project),
merge_requests: project_merge_requests_path(project)
}
end
with_them do
let(:url) { build_url.call }
let(:attr_name) { "#{method_name}_access_level" }
context 'normal user' do
before do
project.team.truncate
sign_in(member)
end
it 'renders 200 if tool is enabled' do
project.project_feature.update_attribute(attr_name, ProjectFeature::ENABLED)
context 'normal user' do
before do
sign_in(member)
end
it 'renders 200 if tool is enabled' do
tools.each do |method_name, url|
project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::ENABLED)
visit url
expect(page.status_code).to eq(200)
end
end
it 'renders 404 if feature is disabled' do
project.project_feature.update_attribute(attr_name, ProjectFeature::DISABLED)
it 'renders 404 if feature is disabled' do
tools.each do |method_name, url|
project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::DISABLED)
visit url
expect(page.status_code).to eq(404)
end
end
it 'renders 404 if feature is enabled only for team members' do
project.project_feature.update_attribute(attr_name, ProjectFeature::PRIVATE)
it 'renders 404 if feature is enabled only for team members' do
project.team.truncate
tools.each do |method_name, url|
project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::PRIVATE)
visit url
expect(page.status_code).to eq(404)
end
end
it 'renders 200 if user is member of group' do
group = create(:group)
project.group = group
project.save
group.add_owner(member)
it 'renders 200 if user is member of group' do
group = create(:group)
project.group = group
project.save
project.project_feature.update_attribute(attr_name, ProjectFeature::PRIVATE)
group.add_owner(member)
tools.each do |method_name, url|
project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::PRIVATE)
visit url
expect(page.status_code).to eq(200)
end
end
end
context 'admin user' do
before do
non_member.update_attribute(:admin, true)
project.team.truncate
sign_in(non_member)
end
context 'admin user' do
before do
non_member.update_attribute(:admin, true)
sign_in(non_member)
end
it 'renders 404 if feature is disabled' do
it 'renders 404 if feature is disabled' do
tools.each do |method_name, url|
project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::DISABLED)
visit url
expect(page.status_code).to eq(404)
end
end
it 'renders 200 if feature is enabled only for team members' do
project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::PRIVATE)
it 'renders 200 if feature is enabled only for team members' do
project.team.truncate
tools.each do |method_name, url|
project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::PRIVATE)
visit url
expect(page.status_code).to eq(200)
end
end
......
......@@ -4,54 +4,164 @@ require 'spec_helper'
describe 'Projects > Wiki > User previews markdown changes', :js do
set(:user) { create(:user) }
set(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
let(:project_wiki) { ProjectWiki.new(project, user) }
let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
let(:wiki_page) { create(:wiki_page, wiki: project.wiki, attrs: { title: 'home', content: '[some link](other-page)' }) }
let(:wiki_content) do
<<-HEREDOC
[regular link](regular)
[relative link 1](../relative)
[relative link 2](./relative)
[relative link 3](./e/f/relative)
[spaced link](title with spaces)
HEREDOC
end
before do
project.add_maintainer(user)
sign_in(user)
init_home!
end
def init_home!
create(:wiki_page, wiki: project.wiki, attrs: { title: 'home', content: '[some link](other-page)' })
end
context "while creating a new wiki page" do
context "when there are no spaces or hyphens in the page name" do
it "rewrites relative links as expected" do
create_wiki_page('a/b/c/d', content: wiki_content)
def fill_in_content!
page.within '.wiki-form' do
fill_in :wiki_page_content, with: wiki_content
expect(page).to have_content("regular link")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/regular\">regular link</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/relative\">relative link 1</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/relative\">relative link 2</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/e/f/relative\">relative link 3</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
end
end
end
def show_preview!
page.within '.wiki-form' do
click_on 'Preview'
context "when there are spaces in the page name" do
it "rewrites relative links as expected" do
create_wiki_page('a page/b page/c page/d page', content: wiki_content)
expect(page).to have_content("regular link")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/regular\">regular link</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
end
end
end
context 'when writing a new page' do
let(:new_wiki_path) { 'a/b/c/d' }
let(:wiki_content) { 'Some [awesome wiki](content)' }
context "when there are hyphens in the page name" do
it "rewrites relative links as expected" do
create_wiki_page('a-page/b-page/c-page/d-page', content: wiki_content)
it 'can show a preview of markdown content' do
visit project_wiki_pages_new_path(project, id: new_wiki_path)
fill_in_content!
show_preview!
expect(page).to have_content("regular link")
expect(page).to have_link('awesome wiki')
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/regular\">regular link</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
end
end
end
context 'when editing an existing page' do
let(:wiki_content) { 'Some [bemusing](content)' }
let(:wiki_page) { create(:wiki_page, wiki: project_wiki) }
context "while editing a wiki page" do
context "when there are no spaces or hyphens in the page name" do
it "rewrites relative links as expected" do
create_wiki_page('a/b/c/d')
click_link 'Edit'
fill_in :wiki_content, with: wiki_content
click_on "Preview"
expect(page).to have_content("regular link")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/regular\">regular link</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/relative\">relative link 1</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/relative\">relative link 2</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/e/f/relative\">relative link 3</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
end
end
context "when there are spaces in the page name" do
it "rewrites relative links as expected" do
create_wiki_page('a page/b page/c page/d page')
click_link 'Edit'
fill_in :wiki_content, with: wiki_content
click_on "Preview"
expect(page).to have_content("regular link")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/regular\">regular link</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
end
end
context "when there are hyphens in the page name" do
it "rewrites relative links as expected" do
create_wiki_page('a-page/b-page/c-page/d-page')
click_link 'Edit'
fill_in :wiki_content, with: wiki_content
click_on "Preview"
it 'can show a preview of markdown content, when writing' do
visit project_wiki_edit_path(project, wiki_page)
fill_in_content!
show_preview!
expect(page).to have_content("regular link")
expect(page).to have_link('bemusing')
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/regular\">regular link</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
end
end
context 'when rendering the preview' do
it 'renders content with CommonMark' do
create_wiki_page('a-page/b-page/c-page/common-mark')
click_link 'Edit'
fill_in :wiki_content, with: "1. one\n - sublist\n"
click_on "Preview"
# the above generates two separate lists (not embedded) in CommonMark
expect(page).to have_content("sublist")
expect(page).not_to have_xpath("//ol//li//ul")
end
end
end
it "does not linkify double brackets inside code blocks as expected" do
wiki_content = <<-HEREDOC
`[[do_not_linkify]]`
```
[[also_do_not_linkify]]
```
HEREDOC
create_wiki_page('linkify_test', wiki_content)
expect(page).to have_content("do_not_linkify")
expect(page.html).to include('[[do_not_linkify]]')
expect(page.html).to include('[[also_do_not_linkify]]')
end
private
def create_wiki_page(path, content = 'content')
visit project_wiki_path(project, wiki_page)
click_link 'New page'
fill_in :wiki_title, with: path
fill_in :wiki_content, with: content
click_button 'Create page'
end
end
......@@ -3,15 +3,9 @@
require "spec_helper"
describe "User creates wiki page" do
include CapybaraHelpers
include WikiHelpers
set(:user) { create(:user) }
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:wiki) { ProjectWiki.new(project, user) }
let(:new_page) { WikiPage.new(wiki) }
let(:message_field) { form_field_name(new_page, :message) }
let(:project) { create(:project) }
before do
project.add_maintainer(user)
......@@ -19,76 +13,36 @@ describe "User creates wiki page" do
sign_in(user)
end
def start_writing(page_path)
click_link("New page")
fill_in(:wiki_page_title, with: page_path)
end
def create_page(attrs = {})
page.within(".wiki-form") do
attrs.each do |k, v|
fill_in("wiki_page_#{k}".to_sym, with: v)
end
end
click_on("Create page")
end
shared_examples 'updates commit message' do
describe 'commit message', :js do
it "has `Create home` as a commit message" do
wait_for_requests
expect(page).to have_field(message_field, with: "Create home")
end
end
end
context "when wiki is empty" do
before do
visit(project_wikis_path(project))
click_link "Create your first page"
find('.wiki-form')
end
context "in a user namespace" do
let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
let(:wiki_page_content) { '' }
it "shows validation error message" do
create_page
expect(page)
.to have_content("The form contains the following error:")
.and have_content("Content can't be blank")
.and have_css('.wiki-form')
.and have_css('.qa-create-page-button')
end
it 'offers to create pages that do not yet exist' do
create_page(content: "[link test](test)")
page.within(".wiki-form") do
fill_in(:wiki_content, with: "")
expect(page)
.to have_content("Home")
.and have_content("link test")
click_on("Create page")
end
click_link("link test")
expect(page).to have_content("The form contains the following error:").and have_content("Content can't be blank")
expect(page).to have_content("Create New Page")
end
page.within(".wiki-form") do
fill_in(:wiki_content, with: "[link test](test)")
it "has a link to the parent directory in the pages sidebar" do
wiki_full_path = "one/two/three-test"
create_page(title: wiki_full_path, content: 'wiki content')
click_on("Create page")
end
wiki_page = wiki.find_page(wiki_full_path)
expect(wiki_page).to be_present
dir = wiki.find_dir(wiki_page.directory)
expect(dir).to be_present
expect(page).to have_content("Home").and have_content("link test")
expect(current_path).to include(wiki_full_path)
click_link("link test")
expect(page).to have_link(dir.slug, href: project_wiki_dir_path(project, dir))
expect(page).to have_content("Create New Page")
end
it "shows non-escaped link in the pages list", :quarantine do
......@@ -104,17 +58,19 @@ describe "User creates wiki page" do
expect(page).to have_xpath("//a[@href='/#{project.full_path}/wikis/one/two/three-test']")
end
it_behaves_like 'updates commit message'
it "has `Create home` as a commit message", :js do
wait_for_requests
expect(page).to have_field("wiki[message]", with: "Create home")
end
it "creates a page from the home page" do
page_content = <<~WIKI_CONTENT
[test](test)
[GitLab API doc](api)
[Rake tasks](raketasks)
# Wiki header
WIKI_CONTENT
fill_in(:wiki_content, with: "[test](test)\n[GitLab API doc](api)\n[Rake tasks](raketasks)\n# Wiki header\n")
fill_in(:wiki_message, with: "Adding links to wiki")
create_page(content: page_content, message: "Adding links to wiki")
page.within(".wiki-form") do
click_button("Create page")
end
expect(current_path).to eq(project_wiki_path(project, "home"))
expect(page).to have_content("test GitLab API doc Rake tasks Wiki header")
......@@ -155,7 +111,7 @@ describe "User creates wiki page" do
end
end
it "creates ASCIIdoc wiki with LaTeX blocks", :js do
it "creates ASCII wiki with LaTeX blocks", :js do
stub_application_setting(plantuml_url: "http://localhost", plantuml_enabled: true)
ascii_content = <<~MD
......@@ -176,25 +132,37 @@ describe "User creates wiki page" do
stem:[2+2] is 4
MD
find("#wiki_page_format option[value=asciidoc]").select_option
find("#wiki_format option[value=asciidoc]").select_option
fill_in(:wiki_content, with: ascii_content)
create_page(content: ascii_content)
page.within(".wiki-form") do
click_button("Create page")
end
page.within ".md" do
expect(page).to have_selector(".katex", count: 3).and have_content("2+2 is 4")
end
end
it_behaves_like 'wiki file attachments'
it_behaves_like 'wiki file attachments', :quarantine
end
context "in a group namespace" do
context "in a group namespace", :js do
let(:project) { create(:project, :wiki_repo, namespace: create(:group, :public)) }
it_behaves_like 'updates commit message'
it "has `Create home` as a commit message" do
wait_for_requests
it "creates a page from the home page" do
create_page(content: "My awesome wiki!")
expect(page).to have_field("wiki[message]", with: "Create home")
end
it "creates a page from the home page", :quarantine do
page.within(".wiki-form") do
fill_in(:wiki_content, with: "My awesome wiki!")
click_button("Create page")
end
expect(page).to have_content("Home")
.and have_content("Last edited by #{user.name}")
......@@ -210,37 +178,76 @@ describe "User creates wiki page" do
visit(project_wikis_path(project))
end
shared_examples 'creates page by slug' do |slug, unslug|
it "creates #{slug}" do
start_writing(slug)
context "in a user namespace" do
let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
# Commit message field should have correct value.
expect(page).to have_field(message_field, with: "Create #{unslug}")
context "via the `new wiki page` page" do
it "creates a page with a single word" do
click_link("New page")
create_page(content: "My awesome wiki!")
page.within(".wiki-form") do
fill_in(:wiki_title, with: "foo")
fill_in(:wiki_content, with: "My awesome wiki!")
end
expect(page).to have_content(unslug)
.and have_content("Last edited by #{user.name}")
.and have_content("My awesome wiki!")
end
end
# Commit message field should have correct value.
expect(page).to have_field("wiki[message]", with: "Create foo")
context "in a user namespace" do
let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
click_button("Create page")
context "via the `new wiki page` page" do
include_examples 'creates page by slug', 'foo', 'foo'
include_examples 'creates page by slug', 'Spaces in the name', 'Spaces in the name'
include_examples 'creates page by slug', 'Hyphens-in-the-name', 'Hyphens in the name'
expect(page).to have_content("foo")
.and have_content("Last edited by #{user.name}")
.and have_content("My awesome wiki!")
end
it "creates a page with spaces in the name" do
click_link("New page")
page.within(".wiki-form") do
fill_in(:wiki_title, with: "Spaces in the name")
fill_in(:wiki_content, with: "My awesome wiki!")
end
# Commit message field should have correct value.
expect(page).to have_field("wiki[message]", with: "Create Spaces in the name")
click_button("Create page")
expect(page).to have_content("Spaces in the name")
.and have_content("Last edited by #{user.name}")
.and have_content("My awesome wiki!")
end
it "creates a page with hyphens in the name" do
click_link("New page")
page.within(".wiki-form") do
fill_in(:wiki_title, with: "hyphens-in-the-name")
fill_in(:wiki_content, with: "My awesome wiki!")
end
# Commit message field should have correct value.
expect(page).to have_field("wiki[message]", with: "Create hyphens in the name")
page.within(".wiki-form") do
fill_in(:wiki_content, with: "My awesome wiki!")
click_button("Create page")
end
expect(page).to have_content("hyphens in the name")
.and have_content("Last edited by #{user.name}")
.and have_content("My awesome wiki!")
end
end
it "shows the emoji autocompletion dropdown" do
start_writing('text-autocomplete')
click_link("New page")
page.within(".wiki-form") do
find("#wiki_page_content").native.send_keys("")
find("#wiki_content").native.send_keys("")
fill_in(:wiki_page_content, with: ":")
fill_in(:wiki_content, with: ":")
end
expect(page).to have_selector(".atwho-view")
......@@ -251,9 +258,23 @@ describe "User creates wiki page" do
let(:project) { create(:project, :wiki_repo, namespace: create(:group, :public)) }
context "via the `new wiki page` page" do
include_examples 'creates page by slug', 'foo', 'foo'
include_examples 'creates page by slug', 'Spaces in the name', 'Spaces in the name'
include_examples 'creates page by slug', 'Hyphens-in-the-name', 'Hyphens in the name'
it "creates a page" do
click_link("New page")
page.within(".wiki-form") do
fill_in(:wiki_title, with: "foo")
fill_in(:wiki_content, with: "My awesome wiki!")
end
# Commit message field should have correct value.
expect(page).to have_field("wiki[message]", with: "Create foo")
click_button("Create page")
expect(page).to have_content("foo")
.and have_content("Last edited by #{user.name}")
.and have_content("My awesome wiki!")
end
end
end
end
......
......@@ -6,7 +6,6 @@ describe 'User deletes wiki page', :js do
let(:user) { create(:user) }
let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
let(:wiki_page) { create(:wiki_page, wiki: project.wiki) }
let(:project_wiki) { ProjectWiki.new(project, user) }
before do
sign_in(user)
......@@ -19,6 +18,5 @@ describe 'User deletes wiki page', :js do
find('.modal-footer .btn-danger').click
expect(page).to have_content('Page was successfully deleted')
expect(project_wiki.find_page(wiki_page.slug)).to be nil
end
end
......@@ -10,13 +10,6 @@ describe 'User updates wiki page' do
sign_in(user)
end
def create_page(attrs = {})
page.within('.wiki-form') do
attrs.each { |k, v| fill_in("wiki_page_#{k}".to_sym, with: v) }
click_on('Create page')
end
end
context 'when wiki is empty' do
before do
visit(project_wikis_path(project))
......@@ -35,7 +28,12 @@ describe 'User updates wiki page' do
end
it 'updates a page that has a path', :js do
create_page(title: 'one/two/three-test', content: 'wiki content')
fill_in(:wiki_title, with: 'one/two/three-test')
page.within '.wiki-form' do
fill_in(:wiki_content, with: 'wiki content')
click_on('Create page')
end
expect(current_path).to include('one/two/three-test')
expect(find('.wiki-pages')).to have_content('three')
......@@ -74,9 +72,9 @@ describe 'User updates wiki page' do
it 'updates a page', :js do
# Commit message field should have correct value.
expect(page).to have_field('wiki_page[message]', with: 'Update home')
expect(page).to have_field('wiki[message]', with: 'Update home')
fill_in(:wiki_page_content, with: 'My awesome wiki!')
fill_in(:wiki_content, with: 'My awesome wiki!')
click_button('Save changes')
expect(page).to have_content('Home')
......@@ -85,31 +83,31 @@ describe 'User updates wiki page' do
end
it 'updates the commit message as the title is changed', :js do
fill_in(:wiki_page_title, with: 'Wiki title')
fill_in(:wiki_title, with: 'Wiki title')
expect(page).to have_field('wiki_page[message]', with: 'Update Wiki title')
expect(page).to have_field('wiki[message]', with: 'Update Wiki title')
end
it 'does not allow XSS', :js do
fill_in(:wiki_page_title, with: '<script>')
fill_in(:wiki_title, with: '<script>')
expect(page).to have_field('wiki_page[message]', with: 'Update &lt;script&gt;')
expect(page).to have_field('wiki[message]', with: 'Update &lt;script&gt;')
end
it 'shows a validation error message' do
fill_in(:wiki_page_content, with: '')
fill_in(:wiki_content, with: '')
click_button('Save changes')
expect(page).to have_selector('.wiki-form')
expect(page).to have_content('Edit Page')
expect(page).to have_content('The form contains the following error:')
expect(page).to have_content("Content can't be blank")
expect(find('textarea#wiki_page_content').value).to eq('')
expect(find('textarea#wiki_content').value).to eq('')
end
it 'shows the emoji autocompletion dropdown', :js do
find('#wiki_page_content').native.send_keys('')
fill_in(:wiki_page_content, with: ':')
find('#wiki_content').native.send_keys('')
fill_in(:wiki_content, with: ':')
expect(page).to have_selector('.atwho-view')
end
......@@ -145,9 +143,9 @@ describe 'User updates wiki page' do
it 'updates a page', :js do
# Commit message field should have correct value.
expect(page).to have_field('wiki_page[message]', with: 'Update home')
expect(page).to have_field('wiki[message]', with: 'Update home')
fill_in(:wiki_page_content, with: 'My awesome wiki!')
fill_in(:wiki_content, with: 'My awesome wiki!')
click_button('Save changes')
......@@ -171,43 +169,50 @@ describe 'User updates wiki page' do
visit(project_wiki_edit_path(project, wiki_page))
end
def edit_title!(title)
fill_in(:wiki_page_title, with: title)
it 'moves the page to the root folder' do
fill_in(:wiki_title, with: "/#{page_name}")
click_button('Save changes')
end
it 'moves the page to the root folder' do
edit_title!("/#{page_name}")
expect(current_path).to eq(project_wiki_path(project, page_name))
end
it 'moves the page to other dir' do
new_page_path = "baz/quux/#{page_name}"
edit_title!(new_page_path)
new_page_dir = "foo1/bar1/#{page_name}"
fill_in(:wiki_title, with: new_page_dir)
click_button('Save changes')
expect(current_path).to eq(project_wiki_path(project, new_page_path))
expect(current_path).to eq(project_wiki_path(project, new_page_dir))
end
it 'remains in the same place if title has not changed' do
original_path = project_wiki_path(project, wiki_page)
edit_title!(page_name)
fill_in(:wiki_title, with: page_name)
click_button('Save changes')
expect(current_path).to eq(original_path)
end
it 'can be moved to a different dir with a different name' do
new_page_path = "quux/baz/new_page_name"
edit_title!(new_page_path)
new_page_dir = "foo1/bar1/new_page_name"
fill_in(:wiki_title, with: new_page_dir)
expect(current_path).to eq(project_wiki_path(project, new_page_path))
click_button('Save changes')
expect(current_path).to eq(project_wiki_path(project, new_page_dir))
end
it 'can be renamed and moved to the root folder' do
new_name = 'new_page_name'
edit_title!("/#{new_name}")
fill_in(:wiki_title, with: "/#{new_name}")
click_button('Save changes')
expect(current_path).to eq(project_wiki_path(project, new_name))
end
......@@ -215,7 +220,9 @@ describe 'User updates wiki page' do
it 'squishes the title before creating the page' do
new_page_dir = " foo1 / bar1 / #{page_name} "
edit_title!(new_page_dir)
fill_in(:wiki_title, with: new_page_dir)
click_button('Save changes')
expect(current_path).to eq(project_wiki_path(project, "foo1/bar1/#{page_name}"))
end
......
......@@ -8,11 +8,10 @@ describe 'User views a wiki page' do
let(:user) { create(:user) }
let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
let(:path) { 'image.png' }
let(:wiki_content) { "Look at this [image](#{path})\n\n ![alt text](#{path})" }
let(:wiki_page) do
create(:wiki_page,
wiki: project.wiki,
attrs: { title: 'home', content: wiki_content })
attrs: { title: 'home', content: "Look at this [image](#{path})\n\n ![alt text](#{path})" })
end
before do
......@@ -20,18 +19,17 @@ describe 'User views a wiki page' do
sign_in(user)
end
def create_page(attrs = {})
page.within('.wiki-form') do
attrs.each { |k, v| fill_in("wiki_page_#{k}".to_sym, with: v) }
click_on('Create page')
end
end
context 'when wiki is empty' do
before do
visit(project_wikis_path(project))
click_link "Create your first page"
create_page(title: 'one/two/three-test', content: 'wiki content')
fill_in(:wiki_title, with: 'one/two/three-test')
page.within('.wiki-form') do
fill_in(:wiki_content, with: 'wiki content')
click_on('Create page')
end
end
it 'shows the history of a page that has a path', :js do
......@@ -85,27 +83,24 @@ describe 'User views a wiki page' do
context 'shows a file stored in a page' do
let(:path) { upload_file_to_wiki(project, user, 'dk.png') }
let(:image_path) { project_wiki_path(project, path) }
it do
expect(page).to have_xpath("//img[@data-src='#{image_path}']")
expect(page).to have_link('image', href: "#{image_path}")
expect(page).to have_xpath("//img[@data-src='#{project.wiki.wiki_base_path}/#{path}']")
expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/#{path}")
click_on('image')
expect(current_path).to match(path)
expect(current_path).to match("wikis/#{path}")
expect(page).not_to have_xpath('/html') # Page should render the image which means there is no html involved
end
end
it 'shows the creation page if file does not exist' do
href = project_wiki_path(project, path)
expect(page).to have_link('image', href: href)
expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/#{path}")
click_on('image')
expect(current_path).to match(href)
expect(current_path).to match("wikis/#{path}")
expect(page).to have_content('Create New Page')
end
end
......
......@@ -7,7 +7,6 @@ describe 'User views wiki pages' do
let(:user) { create(:user) }
let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
let(:project_wiki) { ProjectWiki.new(project, user) }
let!(:wiki_page1) do
create(:wiki_page, wiki: project.wiki, attrs: { title: '3 home', content: '3' })
......@@ -18,182 +17,73 @@ describe 'User views wiki pages' do
let!(:wiki_page3) do
create(:wiki_page, wiki: project.wiki, attrs: { title: '2 home', content: '2' })
end
let!(:wiki_page4) do
create(:wiki_page, wiki: project.wiki, attrs: { title: 'sub-folder/0', content: 'a' })
end
let!(:wiki_page5) do
create(:wiki_page, wiki: project.wiki, attrs: { title: 'sub-folder/b', content: 'b' })
end
let(:page_link_selector) { 'a' }
let(:pages) do
page.all(".wiki-pages-list li #{page_link_selector}")
page.find('.wiki-pages-list').all('li').map { |li| li.find('a') }
end
let(:wikis_allow_change_nesting) { false }
before do
stub_feature_flags(wikis_allow_change_nesting: wikis_allow_change_nesting)
project.add_maintainer(user)
sign_in(user)
visit(project_wikis_pages_path(project))
end
def sort_desc!
page.within('.wiki-sort-dropdown') do
page.find('.qa-reverse-sort').click
end
end
def sort_by_created_at!
page.within('.wiki-sort-dropdown') do
click_button('Title')
click_link('Created date')
end
end
shared_examples 'correctly_sorted_pages' do
it 'has pages displayed in correct order' do
displayed_texts = pages.map(&:text)
expect(displayed_texts).to eq expected_sequence.map(&:title)
end
end
context 'ordered by title' do
let(:sub_folder) { project_wiki.find_dir('sub-folder') }
let(:pages_ordered_by_title) { [wiki_page2, wiki_page3, wiki_page1] }
context 'default display settings' do
context 'asc' do
let(:expected_sequence) { [wiki_page2, wiki_page3, wiki_page1, sub_folder] }
it_behaves_like 'correctly_sorted_pages'
end
context 'desc' do
before do
sort_desc!
context 'asc' do
it 'pages are displayed in direct order' do
pages.each.with_index do |page_title, index|
expect(page_title.text).to eq(pages_ordered_by_title[index].title)
end
let(:expected_sequence) { [sub_folder, wiki_page1, wiki_page3, wiki_page2] }
it_behaves_like 'correctly_sorted_pages'
end
end
context 'changing nesting is disabled' do
let(:wikis_allow_change_nesting) { false }
it 'does not display a nesting controller' do
expect(page).not_to have_css('.wiki-nesting-dropdown')
end
end
context 'changing nesting is enabled' do
let(:wikis_allow_change_nesting) { true }
it 'displays a nesting controller' do
expect(page).to have_css('.wiki-nesting-dropdown')
end
context 'tree' do
before do
page.within('.wiki-nesting-dropdown') do
click_link 'Show folder contents'
end
end
context 'asc' do
let(:expected_sequence) { [wiki_page2, wiki_page3, wiki_page1, sub_folder, wiki_page4, wiki_page5] }
it_behaves_like 'correctly_sorted_pages'
end
context 'desc' do
before do
sort_desc!
end
let(:expected_sequence) { [sub_folder, wiki_page5, wiki_page4, wiki_page1, wiki_page3, wiki_page2] }
it_behaves_like 'correctly_sorted_pages'
end
end
context 'nested' do
before do
page.within('.wiki-nesting-dropdown') do
click_link 'Hide folder contents'
end
end
context 'asc' do
let(:expected_sequence) { [wiki_page2, wiki_page3, wiki_page1, sub_folder] }
it_behaves_like 'correctly_sorted_pages'
end
context 'desc' do
before do
sort_desc!
end
let(:expected_sequence) { [sub_folder, wiki_page1, wiki_page3, wiki_page2] }
it_behaves_like 'correctly_sorted_pages'
context 'desc' do
before do
page.within('.wiki-sort-dropdown') do
page.find('.rspec-reverse-sort').click
end
end
context 'flat' do
before do
page.within('.wiki-nesting-dropdown') do
click_link 'Show files separately'
end
end
let(:page_link_selector) { 'a.wiki-page-title' }
context 'asc' do
let(:expected_sequence) { [wiki_page2, wiki_page3, wiki_page1, wiki_page4, wiki_page5] }
it_behaves_like 'correctly_sorted_pages'
end
context 'desc' do
before do
sort_desc!
end
let(:expected_sequence) { [wiki_page5, wiki_page4, wiki_page1, wiki_page3, wiki_page2] }
it_behaves_like 'correctly_sorted_pages'
it 'pages are displayed in reversed order' do
pages.reverse_each.with_index do |page_title, index|
expect(page_title.text).to eq(pages_ordered_by_title[index].title)
end
end
end
end
context 'ordered by created_at' do
let(:pages_ordered_by_created_at) { [wiki_page1, wiki_page2, wiki_page3, wiki_page4, wiki_page5] }
let(:pages_ordered_by_created_at) { [wiki_page1, wiki_page2, wiki_page3] }
before do
sort_by_created_at!
page.within('.wiki-sort-dropdown') do
click_button('Title')
click_link('Created date')
end
end
let(:page_link_selector) { 'a.wiki-page-title' }
context 'asc' do
let(:expected_sequence) { [wiki_page1, wiki_page2, wiki_page3, wiki_page4, wiki_page5] }
it_behaves_like 'correctly_sorted_pages'
it 'pages are displayed in direct order' do
pages.each.with_index do |page_title, index|
expect(page_title.text).to eq(pages_ordered_by_created_at[index].title)
end
end
end
context 'desc' do
before do
sort_desc!
page.within('.wiki-sort-dropdown') do
page.find('.rspec-reverse-sort').click
end
end
let(:expected_sequence) { [wiki_page5, wiki_page4, wiki_page3, wiki_page2, wiki_page1] }
it_behaves_like 'correctly_sorted_pages'
it 'pages are displayed in reversed order' do
pages.reverse_each.with_index do |page_title, index|
expect(page_title.text).to eq(pages_ordered_by_created_at[index].title)
end
end
end
end
end
......@@ -3,27 +3,27 @@ import { setHTMLFixture } from './helpers/fixtures';
describe('Wikis', () => {
describe('setting the commit message when the title changes', () => {
const editFormHtmlFixture = args => `<form class="wiki-form ${
args.newPage ? 'js-new-wiki-page' : ''
}">
<input type="text" id="wiki_title" value="My title" />
<input type="text" id="wiki_message" />
</form>`;
let wikis;
let titleInput;
let messageInput;
const CREATE = true;
const UPDATE = false;
const editFormHtmlFixture = newPage =>
`<form class="wiki-form ${newPage ? 'js-new-wiki-page' : ''}">
<input type="text" id="wiki_page_title" value="My title" />
<input type="text" id="wiki_page_message" />
</form>`;
describe('when the wiki page is being created', () => {
const formHtmlFixture = editFormHtmlFixture({ newPage: true });
const init = newPage => {
setHTMLFixture(editFormHtmlFixture(newPage));
titleInput = document.getElementById('wiki_page_title');
messageInput = document.getElementById('wiki_page_message');
wikis = new Wikis();
};
beforeEach(() => {
setHTMLFixture(formHtmlFixture);
describe('when the wiki page is being created', () => {
beforeEach(() => init(CREATE));
titleInput = document.getElementById('wiki_title');
messageInput = document.getElementById('wiki_message');
wikis = new Wikis();
});
it('binds an event listener to the title input', () => {
wikis.handleWikiTitleChange = jest.fn();
......@@ -51,7 +51,15 @@ describe('Wikis', () => {
});
describe('when the wiki page is being updated', () => {
beforeEach(() => init(UPDATE));
const formHtmlFixture = editFormHtmlFixture({ newPage: false });
beforeEach(() => {
setHTMLFixture(formHtmlFixture);
titleInput = document.getElementById('wiki_title');
messageInput = document.getElementById('wiki_message');
wikis = new Wikis();
});
it('sets the commit message when title changes, prefixing with "Update"', () => {
titleInput.value = 'My title';
......
......@@ -23,13 +23,8 @@ describe WikiHelper do
describe '#wiki_sort_controls' do
let(:project) { create(:project) }
let(:classes) { described_class::WIKI_SORT_CSS_CLASSES }
subject(:wiki_link) do
helper.wiki_sort_controls(sort: sort, direction: direction) do |opts|
project_wikis_pages_path(project, opts)
end
end
let(:wiki_link) { helper.wiki_sort_controls(project, sort, direction) }
let(:classes) { "btn btn-default has-tooltip reverse-sort-btn qa-reverse-sort rspec-reverse-sort" }
def expected_link(sort, direction, icon_class)
path = "/#{project.full_path}/wikis/pages?direction=#{direction}&sort=#{sort}"
......@@ -67,18 +62,6 @@ describe WikiHelper do
end
end
describe '#wiki_show_children_icon' do
ProjectWiki::NESTINGS.each do |nesting|
context "When the nesting parameter is `#{nesting}`" do
let(:element) { helper.wiki_show_children_icon(nesting) }
it 'produces something that contains an SVG' do
expect(element).to match(/svg/)
end
end
end
end
describe '#wiki_sort_title' do
it 'returns a title corresponding to a key' do
expect(helper.wiki_sort_title('created_at')).to eq('Created date')
......
......@@ -11,10 +11,6 @@ describe Banzai::Filter::WikiLinkFilter do
let(:wiki) { ProjectWiki.new(project, user) }
let(:repository_upload_folder) { Wikis::CreateAttachmentService::ATTACHMENT_PATH }
def upload_href(file_name)
::File.join(wiki.wiki_page_path, repository_upload_folder, file_name)
end
it "doesn't rewrite absolute links" do
filtered_link = filter("<a href='http://example.com:8000/'>Link</a>", project_wiki: wiki).children[0]
......@@ -32,12 +28,12 @@ describe Banzai::Filter::WikiLinkFilter do
it 'rewrites links' do
filtered_link = filter("<a href='#{repository_upload_folder}/a.test'>Link</a>", project_wiki: wiki).children[0]
expect(filtered_link.attribute('href').value).to eq(upload_href "a.test")
expect(filtered_link.attribute('href').value).to eq("#{wiki.wiki_base_path}/#{repository_upload_folder}/a.test")
end
end
context 'with "img" html tag' do
let(:path) { upload_href "a.jpg" }
let(:path) { "#{wiki.wiki_base_path}/#{repository_upload_folder}/a.jpg" }
context 'inside an "a" html tag' do
it 'rewrites links' do
......@@ -61,7 +57,7 @@ describe Banzai::Filter::WikiLinkFilter do
it 'rewrites links' do
filtered_link = filter("<video src='#{repository_upload_folder}/a.mp4'></video>", project_wiki: wiki).children[0]
expect(filtered_link.attribute('src').value).to eq(upload_href "a.mp4")
expect(filtered_link.attribute('src').value).to eq("#{wiki.wiki_base_path}/#{repository_upload_folder}/a.mp4")
end
end
......@@ -69,8 +65,7 @@ describe Banzai::Filter::WikiLinkFilter do
it 'rewrites links' do
filtered_link = filter("<audio src='#{repository_upload_folder}/a.wav'></audio>", project_wiki: wiki).children[0]
# expect(filtered_link.attribute('src').value).to eq("#{wiki.wiki_base_path}/#{repository_upload_folder}/a.wav")
expect(filtered_link.attribute('src').value).to eq(upload_href "a.wav")
expect(filtered_link.attribute('src').value).to eq("#{wiki.wiki_base_path}/#{repository_upload_folder}/a.wav")
end
end
end
......
......@@ -3,12 +3,6 @@
require 'spec_helper'
describe Banzai::Pipeline::WikiPipeline do
let_it_be(:namespace) { create(:namespace, name: "wiki_link_ns") }
let_it_be(:project) { create(:project, :public, name: "wiki_link_project", namespace: namespace) }
let_it_be(:project_wiki) { ProjectWiki.new(project, double(:user)) }
let_it_be(:page) { build(:wiki_page, wiki: project_wiki, page: OpenStruct.new(url_path: 'nested/twice/start-page')) }
let(:prefix) { project_wiki.wiki_page_path }
describe 'TableOfContents' do
it 'replaces the tag with the TableOfContentsFilter result' do
markdown = <<-MD.strip_heredoc
......@@ -60,138 +54,132 @@ describe Banzai::Pipeline::WikiPipeline do
end
describe "Links" do
shared_examples 'a correct link rewrite' do
it 'rewrites links correctly' do
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
expect(output).to include("href=\"#{page_href}\"")
end
end
shared_examples 'link examples' do |test_name|
let(:page_href) { "#{prefix}/#{expected_page_path}" }
context "when GitLab is hosted at a #{test_name} URL" do
let(:namespace) { create(:namespace, name: "wiki_link_ns") }
let(:project) { create(:project, :public, name: "wiki_link_project", namespace: namespace) }
let(:project_wiki) { ProjectWiki.new(project, double(:user)) }
let(:page) { build(:wiki_page, wiki: project_wiki, page: OpenStruct.new(url_path: 'nested/twice/start-page')) }
{ "when GitLab is hosted at a root URL" => '/',
"when GitLab is hosted at a relative URL" => '/nested/relative/gitlab' }.each do |test_name, relative_url_root|
context test_name do
before do
allow(Gitlab.config.gitlab).to receive(:relative_url_root).and_return(relative_url_root)
end
describe "linking to pages within the wiki" do
let(:markdown) { "[Page](#{nesting}page#{extension})" }
context "when creating hierarchical links to the current directory" do
let(:nesting) { './' }
context 'non file links' do
let(:extension) { '' }
let(:expected_page_path) { 'nested/twice/page' }
it_behaves_like 'a correct link rewrite'
it "rewrites non-file links to be at the scope of the current directory" do
markdown = "[Page](./page)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/nested/twice/page\"")
end
context 'file-like links' do
let(:extension) { '.md' }
let(:expected_page_path) { 'nested/twice/page.md' }
it_behaves_like 'a correct link rewrite'
it "rewrites file links to be at the scope of the current directory" do
markdown = "[Link to Page](./page.md)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/nested/twice/page.md\"")
end
end
context "when creating hierarchical links to the parent directory" do
let(:nesting) { '../' }
context "non file links" do
let(:extension) { '' }
let(:expected_page_path) { 'nested/page' }
it_behaves_like 'a correct link rewrite'
it "rewrites non-file links to be at the scope of the parent directory" do
markdown = "[Link to Page](../page)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/nested/page\"")
end
context "file-like links" do
let(:extension) { '.md' }
let(:expected_page_path) { 'nested/page.md' }
it_behaves_like 'a correct link rewrite'
it "rewrites file links to be at the scope of the parent directory" do
markdown = "[Link to Page](../page.md)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/nested/page.md\"")
end
end
context "when creating hierarchical links to a sub-directory" do
let(:nesting) { './subdirectory/' }
it "rewrites non-file links to be at the scope of the sub-directory" do
markdown = "[Link to Page](./subdirectory/page)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
context "non file links" do
let(:extension) { '' }
let(:expected_page_path) { 'nested/twice/subdirectory/page' }
it_behaves_like 'a correct link rewrite'
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/nested/twice/subdirectory/page\"")
end
context 'file-like links' do
let(:extension) { '.md' }
let(:expected_page_path) { 'nested/twice/subdirectory/page.md' }
it_behaves_like 'a correct link rewrite'
it "rewrites file links to be at the scope of the sub-directory" do
markdown = "[Link to Page](./subdirectory/page.md)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/nested/twice/subdirectory/page.md\"")
end
end
describe "when creating non-hierarchical links" do
let(:nesting) { '' }
it 'rewrites non-file links to be at the scope of the wiki root' do
markdown = "[Link to Page](page)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
context 'non-file links' do
let(:extension) { '' }
let(:expected_page_path) { 'page' }
it_behaves_like 'a correct link rewrite'
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/page\"")
end
context 'non-file links (with spaces)' do
let(:extension) { ' slug' }
let(:expected_page_path) { 'page%20slug' }
it_behaves_like 'a correct link rewrite'
it 'rewrites non-file links (with spaces) to be at the scope of the wiki root' do
markdown = "[Link to Page](page slug)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/page%20slug\"")
end
context "file links" do
let(:extension) { '.md' }
let(:expected_page_path) { 'nested/twice/page.md' }
it_behaves_like 'a correct link rewrite'
it "rewrites file links to be at the scope of the current directory" do
markdown = "[Link to Page](page.md)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/nested/twice/page.md\"")
end
context 'links with anchor' do
let(:extension) { '#title' }
let(:expected_page_path) { 'page#title' }
it_behaves_like 'a correct link rewrite'
it 'rewrites links with anchor' do
markdown = '[Link to Header](start-page#title)'
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/start-page#title\"")
end
context 'links (with spaces) with anchor' do
let(:extension) { ' two#title' }
let(:expected_page_path) { 'page%20two#title' }
it_behaves_like 'a correct link rewrite'
it 'rewrites links (with spaces) with anchor' do
markdown = '[Link to Header](start page#title)'
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/start%20page#title\"")
end
end
describe "when creating root links" do
let(:nesting) { '/' }
it 'rewrites non-file links to be at the scope of the wiki root' do
markdown = "[Link to Page](/page)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
context 'non-file links' do
let(:extension) { '' }
let(:expected_page_path) { 'page' }
it_behaves_like 'a correct link rewrite'
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/page\"")
end
context 'file links' do
let(:extension) { '.md' }
let(:expected_page_path) { 'page.md' }
it_behaves_like 'a correct link rewrite'
it 'rewrites file links to be at the scope of the wiki root' do
markdown = "[Link to Page](/page.md)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/page.md\"")
end
end
end
describe "linking to pages outside the wiki (absolute)" do
let(:markdown) { "[Link to Page](http://example.com/page)" }
let(:page_href) { 'http://example.com/page' }
it_behaves_like 'a correct link rewrite'
it "doesn't rewrite links" do
markdown = "[Link to Page](http://example.com/page)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
expect(output).to include('href="http://example.com/page"')
end
end
end
end
include_examples 'link examples', :root do
let(:relative_url_root) { '/' }
end
include_examples 'link examples', :relative do
let(:relative_url_root) { '/nested/relative/gitlab' }
end
describe "checking slug validity when assembling links" do
context "with a valid slug" do
let(:valid_slug) { "http://example.com" }
......@@ -273,54 +261,37 @@ describe Banzai::Pipeline::WikiPipeline do
end
describe 'videos and audio' do
def src(file_name)
"#{prefix}/nested/twice/#{file_name}"
end
shared_examples 'correct video rewrite' do
let(:markdown) { "![video_file](#{file_name})" }
let(:video_fragment) { "<video src=\"#{prefix}/#{expected_file_path}\"" }
let(:options) do
{
project: project,
project_wiki: project_wiki,
page_slug: page.slug
}
end
let_it_be(:namespace) { create(:namespace, name: "wiki_link_ns") }
let_it_be(:project) { create(:project, :public, name: "wiki_link_project", namespace: namespace) }
let_it_be(:project_wiki) { ProjectWiki.new(project, double(:user)) }
let_it_be(:page) { build(:wiki_page, wiki: project_wiki, page: OpenStruct.new(url_path: 'nested/twice/start-page')) }
it 'generates video html structure' do
output = described_class.to_html(markdown, options)
it 'generates video html structure' do
markdown = "![video_file](video_file_name.mp4)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
expect(output).to include(video_fragment)
end
expect(output).to include('<video src="/wiki_link_ns/wiki_link_project/wikis/nested/twice/video_file_name.mp4"')
end
context 'underscores' do
let(:file_name) { 'video_file_name.mp4' }
let(:expected_file_path) { 'nested/twice/video_file_name.mp4' }
it_behaves_like 'correct video rewrite'
end
it 'rewrites and replaces video links names with white spaces to %20' do
markdown = "![video file](video file name.mp4)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
context 'spaces' do
let(:file_name) { 'video file name.mp4' }
let(:expected_file_path) { 'nested/twice/video%20file%20name.mp4' }
it_behaves_like 'correct video rewrite'
expect(output).to include('<video src="/wiki_link_ns/wiki_link_project/wikis/nested/twice/video%20file%20name.mp4"')
end
it 'generates audio html structure' do
markdown = "![audio_file](audio_file_name.wav)"
safe_name = "audio_file_name.wav"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
expect(output).to include(%Q'<audio src="#{src(safe_name)}"')
expect(output).to include('<audio src="/wiki_link_ns/wiki_link_project/wikis/nested/twice/audio_file_name.wav"')
end
it 'rewrites and replaces audio links names with white spaces to %20' do
markdown = "![audio file](audio file name.wav)"
safe_name = "audio%20file%20name.wav"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
expect(output).to include(%Q'<audio src="#{src(safe_name)}"')
expect(output).to include('<audio src="/wiki_link_ns/wiki_link_project/wikis/nested/twice/audio%20file%20name.wav"')
end
end
end
......@@ -9,6 +9,7 @@ describe Gitlab::Tracking do
stub_application_setting(snowplow_collector_hostname: 'gitfoo.com')
stub_application_setting(snowplow_cookie_domain: '.gitfoo.com')
stub_application_setting(snowplow_site_id: '_abc123_')
stub_application_setting(snowplow_iglu_registry_url: 'https://example.org')
end
describe '.snowplow_options' do
......@@ -19,7 +20,8 @@ describe Gitlab::Tracking do
cookieDomain: '.gitfoo.com',
appId: '_abc123_',
formTracking: true,
linkClickTracking: true
linkClickTracking: true,
igluRegistryUrl: 'https://example.org'
}
expect(subject.snowplow_options(nil)).to match(expected_fields)
......
......@@ -150,7 +150,7 @@ describe Gitlab::UrlBuilder do
wiki_page = build(:wiki_page)
url = described_class.build(wiki_page)
expect(url).to eq "#{Gitlab.config.gitlab.url}#{wiki_page.wiki.wiki_page_path}/#{wiki_page.slug}"
expect(url).to eq "#{Gitlab.config.gitlab.url}#{wiki_page.wiki.wiki_base_path}/#{wiki_page.slug}"
end
end
end
......
......@@ -19,7 +19,11 @@ describe Gitlab::UsageData do
create(:service, project: projects[2], type: 'SlackService', active: true)
create(:project_error_tracking_setting, project: projects[0])
create(:project_error_tracking_setting, project: projects[1], enabled: false)
create_list(:issue, 4, project: projects[0])
create(:zoom_meeting, project: projects[0], issue: projects[0].issues[0], issue_status: :added)
create_list(:zoom_meeting, 2, project: projects[0], issue: projects[0].issues[1], issue_status: :removed)
create(:zoom_meeting, project: projects[0], issue: projects[0].issues[2], issue_status: :added)
create_list(:zoom_meeting, 2, project: projects[0], issue: projects[0].issues[2], issue_status: :removed)
gcp_cluster = create(:cluster, :provided_by_gcp)
create(:cluster, :provided_by_user)
create(:cluster, :provided_by_user, :disabled)
......@@ -125,6 +129,8 @@ describe Gitlab::UsageData do
in_review_folder
groups
issues
issues_with_associated_zoom_link
issues_using_zoom_quick_actions
keys
label_lists
labels
......@@ -176,6 +182,8 @@ describe Gitlab::UsageData do
expect(count_data[:projects_slack_slash_active]).to eq(1)
expect(count_data[:projects_with_repositories_enabled]).to eq(3)
expect(count_data[:projects_with_error_tracking_enabled]).to eq(1)
expect(count_data[:issues_with_associated_zoom_link]).to eq(2)
expect(count_data[:issues_using_zoom_quick_actions]).to eq(3)
expect(count_data[:clusters_enabled]).to eq(7)
expect(count_data[:project_clusters_enabled]).to eq(6)
......
......@@ -252,41 +252,4 @@ describe Gitlab::Utils do
expect(described_class.string_to_ip_object('1:0:0:0:0:0:0:0/124')).to eq(IPAddr.new('1:0:0:0:0:0:0:0/124'))
end
end
describe '.allow_hash_values' do
it 'removes keys that do not pass the inclusion filters' do
symbols = %i[x y z]
ints = (0..100)
strings = %w[foo bar baz].to_set
hash = {
a: :x,
b: 100,
c: 'foo',
d: :irrelevant,
aa: :w,
bb: 200,
cc: 'food',
dd: :totally_irrelevant
}
allowed = {
a: symbols,
b: ints,
c: strings,
aa: symbols,
bb: ints,
cc: strings
}
described_class.allow_hash_values(hash, allowed)
expect(hash).to eq({
a: :x,
b: 100,
c: 'foo',
d: :irrelevant,
dd: :totally_irrelevant
})
end
end
end
......@@ -72,10 +72,14 @@ describe ApplicationSetting do
it { is_expected.not_to allow_value(nil).for(:snowplow_collector_hostname) }
it { is_expected.to allow_value("snowplow.gitlab.com").for(:snowplow_collector_hostname) }
it { is_expected.not_to allow_value('/example').for(:snowplow_collector_hostname) }
it { is_expected.to allow_value('https://example.org').for(:snowplow_iglu_registry_url) }
it { is_expected.not_to allow_value('not-a-url').for(:snowplow_iglu_registry_url) }
it { is_expected.to allow_value(nil).for(:snowplow_iglu_registry_url) }
end
context 'when snowplow is not enabled' do
it { is_expected.to allow_value(nil).for(:snowplow_collector_hostname) }
it { is_expected.to allow_value(nil).for(:snowplow_iglu_registry_url) }
end
context 'when pendo is enabled' do
......
......@@ -515,6 +515,48 @@ describe Environment, :use_clean_rails_memory_store_caching do
end
end
describe '#last_visible_deployment' do
subject { environment.last_visible_deployment }
before do
allow_any_instance_of(Deployment).to receive(:create_ref)
end
context 'when there is an old deployment record' do
let!(:previous_deployment) { create(:deployment, :success, environment: environment) }
context 'when there is a deployment record with created status' do
let!(:deployment) { create(:deployment, environment: environment) }
it { is_expected.to eq(previous_deployment) }
end
context 'when there is a deployment record with running status' do
let!(:deployment) { create(:deployment, :running, environment: environment) }
it { is_expected.to eq(deployment) }
end
context 'when there is a deployment record with success status' do
let!(:deployment) { create(:deployment, :success, environment: environment) }
it { is_expected.to eq(deployment) }
end
context 'when there is a deployment record with failed status' do
let!(:deployment) { create(:deployment, :failed, environment: environment) }
it { is_expected.to eq(deployment) }
end
context 'when there is a deployment record with canceled status' do
let!(:deployment) { create(:deployment, :canceled, environment: environment) }
it { is_expected.to eq(deployment) }
end
end
end
describe '#has_terminals?' do
subject { environment.has_terminals? }
......
......@@ -28,9 +28,7 @@ describe ProjectWiki do
describe '#web_url' do
it 'returns the full web URL to the wiki' do
home_url = Gitlab::Routing.url_helpers.project_wiki_url(project, :home)
expect(subject.web_url).to eq(home_url)
expect(subject.web_url).to eq("#{Gitlab.config.gitlab.url}/#{project.full_path}/wikis/home")
end
end
......@@ -73,23 +71,9 @@ describe ProjectWiki do
describe "#wiki_base_path" do
it "returns the wiki base path" do
wiki_path = Gitlab::Routing.url_helpers.project_wikis_path(project)
expect(subject.wiki_base_path).to eq(wiki_path)
end
end
wiki_base_path = "#{Gitlab.config.gitlab.relative_url_root}/#{project.full_path}/wikis"
describe "#wiki_page_path" do
let(:page) { create(:wiki_page, wiki: project_wiki) }
describe 'suffixed with /:page_slug' do
subject { "#{project_wiki.wiki_page_path}/#{page.slug}" }
it "equals the project_wiki_path" do
path = Gitlab::Routing.url_helpers.project_wiki_path(project, page)
expect(subject).to eq(path)
end
expect(subject.wiki_base_path).to eq(wiki_base_path)
end
end
......
# frozen_string_literal: true
require 'spec_helper'
require 'set'
RSpec.describe WikiDirectory do
include GitHelpers
let(:project) { create(:project, :wiki_repo) }
let(:user) { project.owner }
let(:wiki) { ProjectWiki.new(project, user) }
describe 'validations' do
subject { build(:wiki_directory) }
it { is_expected.to validate_presence_of(:slug) }
end
describe '.group_by_directory' do
context 'when there are no pages' do
it 'returns an empty array' do
expect(described_class.group_by_directory(nil)).to eq([])
expect(described_class.group_by_directory([])).to eq([])
end
end
context 'when there are pages' do
before do
create_page('dir_1/dir_1_1/page_3', 'content')
create_page('page_1', 'content')
create_page('dir_1/page_2', 'content')
create_page('dir_2', 'page with dir name')
create_page('dir_2/page_5', 'content')
create_page('page_6', 'content')
create_page('dir_2/page_4', 'content')
end
let(:page_1) { wiki.find_page('page_1') }
let(:page_6) { wiki.find_page('page_6') }
let(:page_dir_2) { wiki.find_page('dir_2') }
let(:dir_1) do
described_class.new('dir_1', [wiki.find_page('dir_1/page_2')])
end
let(:dir_1_1) do
described_class.new('dir_1/dir_1_1', [wiki.find_page('dir_1/dir_1_1/page_3')])
end
let(:dir_2) do
pages = [wiki.find_page('dir_2/page_5'),
wiki.find_page('dir_2/page_4')]
described_class.new('dir_2', pages)
end
context "#list_pages" do
shared_examples "a correct grouping" do
let(:grouped_slugs) { grouped_entries.map(&method(:slugs)) }
let(:expected_slugs) { expected_grouped_entries.map(&method(:slugs)).map(&method(:match_array)) }
it 'returns an array with pages and directories' do
expect(grouped_slugs).to match_array(expected_slugs)
end
end
context 'sort by title' do
let(:grouped_entries) { described_class.group_by_directory(wiki.list_pages) }
let(:expected_grouped_entries) { [dir_1_1, dir_1, page_dir_2, dir_2, page_1, page_6] }
it_behaves_like "a correct grouping"
end
context 'sort by created_at' do
let(:grouped_entries) { described_class.group_by_directory(wiki.list_pages(sort: 'created_at')) }
let(:expected_grouped_entries) { [dir_1_1, page_1, dir_1, page_dir_2, dir_2, page_6] }
it_behaves_like "a correct grouping"
end
it 'returns an array with retained order with directories at the top' do
expected_order = ['dir_1/dir_1_1/page_3', 'dir_1/page_2', 'dir_2', 'dir_2/page_4', 'dir_2/page_5', 'page_1', 'page_6']
grouped_entries = described_class.group_by_directory(wiki.list_pages)
actual_order = grouped_entries.flat_map(&method(:slugs))
expect(actual_order).to eq(expected_order)
end
end
end
end
describe '#initialize' do
context 'when there are pages' do
let(:pages) { [build(:wiki_page)] }
......@@ -120,112 +40,7 @@ RSpec.describe WikiDirectory do
it 'returns the relative path to the partial to be used' do
directory = build(:wiki_directory)
expect(directory.to_partial_path).to eq('projects/wiki_directories/wiki_directory')
end
end
describe 'attributes' do
def page_path(index)
"dir-path/page-#{index}"
end
let(:page_paths) { (1..3).map { |n| page_path(n) } }
let(:pages) do
page_paths.map { |p| wiki.find_page(p) }
expect(directory.to_partial_path).to eq('projects/wikis/wiki_directory')
end
subject { described_class.new('dir-path', pages) }
context 'there are no pages' do
let(:pages) { [] }
it { is_expected.to have_attributes(page_count: 0, last_version: be_nil) }
end
context 'there is one page' do
before do
create_page("dir-path/singleton", "Just this page")
end
let(:the_page) { wiki.find_page("dir-path/singleton") }
let(:pages) { [the_page] }
it { is_expected.to have_attributes(page_count: 1, last_version: the_page.last_version) }
end
context 'there are a few pages, each with a single version' do
before do
page_paths.each_with_index do |path, n|
Timecop.freeze(Time.local(1990) + n.minutes) do
create_page(path, "this is page #{n}")
end
end
end
let(:expected_last_version) { pages.last.last_version }
it { is_expected.to have_attributes(page_count: 3, last_version: expected_last_version) }
end
context 'there are a few pages, each with a few versions' do
before do
page_paths.each_with_index do |path, n|
t = Time.local(1990) + n.minutes
Timecop.freeze(t) do
create_page(path, "This is page #{n}")
(2..3).each do |v|
Timecop.freeze(t + v.seconds) do
update_page(path, "Now at version #{v}")
end
end
end
end
end
it { is_expected.to have_attributes(page_count: 3, last_version: pages.last.last_version) }
end
end
private
def create_page(name, content)
wiki.wiki.write_page(name, :markdown, content, commit_details)
set_time(name)
end
def update_page(name, content)
wiki.wiki.update_page(name, name, :markdown, content, update_commit_details)
set_time(name)
end
def set_time(name)
return unless Timecop.frozen?
new_date = Time.now
page = wiki.find_page(name).page
commit = page.version.commit
repo = commit.instance_variable_get(:@repository)
rug_commit = rugged_repo_at_path(repo.relative_path).lookup(commit.id)
rug_commit.amend(
message: rug_commit.message,
tree: rug_commit.tree,
author: rug_commit.author.merge(time: new_date),
committer: rug_commit.committer.merge(time: new_date),
update_ref: 'HEAD'
)
end
def commit_details
Gitlab::Git::Wiki::CommitDetails.new(user.id, user.username, user.name, user.email, "test commit")
end
def update_commit_details
Gitlab::Git::Wiki::CommitDetails.new(user.id, user.username, user.name, user.email, "test update")
end
def slugs(thing)
Array.wrap(thing.respond_to?(:pages) ? thing.pages.map(&:slug) : thing.slug)
end
end
......@@ -9,6 +9,87 @@ describe WikiPage do
subject { described_class.new(wiki) }
describe '.group_by_directory' do
context 'when there are no pages' do
it 'returns an empty array' do
expect(described_class.group_by_directory(nil)).to eq([])
expect(described_class.group_by_directory([])).to eq([])
end
end
context 'when there are pages' do
before do
create_page('dir_1/dir_1_1/page_3', 'content')
create_page('page_1', 'content')
create_page('dir_1/page_2', 'content')
create_page('dir_2', 'page with dir name')
create_page('dir_2/page_5', 'content')
create_page('page_6', 'content')
create_page('dir_2/page_4', 'content')
end
let(:page_1) { wiki.find_page('page_1') }
let(:page_6) { wiki.find_page('page_6') }
let(:page_dir_2) { wiki.find_page('dir_2') }
let(:dir_1) do
WikiDirectory.new('dir_1', [wiki.find_page('dir_1/page_2')])
end
let(:dir_1_1) do
WikiDirectory.new('dir_1/dir_1_1', [wiki.find_page('dir_1/dir_1_1/page_3')])
end
let(:dir_2) do
pages = [wiki.find_page('dir_2/page_5'),
wiki.find_page('dir_2/page_4')]
WikiDirectory.new('dir_2', pages)
end
context "#list_pages" do
context 'sort by title' do
let(:grouped_entries) { described_class.group_by_directory(wiki.list_pages) }
let(:expected_grouped_entries) { [dir_1_1, dir_1, page_dir_2, dir_2, page_1, page_6] }
it 'returns an array with pages and directories' do
grouped_entries.each_with_index do |page_or_dir, i|
expected_page_or_dir = expected_grouped_entries[i]
expected_slugs = get_slugs(expected_page_or_dir)
slugs = get_slugs(page_or_dir)
expect(slugs).to match_array(expected_slugs)
end
end
end
context 'sort by created_at' do
let(:grouped_entries) { described_class.group_by_directory(wiki.list_pages(sort: 'created_at')) }
let(:expected_grouped_entries) { [dir_1_1, page_1, dir_1, page_dir_2, dir_2, page_6] }
it 'returns an array with pages and directories' do
grouped_entries.each_with_index do |page_or_dir, i|
expected_page_or_dir = expected_grouped_entries[i]
expected_slugs = get_slugs(expected_page_or_dir)
slugs = get_slugs(page_or_dir)
expect(slugs).to match_array(expected_slugs)
end
end
end
it 'returns an array with retained order with directories at the top' do
expected_order = ['dir_1/dir_1_1/page_3', 'dir_1/page_2', 'dir_2', 'dir_2/page_4', 'dir_2/page_5', 'page_1', 'page_6']
grouped_entries = described_class.group_by_directory(wiki.list_pages)
actual_order =
grouped_entries.flat_map do |page_or_dir|
get_slugs(page_or_dir)
end
expect(actual_order).to eq(expected_order)
end
end
end
end
describe '.unhyphenize' do
it 'removes hyphens from a name' do
name = 'a-name--with-hyphens'
......@@ -441,7 +522,7 @@ describe WikiPage do
it 'returns the relative path to the partial to be used' do
page = build(:wiki_page)
expect(page.to_partial_path).to eq('projects/wiki_pages/wiki_page')
expect(page.to_partial_path).to eq('projects/wikis/wiki_page')
end
end
......@@ -521,4 +602,12 @@ describe WikiPage do
page = wiki.wiki.page(title: title, dir: dir)
wiki.delete_page(page, "test commit")
end
def get_slugs(page_or_dir)
if page_or_dir.is_a? WikiPage
[page_or_dir.slug]
else
page_or_dir.pages.present? ? page_or_dir.pages.map(&:slug) : []
end
end
end
......@@ -178,7 +178,8 @@ describe API::Settings, 'Settings' do
snowplow_collector_hostname: "snowplow.example.com",
snowplow_cookie_domain: ".example.com",
snowplow_enabled: true,
snowplow_site_id: "site_id"
snowplow_site_id: "site_id",
snowplow_iglu_registry_url: 'https://example.com'
}
end
......
......@@ -5,12 +5,9 @@ require 'spec_helper'
describe 'project routing' do
before do
allow(Project).to receive(:find_by_full_path).and_return(false)
allow(Project).to receive(:find_by_full_path).with('gitlab/gitlabhq', any_args).and_return(project)
allow(Project).to receive(:find_by_full_path).with('gitlab/gitlabhq', any_args).and_return(true)
end
set(:namespace) { create(:namespace, name: 'gitlab') }
set(:project) { create(:project, namespace: namespace, name: 'gitlabhq') }
# Shared examples for a resource inside a Project
#
# By default it tests all the default REST actions: index, create, new, edit,
......@@ -150,39 +147,24 @@ describe 'project routing' do
it_behaves_like 'redirecting a legacy project path', "/gitlab/gitlabhq/autocomplete_sources/labels", "/gitlab/gitlabhq/-/autocomplete_sources/labels"
end
# GET /:project_id/wikis/pages(.:format) projects/wikis#pages
# GET /:project_id/-/wiki_pages/:id/history(.:format) projects/wiki_pages#history
# POST /:project_id/-/wiki_pages(.:format) projects/wiki_pages#create
# GET /:project_id/-/wiki_pages/:id/edit(.:format) projects/wiki_pages#edit
# GET /:project_id/-/wiki_pages/:id(.:format) projects/wiki_pages#show
# DELETE /:project_id/-/wiki_pages/:id(.:format) projects/wiki_pages#destroy
# pages_project_wikis GET /:project_id/wikis/pages(.:format) projects/wikis#pages
# history_project_wiki GET /:project_id/wikis/:id/history(.:format) projects/wikis#history
# project_wikis POST /:project_id/wikis(.:format) projects/wikis#create
# edit_project_wiki GET /:project_id/wikis/:id/edit(.:format) projects/wikis#edit
# project_wiki GET /:project_id/wikis/:id(.:format) projects/wikis#show
# DELETE /:project_id/wikis/:id(.:format) projects/wikis#destroy
describe Projects::WikisController, 'routing' do
let(:wiki) { ProjectWiki.new(project, project.owner) }
let(:wiki_page) { create(:wiki_page, wiki: wiki) }
it '#pages' do
expect(get('/gitlab/gitlabhq/wikis/pages'))
.to route_to('projects/wikis#pages',
namespace_id: 'gitlab',
project_id: 'gitlabhq')
it 'to #pages' do
expect(get('/gitlab/gitlabhq/wikis/pages')).to route_to('projects/wikis#pages', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
describe '#history' do
let(:history_path) { project_wiki_history_path(project, wiki_page) }
it 'routes to history' do
expect(get(history_path))
.to route_to('projects/wiki_pages#history',
namespace_id: namespace.path,
project_id: project.name,
id: wiki_page.slug)
end
it 'to #history' do
expect(get('/gitlab/gitlabhq/wikis/1/history')).to route_to('projects/wikis#history', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
end
it_behaves_like 'RESTful project resources' do
let(:actions) { [:create, :edit, :show, :destroy] }
let(:controller) { 'wiki_pages' }
let(:controller_path) { '-/wiki_pages' }
let(:controller) { 'wikis' }
end
end
......
# frozen_string_literal: true
require 'spec_helper'
# We build URIs to wiki pages manually in various places (most notably
# in markdown generation). To ensure these do not get out of sync, these
# tests verify that our path generation assumptions are sound.
describe 'Wiki path generation assumptions' do
set(:project) { create(:project, :public, :repository) }
let(:project_wiki) { ProjectWiki.new(project, project.owner) }
let(:some_page_name) { 'some-wiki-page' }
let(:wiki_page) do
create(:wiki_page, wiki: project_wiki, attrs: { title: some_page_name })
end
describe 'WikiProject#wiki_page_path', 'routing' do
it 'is consistent with routing to wiki#show' do
uri = URI.parse(project_wiki.wiki_page_path)
path = ::File.join(uri.path, some_page_name)
expect(get('/' + path)).to route_to('projects/wiki_pages#show',
id: some_page_name,
namespace_id: project.namespace.to_param,
project_id: project.to_param)
end
end
describe 'project_wiki_path', 'routing' do
describe 'GET' do
it 'routes to the :show action' do
path = project_wiki_path(project, wiki_page)
expect(get('/' + path)).to route_to('projects/wiki_pages#show',
id: wiki_page.slug,
namespace_id: project.namespace.to_param,
project_id: project.to_param)
end
end
end
describe 'project_wiki_pages_new_path', 'routing' do
describe 'GET' do
it 'routes to the :new action' do
path = project_wiki_pages_new_path(project)
expect(get('/' + path)).to route_to('projects/wiki_pages#new',
namespace_id: project.namespace.to_param,
project_id: project.to_param)
end
end
end
# Early versions of the wiki paths routed all wiki pages at
# /wikis/:id - this test exists to guarantee that we support
# old URIs that may be out there, saved in bookmarks, on other wikis, etc.
describe 'legacy route support', type: 'request' do
let(:path) { ::File.join(project_wikis_path(project), some_page_name) }
before do
get(path)
end
it 'routes to new wiki paths' do
dest = project_wiki_path(project, wiki_page)
expect(response).to redirect_to(dest)
end
context 'the page is nested in a directory' do
let(:some_page_name) { 'some-dir/some-deep-dir/some-page' }
let(:path) { ::File.join(project_wikis_path(project), some_page_name) }
it 'still routes correctly' do
dest = project_wiki_path(project, wiki_page)
expect(response).to redirect_to(dest)
end
end
context 'the user requested the old history path' do
let(:some_page_name) { 'some-dir/some-deep-dir/some-page' }
let(:path) { ::File.join(project_wikis_path(project), some_page_name, 'history') }
it 'redirects to the new history path' do
dest = project_wiki_history_path(project, wiki_page)
expect(response).to redirect_to(dest)
end
end
context 'the user requested the old edit path' do
let(:some_page_name) { 'some-dir/some-deep-dir/some-page' }
let(:path) { ::File.join(project_wikis_path(project), some_page_name, 'edit') }
it 'redirects to the new history path' do
dest = project_wiki_edit_path(project, wiki_page)
expect(response).to redirect_to(dest)
end
end
end
end
# frozen_string_literal: true
def forbid_controller_ability!(ability)
allow(controller).to receive(:can?).and_call_original
allow(controller).to receive(:can?).with(anything, ability, any_args).and_return(false)
end
......@@ -46,14 +46,4 @@ module CapybaraHelpers
def javascript_test?
Capybara.current_driver == Capybara.javascript_driver
end
def scroll_to(element)
raise 'JS not available' unless javascript_test?
script = <<-JS
arguments[0].scrollIntoView(true);
JS
page.driver.browser.execute_script(script, element.native)
end
end
......@@ -14,8 +14,6 @@ module DropzoneHelper
# If it's 'false', then the helper will NOT wait for backend response
# It lets to test behaviors while AJAX is processing.
def dropzone_file(files, max_file_size = 0, wait_for_queuecomplete = true)
# Assert that there is a dropzone to use (waiting until it is ready)
expect(page).to have_css('.div-dropzone')
# Generate a fake file input that Capybara can attach to
page.execute_script <<-JS.strip_heredoc
$('#fakeFileInput').remove();
......
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册