Don't use stubs for UserInfo.current_user_id because it blocks the use case where UserInfo.current_user_id is changed at runtime.

This commit is contained in:
Mantas Masalskis 2019-09-18 19:25:04 +02:00 committed by Thorsten Eckel
parent 8096ee0892
commit 6606aa29cd
33 changed files with 482 additions and 242 deletions

View file

@ -336,6 +336,7 @@ browser:build:
paths: paths:
- public/assets/.sprockets-manifest* - public/assets/.sprockets-manifest*
- public/assets/application-* - public/assets/application-*
- public/assets/knowledge_base*
- public/assets/print-* - public/assets/print-*
.services_browser_template: &services_browser_definition .services_browser_template: &services_browser_definition

View file

@ -86,13 +86,6 @@ Rails/HasManyOrHasOneDependent:
- 'app/models/ticket/state_type.rb' - 'app/models/ticket/state_type.rb'
- 'app/models/user.rb' - 'app/models/user.rb'
# Offense count: 15
# Configuration parameters: Include.
# Include: app/helpers/**/*.rb
Rails/HelperInstanceVariable:
Exclude:
- 'app/helpers/knowledge_base_helper.rb'
# Offense count: 808 # Offense count: 808
Style/Documentation: Style/Documentation:
Enabled: false Enabled: false

View file

@ -0,0 +1,58 @@
module KnowledgeBaseBreadcrumbHelper
def render_breadcrumb_if_needed(knowledge_base, object, alternative)
objects = calculate_breadcrumb_path(object, alternative)
return if objects.empty?
render 'knowledge_base/public/breadcrumb',
{
objects: objects,
knowledge_base: knowledge_base
}
end
def calculate_breadcrumb_path(object, alternative)
objects = calculate_breadcrumb_to_category(object&.parent)
last = if alternative.present? && alternative.translations.any?
Translation.translate(system_locale_via_uri&.locale, 'Alternative translations')
else
object
end
objects + [last].compact
end
def calculate_breadcrumb_to_category(category)
return [] if category.blank?
output = [category]
parent = category
while (parent = find_category(parent&.parent_id))
output << parent
end
output.compact.reverse
end
def breadcrumb_path_for(object, locale = params.fetch(:locale))
case object
when KnowledgeBase
help_root_path(locale: locale)
when KnowledgeBase::Category
help_category_path(object.translation, locale: locale)
when KnowledgeBase::Answer
help_answer_path(object.category.translation, object.translation, locale: locale)
end
end
def breadcrumb_text_for(object)
case object
when HasTranslations
object.translation.title
else
object
end
end
end

View file

