Merge branch 'dev' into custom-fields-in-config

Conflicts:
	README.md
	lib/open_project/pdf_export/export_card/column_element.rb
pull/6827/head
Richard 11 years ago
commit a628d4ae82
  1. 16
      README.md
  2. 2
      app/controllers/export_card_configurations_controller.rb
  3. 41
      app/models/export_card_configuration.rb
  4. 7
      app/views/export_card_configurations/_form.html.erb
  5. 8
      app/views/export_card_configurations/_rows_format_help.html.erb
  6. 4
      app/views/export_card_configurations/edit.html.erb
  7. 4
      app/views/export_card_configurations/index.html.erb
  8. 2
      app/views/export_card_configurations/new.html.erb
  9. 8
      config/locales/de.yml
  10. 8
      config/locales/en.yml
  11. 5
      db/migrate/20140207134248_add_description_to_export_card_configurations.rb
  12. 3
      doc/CHANGELOG.md
  13. 5
      features/step_definitions/given.rb
  14. 104
      lib/open_project/pdf_export/export_card/card_element.rb
  15. 45
      lib/open_project/pdf_export/export_card/column_element.rb
  16. 37
      spec/controllers/export_card_configurations_controller_spec.rb
  17. 1
      spec/export_card/document_generator_spec.rb
  18. 5
      spec/factories/export_card_configuration_factory.rb
  19. 1
      spec/views/edit.html.erb_spec.rb
  20. 2
      spec/views/index.html.erb_spec.rb

@ -71,6 +71,7 @@ The following sample YAML shows the required form and all of the available confi
<pre>
group1:
has_border: false
height: 200
rows:
row1:
height: 50
@ -133,7 +134,22 @@ group2:
has_label: true
font_size: 15
minimum_lines: 1
group3:
rows:
row1:
priority: 2
columns:
children:
has_label: true
has_count: true
indented: true
font_size: 15
font_style: normal
minimum_lines: 1
render_if_empty: true
</pre>
The config is divided into groups. A group can have a height property which will enforce the minimum height of the group in pixels. The has_border property can be set to true which will draw a border around the rows in the group.
Any number of rows can be defined. The font_size and minimum_lines properties define how much height on the card is given to the row. The plugin will attempt to assign enough space to each of the rows, however space will be assigned based on the priorities of the the rows, with rows with lower priority (higher numbers) being reduced and removed first if there is not enough for all the data. The row height can be forced by giving a value, in pixels, for the row height property. This will override the assigned row height.

@ -100,7 +100,7 @@ class ExportCardConfigurationsController < ApplicationController
end
def export_card_configurations_params
params.require(:export_card_configuration).permit(:name, :rows, :per_page, :page_size, :orientation)
params.require(:export_card_configuration).permit(:name, :rows, :per_page, :page_size, :orientation, :description)
end
def load_config

