Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
dffeff5520
commit
8c7eab92cd
|
|
@ -343,8 +343,6 @@ 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'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
// currently, this controller inherits all behaviors from wikis
|
||||
import '../wikis/index';
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
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,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -1,41 +1,11 @@
|
|||
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_title');
|
||||
this.commitMessageInput = document.querySelector('form.wiki-form #wiki_message');
|
||||
this.editTitleInput = document.querySelector('form.wiki-form #wiki_page_title');
|
||||
this.commitMessageInput = document.querySelector('form.wiki-form #wiki_page_message');
|
||||
this.commitMessageI18n = this.isNewWikiPage
|
||||
? s__('WikiPageCreate|Create %{pageTitle}')
|
||||
: s__('WikiPageEdit|Update %{pageTitle}');
|
||||
|
|
|
|||
|
|
@ -99,3 +99,18 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,31 @@
|
|||
.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;
|
||||
|
|
@ -5,6 +33,15 @@
|
|||
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;
|
||||
|
||||
|
|
@ -79,7 +116,7 @@
|
|||
}
|
||||
|
||||
.sidebar-container {
|
||||
padding: $gl-padding 0;
|
||||
padding-bottom: $gl-padding;
|
||||
width: calc(100% + 100px);
|
||||
padding-right: 100px;
|
||||
height: 100%;
|
||||
|
|
@ -125,7 +162,7 @@
|
|||
}
|
||||
|
||||
.wiki-sidebar-header {
|
||||
padding: 0 $gl-padding $gl-padding;
|
||||
padding: $gl-padding;
|
||||
|
||||
.gutter-toggle {
|
||||
margin-top: 0;
|
||||
|
|
|
|||
|
|
@ -9,11 +9,10 @@ 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 {}
|
||||
else preview_markdown_params
|
||||
end
|
||||
|
||||
render json: {
|
||||
|
|
@ -25,6 +24,7 @@ module PreviewMarkdown
|
|||
}
|
||||
}
|
||||
end
|
||||
# rubocop:enable Gitlab/ModuleWithInstanceVariables
|
||||
|
||||
def projects_filter_params
|
||||
{
|
||||
|
|
@ -32,5 +32,11 @@ module PreviewMarkdown
|
|||
suggestions_filter_enabled: params[:preview_suggestions].present?
|
||||
}
|
||||
end
|
||||
# rubocop:enable Gitlab/ModuleWithInstanceVariables
|
||||
|
||||
private
|
||||
|
||||
# Override this method to customise the markdown for your controller
|
||||
def preview_markdown_params
|
||||
{}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
# 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
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
# 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
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
# 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
|
||||
|
|
@ -1,120 +1,28 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Projects::WikisController < Projects::ApplicationController
|
||||
include PreviewMarkdown
|
||||
include SendsBlob
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
include ProjectWikiActions
|
||||
include WikiHelper
|
||||
|
||||
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)
|
||||
def self.local_prefixes
|
||||
[controller_path, 'shared/wiki']
|
||||
end
|
||||
|
||||
def pages
|
||||
@nesting = show_children_param
|
||||
@show_children = @nesting != ProjectWiki::NESTING_CLOSED
|
||||
@wiki_pages = Kaminari.paginate_array(
|
||||
@project_wiki.list_pages(sort: params[:sort], direction: params[:direction])
|
||||
project_wiki.list_pages(**sort_params)
|
||||
).page(params[:page])
|
||||
|
||||
@wiki_entries = WikiPage.group_by_directory(@wiki_pages)
|
||||
end
|
||||
@wiki_entries = case @nesting
|
||||
when ProjectWiki::NESTING_FLAT
|
||||
@wiki_pages
|
||||
else
|
||||
WikiDirectory.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)
|
||||
|
||||
redirect_to project_wiki_path(@project, :home),
|
||||
status: 302,
|
||||
notice: _("Page was successfully deleted")
|
||||
rescue Gitlab::Git::Wiki::OperationError => e
|
||||
@error = e
|
||||
render 'edit'
|
||||
render 'show'
|
||||
end
|
||||
|
||||
def git_access
|
||||
|
|
@ -122,74 +30,13 @@ class Projects::WikisController < Projects::ApplicationController
|
|||
|
||||
private
|
||||
|
||||
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')
|
||||
def sort_params
|
||||
process_params(sort_params_config)
|
||||
end
|
||||
|
||||
def load_project_wiki
|
||||
@project_wiki = load_wiki
|
||||
def show_children_param
|
||||
config = nesting_params_config(params[:sort])
|
||||
|
||||
# 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
|
||||
|
||||
@project_wiki.repository.blob_at(commit.id, params[:id])
|
||||
end
|
||||
process_params(config)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -41,6 +41,12 @@ 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,14 +48,23 @@ module WikiHelper
|
|||
expose_url(api_v4_projects_wikis_attachments_path(id: @project.id))
|
||||
end
|
||||
|
||||
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'
|
||||
WIKI_SORT_CSS_CLASSES = 'btn btn-default has-tooltip reverse-sort-btn qa-reverse-sort rspec-reverse-sort'
|
||||
|
||||
link_to(project_wikis_pages_path(project, sort: sort, direction: reversed_direction),
|
||||
type: 'button', class: link_class, title: _('Sort direction')) do
|
||||
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
|
||||
sprite_icon("sort-#{icon_class}", size: 16)
|
||||
end
|
||||
end
|
||||
|
|
@ -67,4 +76,86 @@ 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
|
||||
|
|
|
|||
|
|
@ -17,6 +17,13 @@ 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.
|
||||
|
|
@ -58,7 +65,11 @@ class ProjectWiki
|
|||
end
|
||||
|
||||
def wiki_base_path
|
||||
[Gitlab.config.gitlab.relative_url_root, '/', @project.full_path, '/wikis'].join('')
|
||||
::File.join(project_base_path, 'wikis')
|
||||
end
|
||||
|
||||
def wiki_page_path
|
||||
::File.join(project_base_path, '-', 'wiki_pages')
|
||||
end
|
||||
|
||||
# Returns the Gitlab::Git::Wiki object.
|
||||
|
|
@ -125,6 +136,23 @@ 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
|
||||
|
|
@ -144,6 +172,12 @@ 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)
|
||||
|
||||
|
|
@ -171,7 +205,7 @@ class ProjectWiki
|
|||
|
||||
title_array = title.split("/")
|
||||
title = title_array.pop
|
||||
[title, title_array.join("/")]
|
||||
[title, ::File.join(title_array)]
|
||||
end
|
||||
|
||||
def repository
|
||||
|
|
@ -198,6 +232,10 @@ 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)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,20 +1,75 @@
|
|||
# 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
|
||||
|
||||
# Relative path to the partial to be used when rendering collections
|
||||
# of this object.
|
||||
def to_partial_path
|
||||
'projects/wikis/wiki_directory'
|
||||
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}"
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -15,30 +15,7 @@ class WikiPage
|
|||
end
|
||||
|
||||
def self.model_name
|
||||
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
|
||||
ActiveModel::Name.new(self, nil, 'wiki_page')
|
||||
end
|
||||
|
||||
def self.unhyphenize(name)
|
||||
|
|
@ -66,6 +43,16 @@ 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
|
||||
|
|
@ -250,10 +237,10 @@ class WikiPage
|
|||
end
|
||||
end
|
||||
|
||||
# Relative path to the partial to be used when rendering collections
|
||||
# of this object.
|
||||
def to_partial_path
|
||||
'projects/wikis/wiki_page'
|
||||
def to_partial_path(context = nil)
|
||||
name = [context, 'wiki_page'].compact.join('_')
|
||||
|
||||
"projects/wiki_pages/#{name}"
|
||||
end
|
||||
|
||||
def id
|
||||
|
|
|
|||
|
|
@ -282,14 +282,14 @@
|
|||
|
||||
- if project_nav_tab? :wiki
|
||||
- wiki_url = project_wiki_path(@project, :home)
|
||||
= nav_link(controller: :wikis) do
|
||||
= nav_link(controller: [:wikis, :wiki_pages, :wiki_directories]) 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, html_options: { class: "fly-out-top-item" } ) do
|
||||
= nav_link(controller: [:wikis, :wiki_pages, :wiki_directories], html_options: { class: "fly-out-top-item" } ) do
|
||||
= link_to wiki_url do
|
||||
%strong.fly-out-top-item-name
|
||||
= _('Wiki')
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
%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
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
%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
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
= render wiki_directory.to_partial_path(context), wiki_dir: wiki_directory, context: context
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
- 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.')
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
- 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) } }
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
- 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,
|
||||
html: { class: form_classes },
|
||||
data: { uploads_path: uploads_path } do |f|
|
||||
= 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_errors(@page)
|
||||
|
||||
- if @page.persisted?
|
||||
|
|
@ -12,7 +14,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: _('Wiki|Page title')
|
||||
= f.text_field :title, class: 'form-control qa-wiki-title-textbox', value: @page.title, required: true, autofocus: !@page.persisted?, placeholder: s_('Wiki|Page title')
|
||||
%span.d-inline-block.mw-100.prepend-top-5
|
||||
= icon('lightbulb-o')
|
||||
- if @page.persisted?
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
= link_to @page.human_title, project_wiki_path(@project, @page)
|
||||
%span.light
|
||||
= _('·').html_safe
|
||||
= subtitle
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
%li
|
||||
%span.text-secondary-500.svg-icon.svg-baseline= sprite_icon('book', size: 16)
|
||||
= wiki_page_link(wiki_page, @nesting, @project)
|
||||
.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
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
- 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
|
||||
|
|
@ -0,0 +1 @@
|
|||
= render wiki_page.to_partial_path(context), wiki_page: wiki_page
|
||||
|
|
@ -1,5 +1,10 @@
|
|||
- @content_class = "limit-container-width" unless fluid_layout
|
||||
- add_to_breadcrumbs _("Wiki"), project_wiki_path(@project, @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)
|
||||
- breadcrumb_title @page.persisted? ? _("Edit") : _("New")
|
||||
- page_title @page.persisted? ? _("Edit") : _("New"), @page.human_title, _("Wiki")
|
||||
|
||||
|
|
@ -12,10 +17,7 @@
|
|||
.nav-text
|
||||
%h2.wiki-page-title
|
||||
- if @page.persisted?
|
||||
= link_to @page.human_title, project_wiki_path(@project, @page)
|
||||
%span.light
|
||||
·
|
||||
= s_("Wiki|Edit Page")
|
||||
= render partial: 'page_title', locals: { subtitle: s_("Wiki|Edit Page") }
|
||||
- else
|
||||
= s_("Wiki|Create New Page")
|
||||
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
- @content_class = 'wiki-history'
|
||||
- page_title _("History"), @page.human_title, _("Wiki")
|
||||
|
||||
.wiki-page-header.top-area.has-sidebar-toggle.flex-column.flex-lg-row
|
||||
|
|
@ -6,10 +7,7 @@
|
|||
|
||||
.nav-text
|
||||
%h2.wiki-page-title
|
||||
= link_to @page.human_title, project_wiki_path(@project, @page)
|
||||
%span.light
|
||||
·
|
||||
= _("History")
|
||||
= render partial: 'page_title', locals: { subtitle: _("History") }
|
||||
|
||||
.table-holder
|
||||
%table.table
|
||||
|
|
@ -39,4 +37,4 @@
|
|||
= version.format
|
||||
= paginate @page_versions, theme: 'gitlab'
|
||||
|
||||
= render 'sidebar'
|
||||
= render 'shared/wiki/sidebar'
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
- @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'
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
%li
|
||||
= link_to wiki_page.title, project_wiki_path(@project, wiki_page)
|
||||
%small (#{wiki_page.format})
|
||||
.float-right
|
||||
- if wiki_page.last_version
|
||||
%small= (s_("Last edited %{date}") % { date: time_ago_with_tooltip(wiki_page.last_version.authored_date) }).html_safe
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
%li{ class: active_when(params[:id] == wiki_page.slug) }
|
||||
= link_to project_wiki_path(@project, wiki_page) do
|
||||
= wiki_page.human_title
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
%li
|
||||
= wiki_directory.slug
|
||||
%ul
|
||||
= render wiki_directory.pages, context: context
|
||||
|
|
@ -1 +0,0 @@
|
|||
= render "#{context}_wiki_page", wiki_page: wiki_page
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
- 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'
|
||||
|
|
@ -1,32 +1,5 @@
|
|||
- @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)
|
||||
- add_to_breadcrumbs "Wiki", project_wiki_path(@project, :home)
|
||||
- breadcrumb_title s_("Wiki|Pages")
|
||||
- page_title s_("Wiki|Contents"), _("Wiki")
|
||||
|
||||
.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'
|
||||
= 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) } }
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
- 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 s_('WikiEmpty|Create your first page'), create_path, class: 'btn btn-success qa-create-first-page-link', title: s_('WikiEmpty|Create your first page')
|
||||
- create_link = link_to create_msg, create_path, class: 'btn btn-success qa-create-first-page-link', title: create_msg
|
||||
|
||||
= render layout: layout_path, locals: { image_path: 'illustrations/wiki_login_empty.svg' } do
|
||||
%h4.text-left
|
||||
|
|
@ -17,7 +20,7 @@
|
|||
|
||||
= render layout: layout_path, locals: { image_path: 'illustrations/wiki_logout_empty.svg' } do
|
||||
%h4
|
||||
= s_('WikiEmpty|This project has no wiki pages')
|
||||
= empty_msg
|
||||
%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
|
||||
|
|
@ -25,6 +28,6 @@
|
|||
- else
|
||||
= render layout: layout_path, locals: { image_path: 'illustrations/wiki_logout_empty.svg' } do
|
||||
%h4
|
||||
= s_('WikiEmpty|This project has no wiki pages')
|
||||
= empty_msg
|
||||
%p
|
||||
= s_('WikiEmpty|You must be a project member in order to add wiki pages.')
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
- if (@page && @page.persisted?)
|
||||
- if can?(current_user, :create_wiki, @project)
|
||||
= link_to project_wikis_new_path(@project), class: "add-new-wiki btn btn-success", role: "button" do
|
||||
= link_to project_wiki_pages_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")
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
- @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'
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Sort wiki pages by date
|
||||
merge_request: 30245
|
||||
type: added
|
||||
|
|
@ -615,7 +615,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
|
|||
end
|
||||
|
||||
# Since both wiki and repository routing contains wildcard characters
|
||||
# its preferable to keep it below all other project routes
|
||||
# its preferable to keep them below all other project routes
|
||||
draw :wiki
|
||||
draw :repository
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,21 @@
|
|||
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 :new
|
||||
get '/', to: redirect('%{namespace_id}/%{project_id}/wikis/home')
|
||||
post '/', to: 'wikis#create'
|
||||
get '/', to: redirect('%{namespace_id}/%{project_id}/-/wiki_pages/home')
|
||||
get '/*id', to: redirect('%{namespace_id}/%{project_id}/-/wiki_pages/%{id}')
|
||||
end
|
||||
|
||||
scope(path: 'wikis/*id', as: :wiki, format: false) do
|
||||
scope(path: '-/wiki_pages', as: :wiki_page, format: false) do
|
||||
post '/', to: 'wiki_pages#create'
|
||||
end
|
||||
|
||||
scope(path: '-/wiki_pages/*id', as: :wiki, format: false, controller: :wiki_pages) do
|
||||
get :edit
|
||||
get :history
|
||||
post :preview_markdown
|
||||
|
|
@ -15,4 +23,8 @@ 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
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ gather feedback through [resolvable threads](discussions/index.md#resolvable-com
|
|||
|
||||
Read through the [GFM documentation](markdown.md) to learn how to apply
|
||||
the best of GitLab Flavored Markdown in your threads, comments,
|
||||
issues and merge requests descriptions, and everywhere else GMF is
|
||||
issues and merge requests descriptions, and everywhere else GFM is
|
||||
supported.
|
||||
|
||||
## Todos
|
||||
|
|
|
|||
|
|
@ -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_base_path
|
||||
@wiki_base_path = wiki && wiki.wiki_page_path
|
||||
@slug = slug
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -130,5 +130,15 @@ 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,6 +411,9 @@ msgstr ""
|
|||
msgid "%{verb} %{time_spent_value} spent time."
|
||||
msgstr ""
|
||||
|
||||
msgid "·"
|
||||
msgstr ""
|
||||
|
||||
msgid "'%{level}' is not a valid visibility level"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -18704,7 +18707,10 @@ msgstr ""
|
|||
msgid "Wiki pages"
|
||||
msgstr ""
|
||||
|
||||
msgid "Wiki was successfully updated."
|
||||
msgid "Wiki was successfully created"
|
||||
msgstr ""
|
||||
|
||||
msgid "Wiki was successfully updated"
|
||||
msgstr ""
|
||||
|
||||
msgid "WikiClone|Clone your wiki"
|
||||
|
|
@ -18722,6 +18728,18 @@ 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 ""
|
||||
|
||||
|
|
@ -18740,6 +18758,9 @@ 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 ""
|
||||
|
||||
|
|
@ -18749,6 +18770,9 @@ 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 ""
|
||||
|
||||
|
|
@ -18803,6 +18827,9 @@ msgstr ""
|
|||
msgid "WikiPage|Write your content or drag files here…"
|
||||
msgstr ""
|
||||
|
||||
msgid "Wiki|Contents"
|
||||
msgstr ""
|
||||
|
||||
msgid "Wiki|Create New Page"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -18815,6 +18842,9 @@ msgstr ""
|
|||
msgid "Wiki|Edit Page"
|
||||
msgstr ""
|
||||
|
||||
msgid "Wiki|Hide folder contents"
|
||||
msgstr ""
|
||||
|
||||
msgid "Wiki|More Pages"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -18833,10 +18863,13 @@ msgstr ""
|
|||
msgid "Wiki|Pages"
|
||||
msgstr ""
|
||||
|
||||
msgid "Wiki|Title"
|
||||
msgid "Wiki|Show files separately"
|
||||
msgstr ""
|
||||
|
||||
msgid "Wiki|Wiki Pages"
|
||||
msgid "Wiki|Show folder contents"
|
||||
msgstr ""
|
||||
|
||||
msgid "Wiki|Title"
|
||||
msgstr ""
|
||||
|
||||
msgid "Will deploy to"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ module QA
|
|||
module Project
|
||||
module Wiki
|
||||
class Edit < Page::Base
|
||||
view 'app/views/projects/wikis/_main_links.html.haml' do
|
||||
view 'app/views/shared/wiki/_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/wikis/_form.html.haml' do
|
||||
view 'app/views/projects/wiki_pages/_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/projects/wikis/pages.html.haml' do
|
||||
view 'app/views/shared/wiki/_page_listing.html.haml' do
|
||||
element :clone_repository_link, 'Clone repository' # rubocop:disable QA/ElementWithPattern
|
||||
end
|
||||
|
||||
view 'app/views/projects/wikis/show.html.haml' do
|
||||
view 'app/views/projects/wiki_pages/show.html.haml' do
|
||||
element :wiki_page_content
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ module QA
|
|||
resource.message = 'Update home'
|
||||
end
|
||||
|
||||
validate_content('My First Wiki Content')
|
||||
validate_created('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_content('My Second Wiki Content')
|
||||
validate_edited('My Second Wiki Content')
|
||||
|
||||
Resource::Repository::WikiPush.fabricate! do |push|
|
||||
push.wiki = wiki
|
||||
|
|
@ -34,7 +34,12 @@ module QA
|
|||
expect(page).to have_content('My Third Wiki Content')
|
||||
end
|
||||
|
||||
def validate_content(content)
|
||||
def validate_created(content)
|
||||
expect(page).to have_content('Wiki was successfully created')
|
||||
expect(page).to have_content(/#{content}/)
|
||||
end
|
||||
|
||||
def validate_edited(content)
|
||||
expect(page).to have_content('Wiki was successfully updated')
|
||||
expect(page).to have_content(/#{content}/)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
# 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
|
||||
|
|
@ -0,0 +1,399 @@
|
|||
# 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(:user) { project.owner }
|
||||
let(:project_wiki) { ProjectWiki.new(project, user) }
|
||||
let(:wiki) { project_wiki.wiki }
|
||||
let(:wiki_title) { 'page title test' }
|
||||
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' }
|
||||
|
||||
before do
|
||||
create_page(wiki_title, 'hello world')
|
||||
|
|
@ -19,231 +19,86 @@ describe Projects::WikisController do
|
|||
destroy_page(wiki_title)
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
describe 'GET #pages' do
|
||||
subject { get :pages, params: { namespace_id: project.namespace, project_id: project, id: wiki_title } }
|
||||
subject do
|
||||
get :pages, params: { namespace_id: project.namespace, project_id: project, id: wiki_title }.merge(extra_params)
|
||||
end
|
||||
|
||||
let(:extra_params) { {} }
|
||||
|
||||
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 '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 }
|
||||
describe 'illegal params' do
|
||||
shared_examples :a_bad_request do
|
||||
it do
|
||||
expect { subject }.to raise_error(ActionController::BadRequest)
|
||||
end
|
||||
end
|
||||
|
||||
it "returns status #{expected_status}" do
|
||||
expect(response).to have_http_status(expected_status)
|
||||
describe ':sort' do
|
||||
let(:extra_params) { { sort: 'wibble' } }
|
||||
|
||||
it_behaves_like :a_bad_request
|
||||
end
|
||||
|
||||
describe ':direction' do
|
||||
let(:extra_params) { { direction: 'wibble' } }
|
||||
|
||||
it_behaves_like :a_bad_request
|
||||
end
|
||||
|
||||
describe ':show_children' do
|
||||
let(:extra_params) { { show_children: 'wibble' } }
|
||||
|
||||
it_behaves_like :a_bad_request
|
||||
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
|
||||
|
||||
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)
|
||||
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) { {} }
|
||||
|
||||
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' }
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
context 'when file is a svg' do
|
||||
let(:file_name) { 'unsanitized.svg' }
|
||||
it "sets nesting to #{default_nesting} by default" do
|
||||
expect(assigns :nesting).to eq default_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"
|
||||
it 'hides children if the default requires it' do
|
||||
expect(assigns :show_children).to be(default_nesting != ProjectWiki::NESTING_CLOSED)
|
||||
end
|
||||
|
||||
ProjectWiki::NESTINGS.each do |nesting|
|
||||
context "the user explicitly passes show_children = #{nesting}" do
|
||||
let(:nesting_params) { { show_children: nesting } }
|
||||
|
||||
it 'sets nesting to the provided value' do
|
||||
expect(assigns :nesting).to eq nesting
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'the user wants children hidden' do
|
||||
let(:nesting_params) { { show_children: 'hidden' } }
|
||||
|
||||
it 'hides children' do
|
||||
expect(assigns :show_children).to be false
|
||||
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
|
||||
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
|
||||
|
||||
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
|
||||
include_examples 'sorting-and-nesting', ProjectWiki::CREATED_AT_ORDER, ProjectWiki::NESTING_FLAT
|
||||
include_examples 'sorting-and-nesting', ProjectWiki::TITLE_ORDER, ProjectWiki::NESTING_CLOSED
|
||||
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 |page, evaluator|
|
||||
page.attributes = evaluator.attrs
|
||||
before(:create) do |wiki_page, evaluator|
|
||||
wiki_page.attributes = evaluator.attrs.with_indifferent_access
|
||||
end
|
||||
|
||||
to_create do |page|
|
||||
page.create
|
||||
to_create do |wiki_page|
|
||||
wiki_page.create
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@
|
|||
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) }
|
||||
|
||||
|
|
@ -81,85 +82,88 @@ describe 'Edit Project Settings' do
|
|||
end
|
||||
|
||||
describe 'project features visibility pages' do
|
||||
let(:pipeline) { create(:ci_empty_pipeline, project: project) }
|
||||
let(:job) { create(:ci_build, pipeline: pipeline) }
|
||||
set(:pipeline) { create(:ci_empty_pipeline, project: project) }
|
||||
set(: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)
|
||||
}
|
||||
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) }]
|
||||
]
|
||||
end
|
||||
|
||||
context 'normal user' do
|
||||
before do
|
||||
sign_in(member)
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
it 'renders 404 if feature is disabled' do
|
||||
project.project_feature.update_attribute(attr_name, ProjectFeature::DISABLED)
|
||||
|
||||
visit url
|
||||
|
||||
expect(page.status_code).to eq(404)
|
||||
end
|
||||
|
||||
it 'renders 404 if feature is enabled only for team members' do
|
||||
project.project_feature.update_attribute(attr_name, ProjectFeature::PRIVATE)
|
||||
|
||||
visit url
|
||||
|
||||
expect(page.status_code).to eq(404)
|
||||
end
|
||||
|
||||
it 'renders 200 if user is member of group' do
|
||||
group = create(:group)
|
||||
project.group = group
|
||||
project.save
|
||||
|
||||
group.add_owner(member)
|
||||
|
||||
project.project_feature.update_attribute(attr_name, ProjectFeature::PRIVATE)
|
||||
|
||||
visit url
|
||||
|
||||
expect(page.status_code).to eq(200)
|
||||
end
|
||||
end
|
||||
|
||||
it 'renders 404 if feature is disabled' do
|
||||
tools.each do |method_name, url|
|
||||
context 'admin user' do
|
||||
before do
|
||||
non_member.update_attribute(:admin, true)
|
||||
project.team.truncate
|
||||
sign_in(non_member)
|
||||
end
|
||||
|
||||
it 'renders 404 if feature is disabled' do
|
||||
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.team.truncate
|
||||
|
||||
tools.each do |method_name, url|
|
||||
it 'renders 200 if feature is enabled only for team members' do
|
||||
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)
|
||||
|
||||
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)
|
||||
sign_in(non_member)
|
||||
end
|
||||
|
||||
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.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,164 +4,54 @@ require 'spec_helper'
|
|||
|
||||
describe 'Projects > Wiki > User previews markdown changes', :js do
|
||||
set(:user) { create(: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
|
||||
set(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
|
||||
let(:project_wiki) { ProjectWiki.new(project, user) }
|
||||
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
|
||||
sign_in(user)
|
||||
init_home!
|
||||
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 init_home!
|
||||
create(:wiki_page, wiki: project.wiki, attrs: { title: 'home', content: '[some link](other-page)' })
|
||||
end
|
||||
|
||||
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', 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
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
def fill_in_content!
|
||||
page.within '.wiki-form' do
|
||||
fill_in :wiki_page_content, with: wiki_content
|
||||
end
|
||||
end
|
||||
|
||||
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"
|
||||
|
||||
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 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
|
||||
def show_preview!
|
||||
page.within '.wiki-form' do
|
||||
click_on 'Preview'
|
||||
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
|
||||
context 'when writing a new page' do
|
||||
let(:new_wiki_path) { 'a/b/c/d' }
|
||||
let(:wiki_content) { 'Some [awesome wiki](content)' }
|
||||
|
||||
create_wiki_page('linkify_test', 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("do_not_linkify")
|
||||
|
||||
expect(page.html).to include('[[do_not_linkify]]')
|
||||
expect(page.html).to include('[[also_do_not_linkify]]')
|
||||
expect(page).to have_link('awesome wiki')
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
context 'when editing an existing page' do
|
||||
let(:wiki_content) { 'Some [bemusing](content)' }
|
||||
let(:wiki_page) { create(:wiki_page, wiki: project_wiki) }
|
||||
|
||||
def create_wiki_page(path, content = 'content')
|
||||
visit project_wiki_path(project, wiki_page)
|
||||
it 'can show a preview of markdown content, when writing' do
|
||||
visit project_wiki_edit_path(project, wiki_page)
|
||||
fill_in_content!
|
||||
show_preview!
|
||||
|
||||
click_link 'New page'
|
||||
|
||||
fill_in :wiki_title, with: path
|
||||
fill_in :wiki_content, with: content
|
||||
|
||||
click_button 'Create page'
|
||||
expect(page).to have_link('bemusing')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,9 +3,15 @@
|
|||
require "spec_helper"
|
||||
|
||||
describe "User creates wiki page" do
|
||||
let(:user) { create(:user) }
|
||||
let(:wiki) { ProjectWiki.new(project, user) }
|
||||
include CapybaraHelpers
|
||||
include WikiHelpers
|
||||
|
||||
set(:user) { create(:user) }
|
||||
|
||||
let(:project) { create(:project) }
|
||||
let(:wiki) { ProjectWiki.new(project, user) }
|
||||
let(:new_page) { WikiPage.new(wiki) }
|
||||
let(:message_field) { form_field_name(new_page, :message) }
|
||||
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
|
|
@ -13,38 +19,78 @@ 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
|
||||
page.within(".wiki-form") do
|
||||
fill_in(:wiki_content, with: "")
|
||||
create_page
|
||||
|
||||
click_on("Create page")
|
||||
end
|
||||
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
|
||||
|
||||
expect(page).to have_content("The form contains the following error:").and have_content("Content can't be blank")
|
||||
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: "[link test](test)")
|
||||
|
||||
click_on("Create page")
|
||||
end
|
||||
|
||||
expect(page).to have_content("Home").and have_content("link test")
|
||||
expect(page)
|
||||
.to have_content("Home")
|
||||
.and have_content("link test")
|
||||
|
||||
click_link("link test")
|
||||
|
||||
expect(page).to have_content("Create New Page")
|
||||
end
|
||||
|
||||
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')
|
||||
|
||||
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(current_path).to include(wiki_full_path)
|
||||
|
||||
expect(page).to have_link(dir.slug, href: project_wiki_dir_path(project, dir))
|
||||
end
|
||||
|
||||
it "shows non-escaped link in the pages list", :quarantine do
|
||||
fill_in(:wiki_title, with: "one/two/three-test")
|
||||
|
||||
|
|
@ -58,19 +104,17 @@ describe "User creates wiki page" do
|
|||
expect(page).to have_xpath("//a[@href='/#{project.full_path}/wikis/one/two/three-test']")
|
||||
end
|
||||
|
||||
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_behaves_like 'updates commit message'
|
||||
|
||||
it "creates a page from the home page" do
|
||||
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")
|
||||
page_content = <<~WIKI_CONTENT
|
||||
[test](test)
|
||||
[GitLab API doc](api)
|
||||
[Rake tasks](raketasks)
|
||||
# Wiki header
|
||||
WIKI_CONTENT
|
||||
|
||||
page.within(".wiki-form") do
|
||||
click_button("Create page")
|
||||
end
|
||||
create_page(content: page_content, message: "Adding links to wiki")
|
||||
|
||||
expect(current_path).to eq(project_wiki_path(project, "home"))
|
||||
expect(page).to have_content("test GitLab API doc Rake tasks Wiki header")
|
||||
|
|
@ -111,7 +155,7 @@ describe "User creates wiki page" do
|
|||
end
|
||||
end
|
||||
|
||||
it "creates ASCII wiki with LaTeX blocks", :js do
|
||||
it "creates ASCIIdoc wiki with LaTeX blocks", :js do
|
||||
stub_application_setting(plantuml_url: "http://localhost", plantuml_enabled: true)
|
||||
|
||||
ascii_content = <<~MD
|
||||
|
|
@ -132,37 +176,25 @@ describe "User creates wiki page" do
|
|||
stem:[2+2] is 4
|
||||
MD
|
||||
|
||||
find("#wiki_format option[value=asciidoc]").select_option
|
||||
find("#wiki_page_format option[value=asciidoc]").select_option
|
||||
|
||||
fill_in(:wiki_content, with: ascii_content)
|
||||
|
||||
page.within(".wiki-form") do
|
||||
click_button("Create page")
|
||||
end
|
||||
create_page(content: ascii_content)
|
||||
|
||||
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', :quarantine
|
||||
it_behaves_like 'wiki file attachments'
|
||||
end
|
||||
|
||||
context "in a group namespace", :js do
|
||||
context "in a group namespace" do
|
||||
let(:project) { create(:project, :wiki_repo, namespace: create(:group, :public)) }
|
||||
|
||||
it "has `Create home` as a commit message" do
|
||||
wait_for_requests
|
||||
it_behaves_like 'updates commit message'
|
||||
|
||||
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
|
||||
it "creates a page from the home page" do
|
||||
create_page(content: "My awesome wiki!")
|
||||
|
||||
expect(page).to have_content("Home")
|
||||
.and have_content("Last edited by #{user.name}")
|
||||
|
|
@ -178,76 +210,37 @@ 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)
|
||||
|
||||
# Commit message field should have correct value.
|
||||
expect(page).to have_field(message_field, with: "Create #{unslug}")
|
||||
|
||||
create_page(content: "My awesome wiki!")
|
||||
|
||||
expect(page).to have_content(unslug)
|
||||
.and have_content("Last edited by #{user.name}")
|
||||
.and have_content("My awesome wiki!")
|
||||
end
|
||||
end
|
||||
|
||||
context "in a user namespace" do
|
||||
let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
|
||||
|
||||
context "via the `new wiki page` page" do
|
||||
it "creates a page with a single word" 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
|
||||
|
||||
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
|
||||
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'
|
||||
end
|
||||
|
||||
it "shows the emoji autocompletion dropdown" do
|
||||
click_link("New page")
|
||||
start_writing('text-autocomplete')
|
||||
|
||||
page.within(".wiki-form") do
|
||||
find("#wiki_content").native.send_keys("")
|
||||
find("#wiki_page_content").native.send_keys("")
|
||||
|
||||
fill_in(:wiki_content, with: ":")
|
||||
fill_in(:wiki_page_content, with: ":")
|
||||
end
|
||||
|
||||
expect(page).to have_selector(".atwho-view")
|
||||
|
|
@ -258,23 +251,9 @@ describe "User creates wiki page" do
|
|||
let(:project) { create(:project, :wiki_repo, namespace: create(:group, :public)) }
|
||||
|
||||
context "via the `new wiki page` page" do
|
||||
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
|
||||
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'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ 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)
|
||||
|
|
@ -18,5 +19,6 @@ 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,6 +10,13 @@ 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))
|
||||
|
|
@ -28,12 +35,7 @@ describe 'User updates wiki page' do
|
|||
end
|
||||
|
||||
it 'updates a page that has a path', :js do
|
||||
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
|
||||
create_page(title: 'one/two/three-test', content: 'wiki content')
|
||||
|
||||
expect(current_path).to include('one/two/three-test')
|
||||
expect(find('.wiki-pages')).to have_content('three')
|
||||
|
|
@ -72,9 +74,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[message]', with: 'Update home')
|
||||
expect(page).to have_field('wiki_page[message]', with: 'Update home')
|
||||
|
||||
fill_in(:wiki_content, with: 'My awesome wiki!')
|
||||
fill_in(:wiki_page_content, with: 'My awesome wiki!')
|
||||
click_button('Save changes')
|
||||
|
||||
expect(page).to have_content('Home')
|
||||
|
|
@ -83,31 +85,31 @@ describe 'User updates wiki page' do
|
|||
end
|
||||
|
||||
it 'updates the commit message as the title is changed', :js do
|
||||
fill_in(:wiki_title, with: 'Wiki title')
|
||||
fill_in(:wiki_page_title, with: 'Wiki title')
|
||||
|
||||
expect(page).to have_field('wiki[message]', with: 'Update Wiki title')
|
||||
expect(page).to have_field('wiki_page[message]', with: 'Update Wiki title')
|
||||
end
|
||||
|
||||
it 'does not allow XSS', :js do
|
||||
fill_in(:wiki_title, with: '<script>')
|
||||
fill_in(:wiki_page_title, with: '<script>')
|
||||
|
||||
expect(page).to have_field('wiki[message]', with: 'Update <script>')
|
||||
expect(page).to have_field('wiki_page[message]', with: 'Update <script>')
|
||||
end
|
||||
|
||||
it 'shows a validation error message' do
|
||||
fill_in(:wiki_content, with: '')
|
||||
fill_in(:wiki_page_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_content').value).to eq('')
|
||||
expect(find('textarea#wiki_page_content').value).to eq('')
|
||||
end
|
||||
|
||||
it 'shows the emoji autocompletion dropdown', :js do
|
||||
find('#wiki_content').native.send_keys('')
|
||||
fill_in(:wiki_content, with: ':')
|
||||
find('#wiki_page_content').native.send_keys('')
|
||||
fill_in(:wiki_page_content, with: ':')
|
||||
|
||||
expect(page).to have_selector('.atwho-view')
|
||||
end
|
||||
|
|
@ -143,9 +145,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[message]', with: 'Update home')
|
||||
expect(page).to have_field('wiki_page[message]', with: 'Update home')
|
||||
|
||||
fill_in(:wiki_content, with: 'My awesome wiki!')
|
||||
fill_in(:wiki_page_content, with: 'My awesome wiki!')
|
||||
|
||||
click_button('Save changes')
|
||||
|
||||
|
|
@ -169,50 +171,43 @@ describe 'User updates wiki page' do
|
|||
visit(project_wiki_edit_path(project, wiki_page))
|
||||
end
|
||||
|
||||
it 'moves the page to the root folder' do
|
||||
fill_in(:wiki_title, with: "/#{page_name}")
|
||||
def edit_title!(title)
|
||||
fill_in(:wiki_page_title, with: title)
|
||||
|
||||
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_dir = "foo1/bar1/#{page_name}"
|
||||
new_page_path = "baz/quux/#{page_name}"
|
||||
edit_title!(new_page_path)
|
||||
|
||||
fill_in(:wiki_title, with: new_page_dir)
|
||||
|
||||
click_button('Save changes')
|
||||
|
||||
expect(current_path).to eq(project_wiki_path(project, new_page_dir))
|
||||
expect(current_path).to eq(project_wiki_path(project, new_page_path))
|
||||
end
|
||||
|
||||
it 'remains in the same place if title has not changed' do
|
||||
original_path = project_wiki_path(project, wiki_page)
|
||||
|
||||
fill_in(:wiki_title, with: page_name)
|
||||
|
||||
click_button('Save changes')
|
||||
edit_title!(page_name)
|
||||
|
||||
expect(current_path).to eq(original_path)
|
||||
end
|
||||
|
||||
it 'can be moved to a different dir with a different name' do
|
||||
new_page_dir = "foo1/bar1/new_page_name"
|
||||
new_page_path = "quux/baz/new_page_name"
|
||||
edit_title!(new_page_path)
|
||||
|
||||
fill_in(:wiki_title, with: new_page_dir)
|
||||
|
||||
click_button('Save changes')
|
||||
|
||||
expect(current_path).to eq(project_wiki_path(project, new_page_dir))
|
||||
expect(current_path).to eq(project_wiki_path(project, new_page_path))
|
||||
end
|
||||
|
||||
it 'can be renamed and moved to the root folder' do
|
||||
new_name = 'new_page_name'
|
||||
|
||||
fill_in(:wiki_title, with: "/#{new_name}")
|
||||
|
||||
click_button('Save changes')
|
||||
edit_title!("/#{new_name}")
|
||||
|
||||
expect(current_path).to eq(project_wiki_path(project, new_name))
|
||||
end
|
||||
|
|
@ -220,9 +215,7 @@ describe 'User updates wiki page' do
|
|||
it 'squishes the title before creating the page' do
|
||||
new_page_dir = " foo1 / bar1 / #{page_name} "
|
||||
|
||||
fill_in(:wiki_title, with: new_page_dir)
|
||||
|
||||
click_button('Save changes')
|
||||
edit_title!(new_page_dir)
|
||||
|
||||
expect(current_path).to eq(project_wiki_path(project, "foo1/bar1/#{page_name}"))
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,10 +8,11 @@ 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 " }
|
||||
let(:wiki_page) do
|
||||
create(:wiki_page,
|
||||
wiki: project.wiki,
|
||||
attrs: { title: 'home', content: "Look at this [image](#{path})\n\n " })
|
||||
attrs: { title: 'home', content: wiki_content })
|
||||
end
|
||||
|
||||
before do
|
||||
|
|
@ -19,17 +20,18 @@ 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"
|
||||
|
||||
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
|
||||
create_page(title: 'one/two/three-test', content: 'wiki content')
|
||||
end
|
||||
|
||||
it 'shows the history of a page that has a path', :js do
|
||||
|
|
@ -83,24 +85,27 @@ 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='#{project.wiki.wiki_base_path}/#{path}']")
|
||||
expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/#{path}")
|
||||
expect(page).to have_xpath("//img[@data-src='#{image_path}']")
|
||||
expect(page).to have_link('image', href: "#{image_path}")
|
||||
|
||||
click_on('image')
|
||||
|
||||
expect(current_path).to match("wikis/#{path}")
|
||||
expect(current_path).to match(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
|
||||
expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/#{path}")
|
||||
href = project_wiki_path(project, path)
|
||||
|
||||
expect(page).to have_link('image', href: href)
|
||||
|
||||
click_on('image')
|
||||
|
||||
expect(current_path).to match("wikis/#{path}")
|
||||
expect(current_path).to match(href)
|
||||
expect(page).to have_content('Create New Page')
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ 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' })
|
||||
|
|
@ -17,73 +18,182 @@ describe 'User views wiki pages' do
|
|||
let!(:wiki_page3) do
|
||||
create(:wiki_page, wiki: project.wiki, attrs: { title: '2 home', content: '2' })
|
||||
end
|
||||
|
||||
let(:pages) do
|
||||
page.find('.wiki-pages-list').all('li').map { |li| li.find('a') }
|
||||
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}")
|
||||
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
|
||||
|
||||
context 'ordered by title' do
|
||||
let(:pages_ordered_by_title) { [wiki_page2, wiki_page3, wiki_page1] }
|
||||
def sort_desc!
|
||||
page.within('.wiki-sort-dropdown') do
|
||||
page.find('.qa-reverse-sort').click
|
||||
end
|
||||
end
|
||||
|
||||
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)
|
||||
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') }
|
||||
|
||||
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!
|
||||
end
|
||||
|
||||
let(:expected_sequence) { [sub_folder, wiki_page1, wiki_page3, wiki_page2] }
|
||||
|
||||
it_behaves_like 'correctly_sorted_pages'
|
||||
end
|
||||
end
|
||||
|
||||
context 'desc' do
|
||||
before do
|
||||
page.within('.wiki-sort-dropdown') do
|
||||
page.find('.rspec-reverse-sort').click
|
||||
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
|
||||
|
||||
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)
|
||||
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'
|
||||
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'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'ordered by created_at' do
|
||||
let(:pages_ordered_by_created_at) { [wiki_page1, wiki_page2, wiki_page3] }
|
||||
let(:pages_ordered_by_created_at) { [wiki_page1, wiki_page2, wiki_page3, wiki_page4, wiki_page5] }
|
||||
|
||||
before do
|
||||
page.within('.wiki-sort-dropdown') do
|
||||
click_button('Title')
|
||||
click_link('Created date')
|
||||
end
|
||||
sort_by_created_at!
|
||||
end
|
||||
|
||||
let(:page_link_selector) { 'a.wiki-page-title' }
|
||||
|
||||
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_created_at[index].title)
|
||||
end
|
||||
end
|
||||
let(:expected_sequence) { [wiki_page1, wiki_page2, wiki_page3, wiki_page4, wiki_page5] }
|
||||
|
||||
it_behaves_like 'correctly_sorted_pages'
|
||||
end
|
||||
|
||||
context 'desc' do
|
||||
before do
|
||||
page.within('.wiki-sort-dropdown') do
|
||||
page.find('.rspec-reverse-sort').click
|
||||
end
|
||||
sort_desc!
|
||||
end
|
||||
|
||||
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
|
||||
let(:expected_sequence) { [wiki_page5, wiki_page4, wiki_page3, wiki_page2, wiki_page1] }
|
||||
|
||||
it_behaves_like 'correctly_sorted_pages'
|
||||
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>`;
|
||||
|
||||
const init = newPage => {
|
||||
setHTMLFixture(editFormHtmlFixture(newPage));
|
||||
titleInput = document.getElementById('wiki_page_title');
|
||||
messageInput = document.getElementById('wiki_page_message');
|
||||
wikis = new Wikis();
|
||||
};
|
||||
|
||||
describe('when the wiki page is being created', () => {
|
||||
const formHtmlFixture = editFormHtmlFixture({ newPage: true });
|
||||
|
||||
beforeEach(() => {
|
||||
setHTMLFixture(formHtmlFixture);
|
||||
|
||||
titleInput = document.getElementById('wiki_title');
|
||||
messageInput = document.getElementById('wiki_message');
|
||||
wikis = new Wikis();
|
||||
});
|
||||
beforeEach(() => init(CREATE));
|
||||
|
||||
it('binds an event listener to the title input', () => {
|
||||
wikis.handleWikiTitleChange = jest.fn();
|
||||
|
|
@ -51,15 +51,7 @@ describe('Wikis', () => {
|
|||
});
|
||||
|
||||
describe('when the wiki page is being updated', () => {
|
||||
const formHtmlFixture = editFormHtmlFixture({ newPage: false });
|
||||
|
||||
beforeEach(() => {
|
||||
setHTMLFixture(formHtmlFixture);
|
||||
|
||||
titleInput = document.getElementById('wiki_title');
|
||||
messageInput = document.getElementById('wiki_message');
|
||||
wikis = new Wikis();
|
||||
});
|
||||
beforeEach(() => init(UPDATE));
|
||||
|
||||
it('sets the commit message when title changes, prefixing with "Update"', () => {
|
||||
titleInput.value = 'My title';
|
||||
|
|
|
|||
|
|
@ -23,8 +23,13 @@ describe WikiHelper do
|
|||
|
||||
describe '#wiki_sort_controls' do
|
||||
let(:project) { create(:project) }
|
||||
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" }
|
||||
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
|
||||
|
||||
def expected_link(sort, direction, icon_class)
|
||||
path = "/#{project.full_path}/wikis/pages?direction=#{direction}&sort=#{sort}"
|
||||
|
|
@ -62,6 +67,18 @@ 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,6 +11,10 @@ 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]
|
||||
|
||||
|
|
@ -28,12 +32,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("#{wiki.wiki_base_path}/#{repository_upload_folder}/a.test")
|
||||
expect(filtered_link.attribute('href').value).to eq(upload_href "a.test")
|
||||
end
|
||||
end
|
||||
|
||||
context 'with "img" html tag' do
|
||||
let(:path) { "#{wiki.wiki_base_path}/#{repository_upload_folder}/a.jpg" }
|
||||
let(:path) { upload_href "a.jpg" }
|
||||
|
||||
context 'inside an "a" html tag' do
|
||||
it 'rewrites links' do
|
||||
|
|
@ -57,7 +61,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("#{wiki.wiki_base_path}/#{repository_upload_folder}/a.mp4")
|
||||
expect(filtered_link.attribute('src').value).to eq(upload_href "a.mp4")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -65,7 +69,8 @@ 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("#{wiki.wiki_base_path}/#{repository_upload_folder}/a.wav")
|
||||
expect(filtered_link.attribute('src').value).to eq(upload_href "a.wav")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,6 +3,12 @@
|
|||
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
|
||||
|
|
@ -54,132 +60,138 @@ describe Banzai::Pipeline::WikiPipeline do
|
|||
end
|
||||
|
||||
describe "Links" 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')) }
|
||||
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)
|
||||
|
||||
{ "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
|
||||
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
|
||||
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
|
||||
context "when creating hierarchical links to the current directory" do
|
||||
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)
|
||||
let(:markdown) { "[Page](#{nesting}page#{extension})" }
|
||||
|
||||
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/nested/twice/page\"")
|
||||
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'
|
||||
end
|
||||
|
||||
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\"")
|
||||
context 'file-like links' do
|
||||
let(:extension) { '.md' }
|
||||
let(:expected_page_path) { 'nested/twice/page.md' }
|
||||
it_behaves_like 'a correct link rewrite'
|
||||
end
|
||||
end
|
||||
|
||||
context "when creating hierarchical links to the parent directory" do
|
||||
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\"")
|
||||
let(:nesting) { '../' }
|
||||
context "non file links" do
|
||||
let(:extension) { '' }
|
||||
let(:expected_page_path) { 'nested/page' }
|
||||
it_behaves_like 'a correct link rewrite'
|
||||
end
|
||||
|
||||
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\"")
|
||||
context "file-like links" do
|
||||
let(:extension) { '.md' }
|
||||
let(:expected_page_path) { 'nested/page.md' }
|
||||
it_behaves_like 'a correct link rewrite'
|
||||
end
|
||||
end
|
||||
|
||||
context "when creating hierarchical links to a sub-directory" do
|
||||
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)
|
||||
let(:nesting) { './subdirectory/' }
|
||||
|
||||
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/nested/twice/subdirectory/page\"")
|
||||
context "non file links" do
|
||||
let(:extension) { '' }
|
||||
let(:expected_page_path) { 'nested/twice/subdirectory/page' }
|
||||
it_behaves_like 'a correct link rewrite'
|
||||
end
|
||||
|
||||
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\"")
|
||||
context 'file-like links' do
|
||||
let(:extension) { '.md' }
|
||||
let(:expected_page_path) { 'nested/twice/subdirectory/page.md' }
|
||||
it_behaves_like 'a correct link rewrite'
|
||||
end
|
||||
end
|
||||
|
||||
describe "when creating non-hierarchical links" do
|
||||
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)
|
||||
let(:nesting) { '' }
|
||||
|
||||
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/page\"")
|
||||
context 'non-file links' do
|
||||
let(:extension) { '' }
|
||||
let(:expected_page_path) { 'page' }
|
||||
it_behaves_like 'a correct link rewrite'
|
||||
end
|
||||
|
||||
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\"")
|
||||
context 'non-file links (with spaces)' do
|
||||
let(:extension) { ' slug' }
|
||||
let(:expected_page_path) { 'page%20slug' }
|
||||
it_behaves_like 'a correct link rewrite'
|
||||
end
|
||||
|
||||
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\"")
|
||||
context "file links" do
|
||||
let(:extension) { '.md' }
|
||||
let(:expected_page_path) { 'nested/twice/page.md' }
|
||||
it_behaves_like 'a correct link rewrite'
|
||||
end
|
||||
|
||||
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\"")
|
||||
context 'links with anchor' do
|
||||
let(:extension) { '#title' }
|
||||
let(:expected_page_path) { 'page#title' }
|
||||
it_behaves_like 'a correct link rewrite'
|
||||
end
|
||||
|
||||
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\"")
|
||||
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'
|
||||
end
|
||||
end
|
||||
|
||||
describe "when creating root links" do
|
||||
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)
|
||||
let(:nesting) { '/' }
|
||||
|
||||
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/page\"")
|
||||
context 'non-file links' do
|
||||
let(:extension) { '' }
|
||||
let(:expected_page_path) { 'page' }
|
||||
it_behaves_like 'a correct link rewrite'
|
||||
end
|
||||
|
||||
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\"")
|
||||
context 'file links' do
|
||||
let(:extension) { '.md' }
|
||||
let(:expected_page_path) { 'page.md' }
|
||||
it_behaves_like 'a correct link rewrite'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "linking to pages outside the wiki (absolute)" do
|
||||
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
|
||||
let(:markdown) { "[Link to Page](http://example.com/page)" }
|
||||
let(:page_href) { 'http://example.com/page' }
|
||||
it_behaves_like 'a correct link rewrite'
|
||||
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" }
|
||||
|
|
@ -261,37 +273,54 @@ describe Banzai::Pipeline::WikiPipeline do
|
|||
end
|
||||
|
||||
describe 'videos and audio' 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')) }
|
||||
|
||||
it 'generates video html structure' do
|
||||
markdown = ""
|
||||
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
|
||||
|
||||
expect(output).to include('<video src="/wiki_link_ns/wiki_link_project/wikis/nested/twice/video_file_name.mp4"')
|
||||
def src(file_name)
|
||||
"#{prefix}/nested/twice/#{file_name}"
|
||||
end
|
||||
|
||||
it 'rewrites and replaces video links names with white spaces to %20' do
|
||||
markdown = ""
|
||||
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
|
||||
shared_examples 'correct video rewrite' do
|
||||
let(:markdown) { "" }
|
||||
let(:video_fragment) { "<video src=\"#{prefix}/#{expected_file_path}\"" }
|
||||
let(:options) do
|
||||
{
|
||||
project: project,
|
||||
project_wiki: project_wiki,
|
||||
page_slug: page.slug
|
||||
}
|
||||
end
|
||||
|
||||
expect(output).to include('<video src="/wiki_link_ns/wiki_link_project/wikis/nested/twice/video%20file%20name.mp4"')
|
||||
it 'generates video html structure' do
|
||||
output = described_class.to_html(markdown, options)
|
||||
|
||||
expect(output).to include(video_fragment)
|
||||
end
|
||||
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
|
||||
|
||||
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'
|
||||
end
|
||||
|
||||
it 'generates audio html structure' do
|
||||
markdown = ""
|
||||
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('<audio src="/wiki_link_ns/wiki_link_project/wikis/nested/twice/audio_file_name.wav"')
|
||||
expect(output).to include(%Q'<audio src="#{src(safe_name)}"')
|
||||
end
|
||||
|
||||
it 'rewrites and replaces audio links names with white spaces to %20' do
|
||||
markdown = ""
|
||||
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('<audio src="/wiki_link_ns/wiki_link_project/wikis/nested/twice/audio%20file%20name.wav"')
|
||||
expect(output).to include(%Q'<audio src="#{src(safe_name)}"')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -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_base_path}/#{wiki_page.slug}"
|
||||
expect(url).to eq "#{Gitlab.config.gitlab.url}#{wiki_page.wiki.wiki_page_path}/#{wiki_page.slug}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -252,4 +252,41 @@ 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
|
||||
|
|
|
|||
|
|
@ -28,7 +28,9 @@ describe ProjectWiki do
|
|||
|
||||
describe '#web_url' do
|
||||
it 'returns the full web URL to the wiki' do
|
||||
expect(subject.web_url).to eq("#{Gitlab.config.gitlab.url}/#{project.full_path}/wikis/home")
|
||||
home_url = Gitlab::Routing.url_helpers.project_wiki_url(project, :home)
|
||||
|
||||
expect(subject.web_url).to eq(home_url)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -71,9 +73,23 @@ describe ProjectWiki do
|
|||
|
||||
describe "#wiki_base_path" do
|
||||
it "returns the wiki base path" do
|
||||
wiki_base_path = "#{Gitlab.config.gitlab.relative_url_root}/#{project.full_path}/wikis"
|
||||
wiki_path = Gitlab::Routing.url_helpers.project_wikis_path(project)
|
||||
|
||||
expect(subject.wiki_base_path).to eq(wiki_base_path)
|
||||
expect(subject.wiki_base_path).to eq(wiki_path)
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,94 @@
|
|||
# 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)] }
|
||||
|
|
@ -40,7 +120,112 @@ 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/wikis/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) }
|
||||
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,87 +9,6 @@ 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'
|
||||
|
|
@ -505,7 +424,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/wikis/wiki_page')
|
||||
expect(page.to_partial_path).to eq('projects/wiki_pages/wiki_page')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -585,12 +504,4 @@ 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
|
||||
|
|
|
|||
|
|
@ -3,9 +3,12 @@ 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(true)
|
||||
allow(Project).to receive(:find_by_full_path).with('gitlab/gitlabhq', any_args).and_return(project)
|
||||
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,
|
||||
|
|
@ -145,24 +148,39 @@ describe 'project routing' do
|
|||
it_behaves_like 'redirecting a legacy project path', "/gitlab/gitlabhq/autocomplete_sources/labels", "/gitlab/gitlabhq/-/autocomplete_sources/labels"
|
||||
end
|
||||
|
||||
# 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
|
||||
# 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
|
||||
describe Projects::WikisController, 'routing' do
|
||||
it 'to #pages' do
|
||||
expect(get('/gitlab/gitlabhq/wikis/pages')).to route_to('projects/wikis#pages', namespace_id: 'gitlab', project_id: 'gitlabhq')
|
||||
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')
|
||||
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')
|
||||
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
|
||||
end
|
||||
|
||||
it_behaves_like 'RESTful project resources' do
|
||||
let(:actions) { [:create, :edit, :show, :destroy] }
|
||||
let(:controller) { 'wikis' }
|
||||
let(:controller) { 'wiki_pages' }
|
||||
let(:controller_path) { '-/wiki_pages' }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,103 @@
|
|||
# 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
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
# 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,4 +46,14 @@ 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,6 +14,8 @@ 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();
|
||||
|
|
|
|||
|
|
@ -2,8 +2,11 @@
|
|||
|
||||
module GitHelpers
|
||||
def rugged_repo(repository)
|
||||
path = File.join(TestEnv.repos_path, repository.disk_path + '.git')
|
||||
rugged_repo_at_path(repository.disk_path + '.git')
|
||||
end
|
||||
|
||||
def rugged_repo_at_path(relative_path)
|
||||
path = File.join(TestEnv.repos_path, relative_path)
|
||||
Rugged::Repository.new(path)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -12,4 +12,10 @@ module WikiHelpers
|
|||
::Wikis::CreateAttachmentService.new(project, user, opts)
|
||||
.execute[:result][:file_path]
|
||||
end
|
||||
|
||||
# Generate the form field name for a given attribute of an object.
|
||||
# This is rather general, but is currently only used in the wiki featur tests.
|
||||
def form_field_name(obj, attr_name)
|
||||
"#{ActiveModel::Naming.param_key(obj)}[#{attr_name}]"
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
RSpec::Matchers.define :have_header_with_correct_id_and_link do |level, text, id, parent = ".md"|
|
||||
match do |actual|
|
||||
node = find("#{parent} h#{level} a#user-content-#{id}")
|
||||
# anchors may be invisible
|
||||
node = find("#{parent} h#{level} a#user-content-#{id}", visible: false)
|
||||
|
||||
expect(node[:href]).to end_with("##{id}")
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ shared_examples 'wiki file attachments' do
|
|||
end
|
||||
end
|
||||
|
||||
context 'uploading is complete', :quarantine do
|
||||
context 'uploading is complete' do
|
||||
it 'shows "Attach a file" button on uploading complete' do
|
||||
attach_with_dropzone
|
||||
wait_for_requests
|
||||
|
|
@ -52,11 +52,11 @@ shared_examples 'wiki file attachments' do
|
|||
end
|
||||
|
||||
it 'the markdown link is added to the page' do
|
||||
fill_in(:wiki_content, with: '')
|
||||
fill_in(:wiki_page_content, with: '')
|
||||
attach_with_dropzone(true)
|
||||
wait_for_requests
|
||||
|
||||
expect(page.find('#wiki_content').value)
|
||||
expect(page.find('#wiki_page_content').value)
|
||||
.to match(%r{\!\[dk\]\(uploads/\h{32}/dk\.png\)$})
|
||||
end
|
||||
|
||||
|
|
@ -70,7 +70,7 @@ shared_examples 'wiki file attachments' do
|
|||
img_link = page.find('a.no-attachment-icon img')['src']
|
||||
|
||||
expect(link).to eq img_link
|
||||
expect(URI.parse(link).path).to eq File.join(wiki.wiki_base_path, file_path)
|
||||
expect(URI.parse(link).path).to eq File.join(wiki.wiki_page_path, file_path)
|
||||
end
|
||||
|
||||
it 'the file has been added to the wiki repository' do
|
||||
|
|
|
|||
Loading…
Reference in New Issue