@ -1,97 +1,11 @@
module KnowledgeBaseHelper module KnowledgeBaseHelper
def render_breadcrumb_if_needed def effective_layout_name(knowledge_base, object)
objects = [] layout_prefix = object.present? ? :category : :homepage
if @object knowledge_base.send("#{layout_prefix}_layout")
objects += calculate_breadcrumb_to_category(@category || @object&.parent)
end end
last = if @alternative.present? && @alternative.translations.any? def custom_path_if_needed(path, knowledge_base)
Translation.translate(system_locale_via_uri&.locale, 'Alternative translations')
else
@object
end
objects << last if last.present?
return if objects.empty?
render 'knowledge_base/public/breadcrumb',
{
objects: objects,
knowledge_base: @knowledge_base
}
end
def calculate_breadcrumb_to_category(category)
output = [category]
parent = category
while (parent = find_category(parent&.parent_id))
output << parent
end
output.compact.reverse
end
def visibility_note(object)
return if !current_user&.permissions?('knowledge_base.editor')
text = visibility_text(object)
return if text.nil?
render 'knowledge_base/public/visibility_note', text: text
end
def visibility_text(object)
case object
when CanBePublished
visiblity_text_can_be_published(object)
when KnowledgeBase::Category
visiblity_text_category(object)
end
end
def visiblity_text_can_be_published(object)
case object.can_be_published_aasm.current_state
when :internal
'internal'
when :archived
'archived'
when :draft
'not published'
end
end
def visiblity_text_category(object)
return if object.public_content?
if object.self_with_children_answers.only_internal.any?
'hidden, visible only internally'
else
'hidden, no published answers'
end
end
def breadcrumb_path_for(object, locale = params.fetch(:locale))
case object
when KnowledgeBase
help_root_path(locale: locale)
when KnowledgeBase::Category
help_category_path(object.translation, locale: locale)
when KnowledgeBase::Answer
help_answer_path(object.category.translation, object.translation, locale: locale)
end
end
def effective_layout_name
layout_prefix = @object.present? ? :category : :homepage
@knowledge_base.send("#{layout_prefix}_layout")
end
def custom_path_if_needed(path, knowledge_base = @knowledge_base)
return path if knowledge_base.custom_address_matches? request return path if knowledge_base.custom_address_matches? request
prefix = knowledge_base.custom_address_uri&.path prefix = knowledge_base.custom_address_uri&.path
@ -117,43 +31,6 @@ module KnowledgeBaseHelper
"edit #{suffix}" "edit #{suffix}"
end end
def kb_top_bar_tag(object)
case object
when KnowledgeBase::Answer
object.can_be_published_aasm.current_state
when KnowledgeBase::Category
kb_locale = object&.translation&.kb_locale
object.public_content?(kb_locale) ? 'Visible' : 'Invisible'
when KnowledgeBase
'Published'
end
end
def kb_top_bar_color(object)
case object
when KnowledgeBase::Answer
kb_answer_top_bar_color(object)
when KnowledgeBase::Category
kb_locale = object&.translation&.kb_locale
object.public_content?(kb_locale) ? 'green' : 'yellow'
when KnowledgeBase
'green'
end
end
def kb_answer_top_bar_color(answer)
case answer.can_be_published_aasm.current_state
when :draft
'yellow'
when :internal
'blue'
when :published
'green'
when :archived
'grey'
end
end
def build_kb_link(object) def build_kb_link(object)
locale = params.fetch(:locale, object.translation.kb_locale) locale = params.fetch(:locale, object.translation.kb_locale)
@ -177,47 +54,4 @@ module KnowledgeBaseHelper
.build(host: host, scheme: scheme, port: port, fragment: path) .build(host: host, scheme: scheme, port: port, fragment: path)
.to_s .to_s
end end
def kb_public_page_title
title = @knowledge_base.translation.title
if @page_title_error
suffix = case @page_title_error
when :not_found
'Not Found'
when :alternatives
'Alternative Translations'
end
title + " - #{zt(suffix)}"
elsif @object
title + " - #{@object.translation.title}"
else
title
end
end
def prepare_rich_text_links(input)
scrubber = Loofah::Scrubber.new do |node|
next if node.name != 'a'
next if !node.key? 'data-target-type'
case node['data-target-type']
when 'knowledge-base-answer'
if (translation = KnowledgeBase::Answer::Translation.find_by(id: node['data-target-id']))
path = help_answer_path(translation.answer.category.translation_preferred(translation.kb_locale),
translation,
locale: translation.kb_locale.system_locale.locale)
node['href'] = custom_path_if_needed path
else
node['href'] = '#'
end
end
end
parsed = Loofah.scrub_fragment(input, scrubber).to_s.html_safe # rubocop:disable Rails/OutputSafety
parsed
end
end end

View file

@ -0,0 +1,21 @@
module KnowledgeBasePublicPageTitleHelper
def kb_public_page_title(leading, trailing, exception)
[
leading&.translation&.title,
kb_public_page_title_suffix(trailing, exception)
].compact.join(' - ')
end
def kb_public_page_title_suffix(item, exception)
return item&.translation&.title if exception.blank?
suffix = case exception
when :not_found
'Not Found'
when :alternatives
'Alternative Translations'
end
zt(suffix)
end
end

View file

@ -0,0 +1,25 @@
module KnowledgeBaseRichTextHelper
def prepare_rich_text_links(input)
scrubber = Loofah::Scrubber.new do |node|
next if node.name != 'a'
next if !node.key? 'data-target-type'
case node['data-target-type']
when 'knowledge-base-answer'
if (translation = KnowledgeBase::Answer::Translation.find_by(id: node['data-target-id']))
path = help_answer_path(translation.answer.category.translation_preferred(translation.kb_locale),
translation,
locale: translation.kb_locale.system_locale.locale)
node['href'] = custom_path_if_needed path, translation.kb_locale.knowledge_base
else
node['href'] = '#'
end
end
end
parsed = Loofah.scrub_fragment(input, scrubber).to_s.html_safe # rubocop:disable Rails/OutputSafety
parsed
end
end