@ -28,7 +28,7 @@ class ExportCardConfiguration < ActiveRecord::Base
class RowsYamlValidator < ActiveModel::Validator
REQUIRED_GROUP_KEYS = ["rows"]
VALID_GROUP_KEYS = ["rows", "has_border"]
VALID_GROUP_KEYS = ["rows", "has_border", "height"]
REQUIRED_ROW_KEYS = ["columns"]
VALID_ROW_KEYS = ["columns", "height", "priority"]
# TODO: Security Consideration
@ -37,18 +37,33 @@ class ExportCardConfiguration < ActiveRecord::Base
REQUIRED_COLUMN_KEYS = []
VALID_COLUMN_KEYS = ["has_label", "min_font_size", "max_font_size",
"font_size", "font_style", "text_align", "minimum_lines", "render_if_empty",
"width", "indented"]
"width", "indented", "custom_label", "has_count"]
def assert_required_keys(hash, valid_keys, required_keys)
hash.assert_valid_keys valid_keys
if !hash.is_a?(Hash)
raise ArgumentError, I18n.t('validation_error_yaml_is_badly_formed')
end
begin
hash.assert_valid_keys valid_keys
rescue ArgumentError => e
# Small hack alert: Catch a raise error again but with localised text
raise ArgumentError, "#{I18n.t('validation_error_uknown_key')} '#{e.message.split(": ")[1]}'"
end
pending_keys = required_keys - hash.keys
raise(ArgumentError, "Required key(s) not present: #{pending_keys.join(", ")}") unless pending_keys.empty?
raise(ArgumentError, "#{I18n.t('validation_error_required_keys_not_present')} #{pending_keys.join(", ")}") unless pending_keys.empty?
end
def validate(record)
if record.rows.nil? || !(YAML::load(record.rows)).is_a?(Hash)
record.errors[:rows] << "YAML is badly formed."
return false
begin
if record.rows.nil? || !(YAML::load(record.rows)).is_a?(Hash)
record.errors[:rows] << I18n.t('validation_error_yaml_is_badly_formed')
return false
end
rescue Psych::SyntaxError => e
record.errors[:rows] << I18n.t('validation_error_yaml_is_badly_formed')
return false
end
begin
@ -67,7 +82,7 @@ class ExportCardConfiguration < ActiveRecord::Base
end
end
rescue ArgumentError => e
record.errors[:rows] << "YAML error: #{e.message}"
record.errors[:rows] << "#{I18n.t('yaml_error')} #{e.message}"
end
end
end
@ -76,11 +91,9 @@ class ExportCardConfiguration < ActiveRecord::Base
validates :name, presence: true
validates :rows, rows_yaml: true
validates :per_page, numericality: { only_integer: true }
validates :page_size, inclusion: { in: %w(A4),
message: "%{value} is not a valid page size" }, allow_nil: false
validates :orientation, inclusion: { in: %w(landscape portrait),
message: "%{value} is not a valid page size" }, allow_nil: true
validates :per_page, numericality: { only_integer: true, greater_than_or_equal_to: 1 }
validates :page_size, inclusion: { in: %w(A4) }, allow_nil: false
validates :orientation, inclusion: { in: %w(landscape portrait) }, allow_nil: true
scope :active, -> { where(active: true) }
@ -106,7 +119,7 @@ class ExportCardConfiguration < ActiveRecord::Base
def rows_hash
config = YAML::load(rows)
raise BadlyFormedExportCardConfigurationError.new("Badly formed YAML") if !config.is_a?(Hash)
raise BadlyFormedExportCardConfigurationError.new(I18n.t('validation_error_yaml_is_badly_formed')) if !config.is_a?(Hash)
config
end

@ -29,11 +29,14 @@ See doc/COPYRIGHT.md for more details.
<p><%= f.error_messages %></p>
<!--[form:export_card_configuration]-->
<p><%= f.text_field :name, :required => true %></p>
<p><%= f.text_area :description, :class => "wiki-edit" %></p>
<p><%= f.text_field :per_page, :required => true %></p>
<p><%= f.text_field :page_size, :required => true, :value => "A4", :readonly => true %></p>
<P><%= f.select :orientation, [:landscape, :portrait], :required => true %></p>
<p><%= f.text_area :rows, :required => true %></p>
<% field = (f.text_area :rows, :required => true, :class => "wiki-edit") %>
<%= render partial: "rows_format_help", locals: { field: field } %>
<!--[eoform:export_card_configuration]-->
</div>
<%= submit_tag l(@config.new_record? ? :button_create : :button_save) %>
<%= submit_tag l(@config.new_record? ? :button_create : :button_save) %>

@ -0,0 +1,8 @@
<p class="jstElements">
<span class="help">
<a href="https://github.com/finnlabs/openproject-pdf_export#usage" class="icon icon-help" onclick="window.open('https://github.com/finnlabs/openproject-pdf_export#usage', '', 'resizable=yes, location=no, width=850, height=800, menubar=no, status=no, scrollbars=yes'); return false;">
<%= l('help_link_rows_format') %>
</a>
</span>
<%= field %>
</p>

@ -24,8 +24,10 @@ See doc/COPYRIGHT.md for more details.
++#%>
<% html_title l(:label_administration), "#{l(:label_edit)} #{l(:label_export_card_configuration)} #{@config.name}" %>
<h2><%= link_to t(:label_export_card_configuration_plural), pdf_export_export_card_configurations_path %> &#187; <%= h(@config.name) %></h2>
<%= form_for @config, :method => :put, url: pdf_export_export_card_configuration_path(@config.id), :builder => TabularFormBuilder do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %>
<% end %>
<% end %>