View file

@ -0,0 +1,38 @@
module KnowledgeBaseTopBarHelper
def kb_top_bar_color(object)
case object
when KnowledgeBase::Answer
kb_answer_top_bar_color(object)
when KnowledgeBase::Category
kb_locale = object&.translation&.kb_locale
object.public_content?(kb_locale) ? 'green' : 'yellow'
when KnowledgeBase
'green'
end
end
def kb_answer_top_bar_color(answer)
case answer.can_be_published_aasm.current_state
when :draft
'yellow'
when :internal
'blue'
when :published
'green'
when :archived
'grey'
end
end
def kb_top_bar_tag(object)
case object
when KnowledgeBase::Answer
object.can_be_published_aasm.current_state
when KnowledgeBase::Category
kb_locale = object&.translation&.kb_locale
object.public_content?(kb_locale) ? 'Visible' : 'Invisible'
when KnowledgeBase
'Published'
end
end
end

View file

@ -0,0 +1,41 @@
module KnowledgeBaseVisibilityNoteHelper
def visibility_note(object)
return if !current_user&.permissions?('knowledge_base.editor')
text = visibility_text(object)
return if text.nil?
render 'knowledge_base/public/visibility_note', text: text
end
def visibility_text(object)
case object
when CanBePublished
visiblity_text_can_be_published(object)
when KnowledgeBase::Category
visiblity_text_category(object)
end
end
def visiblity_text_can_be_published(object)
case object.can_be_published_aasm.current_state
when :internal
'internal'
when :archived
'archived'
when :draft
'not published'
end
end
def visiblity_text_category(object)
return if object.public_content?
if object.self_with_children_answers.only_internal.any?
'hidden, visible only internally'
else
'hidden, no published answers'
end
end
end

View file

@ -18,6 +18,9 @@ class KnowledgeBase::Answer < ApplicationModel
validates :category, presence: true validates :category, presence: true
# provide consistent naming with KB category
alias_attribute :parent, :category
alias assets_essential assets alias assets_essential assets
def attributes_with_association_ids def attributes_with_association_ids

View file

@ -1,11 +1,11 @@
<nav class="breadcrumbs"> <nav class="breadcrumbs">
<div class="container"> <div class="container">
<% ([knowledge_base] + objects).each do |object| %> <% ([knowledge_base] + objects).each do |object| %>
<%= link_to custom_path_if_needed(breadcrumb_path_for(object)), class: :breadcrumb do %> <%= link_to custom_path_if_needed(breadcrumb_path_for(object), knowledge_base), class: :breadcrumb do %>
<%= icon_for_object(object, knowledge_base.iconset) %> <%= icon_for_object(object, knowledge_base.iconset) %>
<span> <span>
<%= object.translation.title %> <%= breadcrumb_text_for(object) %>
</span> </span>
<% end %> <% end %>
<% end %> <% end %>

View file

@ -1,5 +1,5 @@
<main class="main main--article" <main class="main main--article"
data-base-path='<%= custom_path_if_needed help_answer_path(@category, @object, locale: '{locale}') %>' data-base-path='<%= custom_path_if_needed help_answer_path(@category, @object, locale: '{locale}'), @knowledge_base %>'
data-available-locales='<%= @object_locales.map(&:locale).join(',') %>'> data-available-locales='<%= @object_locales.map(&:locale).join(',') %>'>
<article class="article"> <article class="article">
<div class="container"> <div class="container">
@ -16,7 +16,7 @@ data-available-locales='<%= @object_locales.map(&:locale).join(',') %>'>
<%= icon 'paperclip' %> <%= icon 'paperclip' %>
<div class="attachments-title"><%= zt('Attached Files') %></div> <div class="attachments-title"><%= zt('Attached Files') %></div>
<% attachments.each do |attachment| %> <% attachments.each do |attachment| %>
<%= link_to custom_path_if_needed(attachment_path(attachment)), class: 'attachment', download: true do %> <%= link_to custom_path_if_needed(attachment_path(attachment), @knowledge_base), class: 'attachment', download: true do %>
<span class="attachment-name u-highlight"><%= attachment.filename %></span> <span class="attachment-name u-highlight"><%= attachment.filename %></span>
<div class="attachment-size">404kb</div> <div class="attachment-size">404kb</div>
<% end %> <% end %>

View file

@ -1,4 +1,4 @@
<%= link_to custom_path_if_needed help_answer_path(answer.category.translation, answer.translation, locale: params[:locale]) do %> <%= link_to custom_path_if_needed help_answer_path(answer.category.translation, answer.translation, locale: params[:locale]), @knowledge_base do %>
<span class="section-inner"> <span class="section-inner">
<%= icon('knowledge-base-answer') %> <%= icon('knowledge-base-answer') %>
<%= answer.translation.title %> <%= visibility_note(answer) %> <%= answer.translation.title %> <%= visibility_note(answer) %>

View file

@ -1,5 +1,5 @@
<li class="section <%= visibility_class_name(category) %>"> <li class="section <%= visibility_class_name(category) %>">
<%= link_to custom_path_if_needed help_category_path(category.translation, locale: params[:locale]) do %> <%= link_to custom_path_if_needed help_category_path(category.translation, locale: params[:locale]), @knowledge_base do %>
<span class="section-inner"> <span class="section-inner">
<%= icon(category.category_icon, knowledge_base.iconset) %> <%= icon(category.category_icon, knowledge_base.iconset) %>
<span><%= category.translation.title %></span> <span><%= category.translation.title %></span>

View file

@ -1,6 +1,6 @@
<main class="main main--categories" <main class="main main--categories"
<% if @object %> <% if @object %>
data-base-path='<%= custom_path_if_needed help_category_path(@object, locale: '{locale}') %>' data-base-path='<%= custom_path_if_needed help_category_path(@object, locale: '{locale}'), @knowledge_base %>'
data-available-locales='<%= @object_locales.map(&:locale).join(',') %>' data-available-locales='<%= @object_locales.map(&:locale).join(',') %>'
<% end %>> <% end %>>
<div class="container"> <div class="container">
@ -19,7 +19,7 @@
</div> </div>
<% else %> <% else %>
<% if @categories&.present? %> <% if @categories&.present? %>
<ul class="sections sections--<%= effective_layout_name %>" data-less-than-four="<%= @categories.length < 4 %>"> <ul class="sections sections--<%= effective_layout_name(@knowledge_base, @object) %>" data-less-than-four="<%= @categories.length < 4 %>">
<% @categories.each do |category| %> <% @categories.each do |category| %>
<%= render 'category', { category: category, knowledge_base: @knowledge_base } %> <%= render 'category', { category: category, knowledge_base: @knowledge_base } %>
<% end %> <% end %>

View file

@ -4,7 +4,7 @@
<h1><%= zt('Page not found') %></h1> <h1><%= zt('Page not found') %></h1>
<p> <p>
<%= zt('The page you were looking for does not exist.') %> <%= zt('The page you were looking for does not exist.') %>
<%= link_to zt('Click here'), custom_path_if_needed(help_root_path(locale: fallback_locale.locale)) %> <%= link_to zt('Click here'), custom_path_if_needed(help_root_path(locale: fallback_locale.locale), @knowledge_base) %>
<%= zt('to go to the homepage.') %> <%= zt('to go to the homepage.') %>
</p> </p>
</div> </div>

View file

@ -7,7 +7,7 @@
<% @alternative.translations.each do |translation| %> <% @alternative.translations.each do |translation| %>
<li> <li>
<%= translation.kb_locale.system_locale.name %>: <%= translation.kb_locale.system_locale.name %>:
<%= link_to translation.title, custom_path_if_needed(url_for(locale: translation_locale_code(translation))) %> <%= link_to translation.title, custom_path_if_needed(url_for(locale: translation_locale_code(translation)), @knowledge_base) %>
</li> </li>
<% end %> <% end %>
</ul> </ul>

View file