@ -31,6 +31,8 @@ See doc/COPYRIGHT.md for more details.
<%= link_to l(:label_export_card_configuration_new), {:action => 'new'}, :class => 'icon icon-add' %>
</div>
<% html_title l(:label_administration), l(:label_export_card_configuration_plural) %>
<h2><%=l(:label_export_card_configuration_plural)%></h2>
<table class="list">
@ -86,5 +88,3 @@ See doc/COPYRIGHT.md for more details.
<% end %>
</tbody>
</table>
<% html_title(l(:label_type_plural)) -%>

@ -25,6 +25,8 @@ See doc/COPYRIGHT.md for more details.
++#%>
<% html_title l(:label_administration), l(:label_export_card_configuration_new) %>
<h2><%= link_to t(:label_export_card_configuration_plural), pdf_export_export_card_configurations_path %> &#187; <%= t(:label_export_card_configuration_new) %></h2>
<%= form_for @config, :method => :post, url: pdf_export_export_card_configurations_path, :builder => TabularFormBuilder do |f| %>

@ -30,10 +30,16 @@ de:
label_backlogs_export_card_config_select: "Bitte wählen Sie eine Export-Kartenkonfiguration"
label_backlogs_export_card_export: "Exportieren"
label_export_card_configuration_new: "Neue Export-Kartenkonfiguration"
label_export_card_configuration: "Export-Kartenkonfiguration"
label_export_card_configuration_plural: "Export-Kartenkonfigurationen"
label_export_card_activate: "Aktivieren"
label_export_card_deactivate: "De-aktivieren"
notice_export_card_configuration_activated: "Export-Kartenkonfiguration erfolgreich aktiviert"
notice_export_card_configuration_deactivated: "Export-Kartenkonfiguration erfolgreich de-aktiviert"
error_can_not_activate_export_card_configuration: "Diese Konfiguration kann nicht aktiviert werden"
error_can_not_deactivate_export_card_configuration: "Diese Konfiguration kann nicht de-aktiviert werden"
error_can_not_deactivate_export_card_configuration: "Diese Konfiguration kann nicht de-aktiviert werden"
validation_error_required_keys_not_present: "Erfordete(r) Schlüssel nicht vorhanden:"
validation_error_yaml_is_badly_formed: "Ungültiges YAML Format."
validation_error_uknown_key: "Unbekannter Schlüssel:"
yaml_error: "YAML Fehler:"
help_link_rows_format: "Rows formatieren"

@ -30,10 +30,16 @@ en:
label_backlogs_export_card_config_select: "Select export card configuration"
label_backlogs_export_card_export: "Export"
label_export_card_configuration_new: "New Export Card Config"
label_export_card_configuration: "Export Card Config"
label_export_card_configuration_plural: "Export Card Configs"
label_export_card_activate: "Activate"
label_export_card_deactivate: "De-activate"
notice_export_card_configuration_activated: "Config succesfully activated"
notice_export_card_configuration_deactivated: "Config succesfully de-activated"
error_can_not_activate_export_card_configuration: "This config cannot be activated"
error_can_not_deactivate_export_card_configuration: "This config cannot be de-activated"
error_can_not_deactivate_export_card_configuration: "This config cannot be de-activated"
validation_error_required_keys_not_present: "Required key(s) not present:"
validation_error_yaml_is_badly_formed: "YAML is badly formed."
validation_error_uknown_key: "Unknown key:"
yaml_error: "YAML error:"
help_link_rows_format: "Rows Formatting"

@ -0,0 +1,5 @@
class AddDescriptionToExportCardConfigurations < ActiveRecord::Migration
def change
add_column :export_card_configurations, :description, :text
end
end

@ -26,4 +26,7 @@ See doc/COPYRIGHT.md for more details.
# Changelog
* `#4024` Subpages have no unique page titles
* `#4157` Negative integers can be entered in "Per Page" configuration of pdf configuration
* `#4182` Export Configuration - New export configuration field - Description
* Initial plugin release