@ -3,11 +3,11 @@
data-id='<%= @knowledge_base.id %>' data-id='<%= @knowledge_base.id %>'
data-iconset='<%= @knowledge_base.iconset %>' data-iconset='<%= @knowledge_base.iconset %>'
data-available-locales='<%= all_locales.map(&:locale).join(',') %>' data-available-locales='<%= all_locales.map(&:locale).join(',') %>'
data-base-path='<%= custom_path_if_needed help_root_path(locale: '{locale}') %>' data-base-path='<%= custom_path_if_needed help_root_path(locale: '{locale}'), @knowledge_base %>'
data-primary-locale="<%= (filter_primary_kb_locale || all_locales.first)&.locale %>"> data-primary-locale="<%= (filter_primary_kb_locale || all_locales.first)&.locale %>">
<meta charset="utf-8"> <meta charset="utf-8">
<title><%= kb_public_page_title %></title> <title><%= kb_public_page_title(@knowledge_base, @object, @page_title_error) %></title>
<meta name="viewport" content="width=device-width,initial-scale=1.0,viewport-fit=cover"> <meta name="viewport" content="width=device-width,initial-scale=1.0,viewport-fit=cover">
<%= stylesheet_link_tag "knowledge_base.css", :media => 'all' %> <%= stylesheet_link_tag "knowledge_base.css", :media => 'all' %>
<%= render 'knowledge_base/public/inline_stylesheet', knowledge_base: @knowledge_base %> <%= render 'knowledge_base/public/inline_stylesheet', knowledge_base: @knowledge_base %>
@ -18,7 +18,7 @@
<header class="header js-header"> <header class="header js-header">
<div class="container"> <div class="container">
<h1 class="logo"> <h1 class="logo">
<%= link_to custom_path_if_needed(help_root_path(locale: params[:locale])) do %> <%= link_to custom_path_if_needed(help_root_path(locale: params[:locale]), @knowledge_base) do %>
<img src="/assets/images/<%= Setting.get('product_logo') %>"> <img src="/assets/images/<%= Setting.get('product_logo') %>">
<% end %> <% end %>
</h1> </h1>
@ -39,7 +39,7 @@
</div> </div>
</header> </header>
<%= render_breadcrumb_if_needed %> <%= render_breadcrumb_if_needed(@knowledge_base, @object, @alternative) %>
<%= yield %> <%= yield %>
@ -57,7 +57,7 @@
<ul class="dropdown-menu dropdown-menu-right dropdown-menu-up" role="menu"> <ul class="dropdown-menu dropdown-menu-right dropdown-menu-up" role="menu">
<% @object_locales&.each do |locale| %> <% @object_locales&.each do |locale| %>
<li class="<%= 'is-selected' if locale.name == system_locale_via_uri.name %>"> <li class="<%= 'is-selected' if locale.name == system_locale_via_uri.name %>">
<%= link_to custom_path_if_needed(url_for(locale: locale.locale)), hreflang: locale.locale do %> <%= link_to custom_path_if_needed(url_for(locale: locale.locale), @knowledge_base), hreflang: locale.locale do %>
<%= icon 'checkmark' %> <%= icon 'checkmark' %>
<%= locale.name %> <%= locale.name %>
<% end %> <% end %>

View file

@ -16,7 +16,7 @@ FactoryBot.define do
end end
if kb.kb_locales.blank? if kb.kb_locales.blank?
kb.kb_locales << build(:knowledge_base_locale, knowledge_base: kb) kb.kb_locales << build(:knowledge_base_locale, knowledge_base: kb, primary: true)
end end
end end
end end

View file

@ -1,5 +1,11 @@
FactoryBot.define do FactoryBot.define do
factory 'knowledge_base/answer', aliases: %i[knowledge_base_answer] do factory 'knowledge_base/answer', aliases: %i[knowledge_base_answer] do
category { create(:knowledge_base_category) } category { create(:knowledge_base_category) }
before(:create) do |answer|
next if answer.translations.present?
answer.translations << build('knowledge_base/answer/translation', answer: answer)
end
end end
end end

View file

@ -2,12 +2,25 @@ FactoryBot.define do
factory 'knowledge_base/answer/translation', aliases: %i[knowledge_base_answer_translation] do factory 'knowledge_base/answer/translation', aliases: %i[knowledge_base_answer_translation] do
answer { nil } answer { nil }
kb_locale { nil } kb_locale { nil }
title { Faker::Appliance.equipment } sequence(:title) { |n| "#{Faker::Appliance.equipment} ##{n}" }
content { build(:knowledge_base_answer_translation_content) } content { build(:knowledge_base_answer_translation_content) }
before(:create) do |translation| before(:create) do |translation, _context|
if translation.answer.nil? && translation.kb_locale.nil? if translation.answer.nil?
translation.answer = create(:knowledge_base_answer) build(:knowledge_base_answer, translations: [translation])
end
if translation.kb_locale.nil?
translation.kb_locale = translation.answer.category.knowledge_base.kb_locales.first
end
end
after(:build) do |translation, _context|
if translation.answer.nil?
build(:knowledge_base_answer, translations: [translation])
end
if translation.kb_locale.nil?
translation.kb_locale = translation.answer.category.knowledge_base.kb_locales.first translation.kb_locale = translation.answer.category.knowledge_base.kb_locales.first
end end
end end

View file

@ -5,7 +5,7 @@ FactoryBot.define do
before(:create) do |content| before(:create) do |content|
if content.translation.nil? if content.translation.nil?
content.translation = create(:knowledge_base_answer_translation) create(:knowledge_base_answer_translation, content: content)
end end
end end
end end

View file

@ -2,6 +2,12 @@ FactoryBot.define do
factory 'knowledge_base/category', aliases: %i[knowledge_base_category] do factory 'knowledge_base/category', aliases: %i[knowledge_base_category] do
knowledge_base { parent&.knowledge_base || create(:knowledge_base) } knowledge_base { parent&.knowledge_base || create(:knowledge_base) }
category_icon { 'f04b' } category_icon { 'f04b' }
before(:create) do |category|
next if category.translations.present?
category.translations << create('knowledge_base/category/translation', category: category)
end
end end
factory 'kb_category_with_tree', parent: 'knowledge_base/category' do factory 'kb_category_with_tree', parent: 'knowledge_base/category' do

View file

@ -1,14 +1,30 @@
FactoryBot.define do FactoryBot.define do
factory 'knowledge_base/category/translation', aliases: %i[knowledge_base_category_translation] do factory 'knowledge_base/category/translation', aliases: %i[knowledge_base_category_translation] do
transient do
knowledge_base { nil }
parent_category { nil }
end
category { nil } category { nil }
kb_locale { nil } kb_locale { nil }
title { Faker::Appliance.brand } sequence(:title) { |n| "#{Faker::Appliance.brand} ##{n}" }
before(:create) do |translation| before(:create) do |translation, context|
if translation.category.nil? && translation.kb_locale.nil? if translation.category.nil?
translation.category = create(:knowledge_base_category) attrs = if context.parent_category
translation.kb_locale = translation.category.knowledge_base.kb_locales.first { parent: context.parent_category }
end elsif context.knowledge_base
{ knowledge_base: context.knowledge_base }
else
{}
end
attrs[:translations] = [translation]
build(:knowledge_base_category, attrs)
end
translation.kb_locale = translation.category.knowledge_base.kb_locales.first if translation.kb_locale.nil?
end end
end end
end end

View file

@ -3,5 +3,6 @@ FactoryBot.define do
knowledge_base { kb_locale.knowledge_base } knowledge_base { kb_locale.knowledge_base }
kb_locale { nil } kb_locale { nil }
title { Faker::Company.name } title { Faker::Company.name }
footer_note { 'footer' }
end end
end end

View file

@ -1,11 +1,9 @@
require 'rails_helper' require 'rails_helper'
require 'models/contexts/factory_context' require 'models/contexts/factory_context'
RSpec.describe KnowledgeBase::Answer::Translation::Content, type: :model do RSpec.describe KnowledgeBase::Answer::Translation::Content, type: :model, current_user_id: 1 do
subject { create(:knowledge_base_answer_translation_content) } subject { create(:knowledge_base_answer_translation_content) }
before { UserInfo.current_user_id = 1 }
include_context 'factory' include_context 'factory'
it { is_expected.to have_one(:translation) } it { is_expected.to have_one(:translation) }

View file

@ -1,11 +1,9 @@
require 'rails_helper' require 'rails_helper'
require 'models/contexts/factory_context' require 'models/contexts/factory_context'
RSpec.describe KnowledgeBase::Answer::Translation, type: :model do RSpec.describe KnowledgeBase::Answer::Translation, type: :model, current_user_id: 1 do
subject { create(:knowledge_base_answer_translation) } subject { create(:knowledge_base_answer_translation) }
before { UserInfo.current_user_id = 1 }
include_context 'factory' include_context 'factory'
it { is_expected.to validate_presence_of(:title) } it { is_expected.to validate_presence_of(:title) }

View file