@ -26,6 +26,7 @@
Given /^there are multiple export card configurations$/ do
config1 = ExportCardConfiguration.create!({
name: "Default",
description: "This is a description",
active: true,
per_page: 1,
page_size: "A4",
@ -34,6 +35,7 @@ Given /^there are multiple export card configurations$/ do
})
config2 = ExportCardConfiguration.create!({
name: "Custom",
description: "This is a description",
active: true,
per_page: 1,
page_size: "A4",
@ -42,6 +44,7 @@ Given /^there are multiple export card configurations$/ do
})
config3 = ExportCardConfiguration.create!({
name: "Custom 2",
description: "This is a description",
active: true,
per_page: 1,
page_size: "A4",
@ -50,6 +53,7 @@ Given /^there are multiple export card configurations$/ do
})
config4 = ExportCardConfiguration.create!({
name: "Custom Inactive",
description: "This is a description",
active: false,
per_page: 1,
page_size: "A4",
@ -62,6 +66,7 @@ end
Given /^there is the default export card configuration$/ do
config1 = ExportCardConfiguration.create!({
name: "Default",
description: "This is a description",
active: true,
per_page: 1,
page_size: "A4",

@ -34,21 +34,12 @@ module OpenProject::PdfExport::ExportCard
@work_package = work_package
@group_elements = []
# raise BadlyFormedExportCardConfigurationError.new("Badly formed YAML") if @rows.nil?
# Simpler to remove empty rows before calculating the row sizes
RowElement.prune_empty_groups(@groups_config, work_package)
# Get an array of all the row hashes
rows = []
@groups_config.each do |gk, gv|
gv["rows"].each do |rk, rv|
rows << rv
end
end
# Assign the row height, ignoring groups
heights = assign_row_heights(rows)
# NEW
all_heights = assign_all_heights_new(@groups_config)
reduce_rows(all_heights)
text_padding = @orientation[:text_padding]
group_padding = @orientation[:group_padding]
@ -58,8 +49,8 @@ module OpenProject::PdfExport::ExportCard
# Initialize groups
@groups_config.each_with_index do |(g_key, g_value), i|
row_count = g_value["rows"].count
row_heights = heights.slice(current_row, row_count)
group_height = row_heights.sum
row_heights = all_heights[:row_heights].reject {|row| row[:group] != i}.map{|row| row[:height]}
group_height = all_heights[:group_heights][i]
group_orientation = {
y_offset: @orientation[:height] - current_y_offset,
x_offset: 0,
@ -76,65 +67,64 @@ module OpenProject::PdfExport::ExportCard
end
end
def assign_row_heights(rows)
# Assign initial heights for rows in all groups
available = @orientation[:height] - @orientation[:text_padding]
c = rows.count
assigned_heights = Array.new(c){ available / c }
min_heights = min_row_heights(rows)
diffs = assigned_heights.zip(min_heights).map {|a, m| a - m}
diffs.each_with_index do |diff, i|
if diff < 0
# Need to grab some pixels from a low priority row and add them to current one
reduce_low_priority_rows(rows, assigned_heights, diffs, i)
def assign_all_heights_new(groups)
available = @orientation[:height] - (@orientation[:group_padding] * 2)
group_heights = Array.new
row_heights = Array.new
groups.each_with_index do |(gk, gv), i|
enforced_group_height = gv["height"] || -1
used_group_height = 0
gv["rows"].each do |rk, rv|
if rv["height"]
used_group_height += rv["height"]
row_heights << { height: rv["height"], group: i, priority: rv["priority"] || 10 }
else
used_group_height += min_row_height(rv)
row_heights << { height: min_row_height(rv), group: i, priority: rv["priority"] || 10 }
end
end
group_heights << [used_group_height, enforced_group_height].max
end
# TODO: Check assigned heights are big enough
assigned_heights
{ group_heights: group_heights, row_heights: row_heights }
end
def reduce_low_priority_rows(rows, assigned_heights, diffs, conflicted_i)
# Get an array of row indexes sorted by inverse priority
def reduce_rows(heights)
available = @orientation[:height] - (@orientation[:group_padding] * 2)
diff = available - heights[:group_heights].sum
return false if diff >= 0
diff *= -1
rows = heights[:row_heights]
groups = heights[:group_heights]
priorities = *(0..rows.count - 1)
.zip(rows.map { |row| row["priority"] or 10 })
.zip(rows.map { |row| row[:priority] or 10 })
.sort {|x,y| y[1] <=> x[1]}
.map {|x| x[0]}
to_reduce = diffs[conflicted_i] * -1
priorities.each do |p|
diff = diffs[p]
if diff > 0
if diff >= to_reduce
exchange(assigned_heights, diffs, p, conflicted_i, to_reduce)
return true
else
exchange(assigned_heights, diffs, p, conflicted_i, diff)
to_reduce -= diff
end
to_reduce = rows[p]
if to_reduce[:height] >= diff
to_reduce[:height] -= diff
groups[to_reduce[:group]] -= diff
break
else
diff -= to_reduce[:height]
groups[to_reduce[:group]] -= to_reduce[:height]
to_reduce[:height] = 0
end
end
return false
end
def exchange(heights, diffs, a, b, v)
heights[a] -= v
heights[b] += v
diffs[a] -= v
diffs[b] += v
end
def min_row_heights(rows)
# Calculate minimum user assigned heights...
min_heights = Array.new(rows.count)
rows.each_with_index do |row, i|
min_heights[i] = min_row_height(row)
end
min_heights
heights
end
def min_row_height(row)
return row["enforced_group_height"] if row["enforced_group_height"]
# Look through each of the row's columns for the column with the largest minimum height
largest = 0
row["columns"].each do |rk, rv|

@ -51,18 +51,35 @@ module OpenProject::PdfExport::ExportCard
end
end
if value.is_a?(Array)
value = value.map{|c| c.to_s }.join("\n")
end
draw_value(value)
end
private
def available_languages
Setting.available_languages
end
def label_text(value)
if @has_label
custom_label = @config['custom_label']
label_text = if custom_label
"#{custom_label}"
else
localised_property_name
end
if @config['has_count'] && value.is_a?(Array)
label_text = "#{label_text} (#{value.count})"
end
label_text += ": "
else
label_text = ""
end
label_text
end
def abbreviated_text(text, options)
options = options.merge!({ document: @pdf })
text_box = Prawn::Text::Box.new(text, options)
@ -79,7 +96,7 @@ module OpenProject::PdfExport::ExportCard
end
def localised_property_name
@has_label ? "#{@work_package.class.human_attribute_name(@localised_custom_field_name ||= @property_name)}: " : ""
@work_package.class.human_attribute_name(@localised_custom_field_name ||= @property_name)
end
def draw_value(value)
@ -110,15 +127,19 @@ module OpenProject::PdfExport::ExportCard
# Label and text
@has_label = @config['has_label']
indented = @config['indented']
value = value.to_s if !value.is_a?(String)
label_text = localised_property_name
# Flatten value to a display string
display_value = value
display_value = display_value.map{|c| c.to_s }.join("\n") if display_value.is_a?(Array)
display_value = display_value.to_s if !display_value.is_a?(String)
if @has_label && indented
width_ratio = 0.2 # Note: I don't think it's worth having this in the config
# Label Textbox
offset = [@orientation[:x_offset], @orientation[:height] - (@orientation[:text_padding] / 2)]
box = @pdf.text_box(label_text,
box = @pdf.text_box(label_text(value),
{:height => @orientation[:height],
:width => @orientation[:width] * width_ratio,
:at => offset,
@ -137,7 +158,7 @@ module OpenProject::PdfExport::ExportCard
:size => font_size,
:min_font_size => min_font_size,
:align => text_align}
text = abbreviated_text(value, options)
text = abbreviated_text(display_value, options)
offset = [@orientation[:x_offset] + (@orientation[:width] * width_ratio), @orientation[:height] - (@orientation[:text_padding] / 2)]
# Content Textbox
@ -157,9 +178,9 @@ module OpenProject::PdfExport::ExportCard
:overflow => overflow,
:min_font_size => min_font_size,
:align => text_align}
text = abbreviated_text(value, options)
label_text = localised_property_name
texts = [{ text: label_text, styles: [:bold], :size => font_size }, { text: text, :size => font_size }]
text = abbreviated_text(display_value, options)
texts = [{ text: label_text(value), styles: [:bold], :size => font_size }, { text: text, :size => font_size }]
# Label and Content Textbox
offset = [@orientation[:x_offset], @orientation[:height] - (@orientation[:text_padding] / 2)]
@ -173,4 +194,4 @@ module OpenProject::PdfExport::ExportCard
end
end
end
end
end

@ -37,12 +37,14 @@ describe ExportCardConfigurationsController do
@params = {}
@valid_rows_yaml = "group1:\n has_border: false\n rows:\n row1:\n height: 50\n priority: 1\n columns:\n id:\n has_label: false"
@invalid_rows_yaml = "group1:\n invalid_property: true"
end
describe 'Create' do
it 'should let you create a configuration with all the values set' do
@params[:export_card_configuration] = {
name: "Config 1",
description: "This is a description",
rows: @valid_rows_yaml,
per_page: 5,
page_size: "A4",
@ -54,7 +56,7 @@ describe ExportCardConfigurationsController do
flash[:notice].should eql(I18n.t(:notice_successful_create))
end
it 'should not let you create an invalid configuration' do
it 'should not let you create a configuration with missing data' do
@params[:export_card_configuration] = {
name: "Config 1",
}
@ -62,6 +64,19 @@ describe ExportCardConfigurationsController do
response.should render_template('new')
end
it 'should not let you create a configuration with invalid data' do
@params[:export_card_configuration] = {
name: "Config 1",
rows: @invalid_rows_yaml,
per_page: 0,
page_size: "invalid",
orientation: "invalid"
}
post 'create', @params
response.should render_template('new')
end
end
describe 'Update' do
@ -74,9 +89,25 @@ describe ExportCardConfigurationsController do
flash[:notice].should eql(I18n.t(:notice_successful_update))
end
it 'should not let you update an invalid configuration' do
it 'should not let you update a configuration with invalid per_page' do
@params[:id] = @custom_config.id
@params[:export_card_configuration] = { per_page: 0}
put 'update', @params
response.should render_template('edit')
end
it 'should not let you update a configuration with invalid page_size' do
@params[:id] = @custom_config.id
@params[:export_card_configuration] = { page_size: "invalid"}
put 'update', @params
response.should render_template('edit')
end
it 'should not let you update a configuration with invalid orientation' do
@params[:id] = @custom_config.id
@params[:export_card_configuration] = { per_page: "string"}
@params[:export_card_configuration] = { orientation: "invalid"}
put 'update', @params
response.should render_template('edit')

@ -28,6 +28,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe OpenProject::PdfExport::ExportCard::DocumentGenerator do
let(:config) { ExportCardConfiguration.new({
name: "Default",
description: "This is a description",
per_page: 1,
page_size: "A4",
orientation: "landscape",

@ -27,6 +27,7 @@
FactoryGirl.define do
factory :export_card_configuration do
name "Config 1"
description "This is a description"
rows "group1:\n has_border: false\n rows:\n row1:\n height: 50\n priority: 1\n columns:\n id:\n has_label: false"
per_page 5
page_size "A4"
@ -35,6 +36,7 @@ FactoryGirl.define do
factory :default_export_card_configuration, :class => ExportCardConfiguration do
name "Default"
description "This is a description"
active true
rows "group1:\n has_border: false\n rows:\n row1:\n height: 50\n priority: 1\n columns:\n id:\n has_label: false"
per_page 5
@ -44,6 +46,7 @@ FactoryGirl.define do
factory :invalid_export_card_configuration, :class => ExportCardConfiguration do
name "Invalid"
description "This is a description"
rows "row1"
per_page "string"
page_size "asdf"
@ -52,6 +55,7 @@ FactoryGirl.define do
factory :active_export_card_configuration, :class => ExportCardConfiguration do
name "Config active"
description "This is a description"
active true
rows "group1:\n has_border: false\n rows:\n row1:\n height: 50\n priority: 1\n columns:\n id:\n has_label: false"
per_page 5
@ -61,6 +65,7 @@ FactoryGirl.define do
factory :inactive_export_card_configuration, :class => ExportCardConfiguration do
name "Config inactive"
description "This is a description"
active false
rows "group1:\n has_border: false\n rows:\n row1:\n height: 50\n priority: 1\n columns:\n id:\n has_label: false"
per_page 5

@ -38,6 +38,7 @@ describe 'export_card_configurations/edit' do
render
rendered.should have_field("Name", with: config.name)
rendered.should have_field("Description", with: config.description)
rendered.should have_field("Per page", with: config.per_page.to_s)
rendered.should have_field("Page size", with: config.page_size)
rendered.should have_field("Orientation", with: config.orientation)

@ -40,7 +40,7 @@ describe 'export_card_configurations/index' do
render
rendered.should have_selector("a", text: config1.name)
rendered.should have_selector("a", text: config1.name)
rendered.should have_selector("a", text: config2.name)
end
end
Loading…
Cancel
Save