@ -2,7 +2,7 @@ require 'rails_helper'
require 'models/concerns/checks_kb_client_notification_examples' require 'models/concerns/checks_kb_client_notification_examples'
require 'models/contexts/factory_context' require 'models/contexts/factory_context'
RSpec.describe KnowledgeBase::Answer, type: :model do RSpec.describe KnowledgeBase::Answer, type: :model, current_user_id: 1 do
subject!(:kb_answer) { create(:knowledge_base_answer) } subject!(:kb_answer) { create(:knowledge_base_answer) }
include_context 'factory' include_context 'factory'

View file

@ -2,7 +2,7 @@ require 'rails_helper'
require 'models/concerns/checks_kb_client_notification_examples' require 'models/concerns/checks_kb_client_notification_examples'
require 'models/contexts/factory_context' require 'models/contexts/factory_context'
RSpec.describe KnowledgeBase::Category, type: :model do RSpec.describe KnowledgeBase::Category, type: :model, current_user_id: 1 do
subject(:kb_category) { create(:knowledge_base_category) } subject(:kb_category) { create(:knowledge_base_category) }
include_context 'factory' include_context 'factory'

View file

@ -35,3 +35,22 @@ end
Capybara.add_selector(:table_row) do Capybara.add_selector(:table_row) do
css { |id| %(tr[data-id='#{id}']) } css { |id| %(tr[data-id='#{id}']) }
end end
Capybara.add_selector(:link_containing) do
xpath { |text| ".//a//*[text()[contains(.,\"#{text}\")]]" }
end
# Knowledge Base
Capybara.add_selector(:knowledge_base_editor_bar) do
css { '.topbar' }
end
Capybara.add_selector(:knowledge_base_language_banner) do
css { '.language-banner' }
end
# don't use "knowledge_base" prefix because breadcrumbs
# could be available in other contexts (in the future)
Capybara.add_selector(:breadcrumb) do
css { '.breadcrumbs .breadcrumb span' }
end

View file

@ -0,0 +1,35 @@
RSpec.shared_context 'Knowledge Base users' do
let(:admin_user) { create :admin_user }
let(:agent_user) { create :agent_user }
let(:customer_user) { create :customer_user }
end
RSpec.shared_context 'basic Knowledge Base', current_user_id: 1 do
let :knowledge_base do
create(:knowledge_base)
end
let :primary_locale do
knowledge_base.translation_primary.kb_locale
end
let :alternative_locale do
create(:knowledge_base_locale, knowledge_base: knowledge_base, system_locale: Locale.find_by(locale: 'lt'))
end
let :category do
create(:knowledge_base_category, knowledge_base: knowledge_base)
end
let :answer do
create(:knowledge_base_answer, category: category)
end
let :published_answer do
create(:knowledge_base_answer, category: category, published_at: 1.week.ago)
end
let :internal_answer do
create(:knowledge_base_answer, category: category, internal_at: 1.week.ago)
end
end

View file

@ -1,37 +1,19 @@
# This module registers a before and after each hook callback that # This file registers a before and after each hook callback that
# resets the stored current_user_id in the UserInfo which will otherwise # resets the stored current_user_id in the UserInfo which will otherwise
# persists across multiple examples. # persists across multiple examples.
# This can lead to issues where actions were performed by a user created # This can lead to issues where actions were performed by a user created
# via a FactoryBot factory which will get removed after the example is # via a FactoryBot factory which will get removed after the example is
# completed. The UserInfo.current_user_id will persist which leads to e.g. # completed. The UserInfo.current_user_id will persist which leads to e.g.
# DB ForeignKey violation errors. # DB ForeignKey violation errors.
module ZammadSpecSupportUserInfo # If a `:current_user_id` metadata argument is set the initial value for
# UserInfo.current_user_id will be set to the arguments given value
def self.included(base)
# Execute in RSpec class context
base.class_exec do
before(:each) do |_example|
UserInfo.current_user_id = nil
end
after(:each) do |_example|
UserInfo.current_user_id = nil
end
end
end
end
RSpec.configure do |config| RSpec.configure do |config|
config.include ZammadSpecSupportUserInfo
config.around(:each, :current_user_id) do |example| config.before(:each) do |example|
UserInfo.current_user_id = example.metadata[:current_user_id] UserInfo.current_user_id = example.metadata[:current_user_id]
begin end
example.run
ensure config.after(:each) do |_example|
UserInfo.current_user_id = nil UserInfo.current_user_id = nil
end end
end end
end

View file

@ -0,0 +1,42 @@
require 'rails_helper'
RSpec.describe 'Public Knowledge Base for editor', type: :system, authenticated: true do
include_context 'basic Knowledge Base'
before do
published_answer && answer && internal_answer
end
context 'homepage' do
before { visit help_no_locale_path }
it { expect(page).to have_selector(:knowledge_base_editor_bar) }
it 'expect to have edit button' do
button = find '.topbar-btn'
expect(button['href']).to match(/edit$/)
end
end
context 'category' do
before { visit help_category_path(primary_locale.system_locale.locale, category) }
it 'shows published answer' do
within '.main' do
expect(page).to have_selector(:link_containing, published_answer.translation.title)
end
end
it 'shows draft answer' do
within '.main' do
expect(page).to have_selector(:link_containing, answer.translation.title)
end
end
it 'shows internal answer' do
within '.main' do
expect(page).to have_selector(:link_containing, internal_answer.translation.title)
end
end
end
end

View file

@ -0,0 +1,110 @@
require 'rails_helper'
RSpec.describe 'Public Knowledge Base for guest', type: :system, authenticated: false do
include_context 'basic Knowledge Base'
before do
published_answer && answer && internal_answer
end
context 'homepage' do
before { visit help_no_locale_path }
it('is redirected to primary locale') { expect(page).to have_current_path help_root_path(primary_locale.system_locale.locale) }
it { expect(page).not_to have_selector(:breadcrumb) }
it { expect(page).not_to have_selector(:knowledge_base_editor_bar) }
it 'shows category' do
within '.main' do
expect(page).to have_selector(:link_containing, published_answer.category.translation.title)
end
end
it 'does not show answer' do
within '.main' do
expect(page).not_to have_selector(:link_containing, published_answer.translation.title)
end
end
end
context 'category' do
before { visit help_category_path(primary_locale.system_locale.locale, category) }
it { expect(page).to have_selector(:breadcrumb) }
it 'shows published answer' do
within '.main' do
expect(page).to have_selector(:link_containing, published_answer.translation.title)
end
end
it 'does not show draft answer' do
within '.main' do
expect(page).not_to have_selector(:link_containing, answer.translation.title)
end
end
it 'does not show internal answer' do
within '.main' do
expect(page).not_to have_selector(:link_containing, internal_answer.translation.title)
end
end
context 'breadcrumb' do
it { expect(page).to have_selector(:breadcrumb, count: 2) }
it { expect(page.all(:breadcrumb)[0]).to have_text(knowledge_base.translation.title) }
it { expect(page.all(:breadcrumb)[1]).to have_text(category.translation.title) }
end
end
context 'answer' do
before { visit help_answer_path(primary_locale.system_locale.locale, category, published_answer) }
context 'breadcrumb' do
it { expect(page).to have_selector(:breadcrumb, count: 3) }
it { expect(page.all(:breadcrumb)[0]).to have_text(knowledge_base.translation.title) }
it { expect(page.all(:breadcrumb)[1]).to have_text(category.translation.title) }
it { expect(page.all(:breadcrumb)[2]).to have_text(published_answer.translation.title) }
end
end
context 'wrong locale' do
before { visit help_root_path(alternative_locale.system_locale.locale) }
it { expect(page).to have_selector(:knowledge_base_language_banner) }
context 'switch to correct locale after clicking on language banner' do
before do
within '.language-banner' do
click_on 'activate'
end
end
it { expect(page).not_to have_selector(:knowledge_base_language_banner) }
end
end
context 'offer in another locale' do
before do
create(:knowledge_base_translation, kb_locale: alternative_locale)
visit help_answer_path(alternative_locale.system_locale.locale, category, published_answer)
end
it { expect(page).to have_text(published_answer.translation_primary.title) }
it { expect(page).to have_text('only available in these languages') }
it { expect(page).not_to have_selector('h1', text: published_answer.translation_primary.title) }
context 'follow primary locale' do
before { click_on published_answer.translation_primary.title }
it { expect(page).to have_selector('h1', text: published_answer.translation_primary.title) }
end
end
context '404' do
before { visit help_answer_path(primary_locale.system_locale.locale, category, 12_345) }
it { expect(page).to have_text('Page not found') }
end
end