From 9478c4f00c084c4f3e158c93fe1f7f0d4d73c8e4 Mon Sep 17 00:00:00 2001 From: Brant Evans Date: Thu, 3 Jan 2019 12:15:46 -0700 Subject: [PATCH] Add support for exporting and importing widgets --- lib/task_helpers/exports/widgets.rb | 16 +++ lib/task_helpers/imports/widgets.rb | 26 ++++ lib/tasks/evm_export_import.rake | 17 +++ spec/lib/task_helpers/exports/widgets_spec.rb | 81 +++++++++++ .../imports/data/widgets/Test_Widget.yaml | 128 ++++++++++++++++++ .../data/widgets/Test_Widget_Import.yaml | 127 +++++++++++++++++ .../data/widgets/Test_Widget_attr_error.yml | 128 ++++++++++++++++++ .../widgets/Test_Widget_runtime_error.yml | 128 ++++++++++++++++++ .../data/widgets/Test_Widget_update.yml | 115 ++++++++++++++++ spec/lib/task_helpers/imports/widgets_spec.rb | 103 ++++++++++++++ 10 files changed, 869 insertions(+) create mode 100644 lib/task_helpers/exports/widgets.rb create mode 100644 lib/task_helpers/imports/widgets.rb create mode 100644 spec/lib/task_helpers/exports/widgets_spec.rb create mode 100644 spec/lib/task_helpers/imports/data/widgets/Test_Widget.yaml create mode 100644 spec/lib/task_helpers/imports/data/widgets/Test_Widget_Import.yaml create mode 100644 spec/lib/task_helpers/imports/data/widgets/Test_Widget_attr_error.yml create mode 100644 spec/lib/task_helpers/imports/data/widgets/Test_Widget_runtime_error.yml create mode 100644 spec/lib/task_helpers/imports/data/widgets/Test_Widget_update.yml create mode 100644 spec/lib/task_helpers/imports/widgets_spec.rb diff --git a/lib/task_helpers/exports/widgets.rb b/lib/task_helpers/exports/widgets.rb new file mode 100644 index 00000000000..4247381021c --- /dev/null +++ b/lib/task_helpers/exports/widgets.rb @@ -0,0 +1,16 @@ +module TaskHelpers + class Exports + class Widgets + def export(options = {}) + export_dir = options[:directory] + + widgets = options[:all] ? MiqWidget.all : MiqWidget.where(:read_only => false) + + widgets.each do |widget| + filename = Exports.safe_filename(widget.description, options[:keep_spaces]) + File.write("#{export_dir}/#{filename}.yaml", MiqWidget.export_to_yaml([widget.id], MiqWidget)) + end + end + end + end +end diff --git a/lib/task_helpers/imports/widgets.rb b/lib/task_helpers/imports/widgets.rb new file mode 100644 index 00000000000..fbdc231e6b5 --- /dev/null +++ b/lib/task_helpers/imports/widgets.rb @@ -0,0 +1,26 @@ +module TaskHelpers + class Imports + class Widgets + def import(options = {}) + return unless options[:source] + + glob = File.file?(options[:source]) ? options[:source] : "#{options[:source]}/*.yaml" + Dir.glob(glob) do |filename| + $log.info("Importing Widgets from: #{filename}") + + widget_options = { :userid => 'admin', + :overwrite => options[:overwrite], + :save => true } + + begin + widget_fd = File.open(filename, 'r') + MiqWidget.import(widget_fd, widget_options) + rescue ActiveModel::UnknownAttributeError, RuntimeError => err + $log.error("Error importing #{filename} : #{err.message}") + warn("Error importing #{filename} : #{err.message}") + end + end + end + end + end +end diff --git a/lib/tasks/evm_export_import.rake b/lib/tasks/evm_export_import.rake index 7cb64ec20b7..2861ea2975e 100644 --- a/lib/tasks/evm_export_import.rake +++ b/lib/tasks/evm_export_import.rake @@ -9,6 +9,7 @@ # * SmartState Analysis Scan Profiles # * Customization Templates # * Reports +# * Widgets namespace :evm do namespace :export do @@ -105,6 +106,14 @@ namespace :evm do exit # exit so that parameters to the first rake task are not run as rake tasks end + + desc 'Exports all widgets to individual YAML files' + task :widgets => :environment do + options = TaskHelpers::Exports.parse_options + TaskHelpers::Exports::Widgets.new.export(options) + + exit # exit so that parameters to the first rake task are not run as rake tasks + end end namespace :import do @@ -201,5 +210,13 @@ namespace :evm do exit # exit so that parameters to the first rake task are not run as rake tasks end + + desc 'Imports all widgets from individual YAML files' + task :widgets => :environment do + options = TaskHelpers::Imports.parse_options + TaskHelpers::Imports::Widgets.new.import(options) + + exit # exit so that parameters to the first rake task are not run as rake tasks + end end end diff --git a/spec/lib/task_helpers/exports/widgets_spec.rb b/spec/lib/task_helpers/exports/widgets_spec.rb new file mode 100644 index 00000000000..7f55a061e40 --- /dev/null +++ b/spec/lib/task_helpers/exports/widgets_spec.rb @@ -0,0 +1,81 @@ +describe TaskHelpers::Exports::Widgets do + let(:export_dir) do + Dir.mktmpdir('miq_exp_dir') + end + + before do + _guid, _server, _zone = EvmSpecHelper.create_guid_miq_server_zone + + MiqReport.seed_report("Vendor and Guest OS") + MiqWidget.seed_widget("chart_vendor_and_guest_os") + MiqWidget.sync_from_hash(YAML.safe_load(" + description: Test Widget + title: Test Widget + content_type: report + options: + :col_order: + - name + - vendor_display + :row_count: 5 + visibility: + :roles: + - _ALL_ + resource_name: Vendor and Guest OS + resource_type: MiqReport + enabled: true + read_only: false + ", [Symbol], %i(col_order row_count roles))) + MiqWidget.sync_from_hash(YAML.safe_load(" + description: Test Widget 2 + title: Test Widget 2 + content_type: report + options: + :col_order: + - name + - vendor_display + :row_count: 5 + visibility: + :roles: + - _ALL_ + resource_name: Vendor and Guest OS + resource_type: MiqReport + enabled: true + read_only: false + ", [Symbol], %i(col_order row_count roles))) + end + + after do + FileUtils.remove_entry export_dir + end + + describe "when --all is not specified" do + let(:widget_filename1) { "#{export_dir}/Test_Widget.yaml" } + let(:widget_filename2) { "#{export_dir}/Test_Widget_2.yaml" } + + it "exports custom widgets to individual files in a given directory" do + TaskHelpers::Exports::Widgets.new.export(:directory => export_dir) + expect(Dir[File.join(export_dir, '**', '*')].count { |file| File.file?(file) }).to eq(2) + widget1 = YAML.load_file(widget_filename1) + expect(widget1.first["MiqWidget"]["description"]).to eq("Test Widget") + widget2 = YAML.load_file(widget_filename2) + expect(widget2.first["MiqWidget"]["description"]).to eq("Test Widget 2") + end + end + + describe "when --all is specified" do + let(:widget_filename1) { "#{export_dir}/Test_Widget.yaml" } + let(:widget_filename2) { "#{export_dir}/Test_Widget_2.yaml" } + let(:widget_filename3) { "#{export_dir}/chart_vendor_and_guest_os.yaml" } + + it "exports all reports to individual files in a given directory" do + TaskHelpers::Exports::Widgets.new.export(:directory => export_dir, :all => true) + expect(Dir[File.join(export_dir, '**', '*')].count { |file| File.file?(file) }).to eq(3) + widget1 = YAML.load_file(widget_filename1) + expect(widget1.first["MiqWidget"]["description"]).to eq("Test Widget") + widget2 = YAML.load_file(widget_filename2) + expect(widget2.first["MiqWidget"]["description"]).to eq("Test Widget 2") + widget3 = YAML.load_file(widget_filename3) + expect(widget3.first["MiqWidget"]["description"]).to eq("chart_vendor_and_guest_os") + end + end +end diff --git a/spec/lib/task_helpers/imports/data/widgets/Test_Widget.yaml b/spec/lib/task_helpers/imports/data/widgets/Test_Widget.yaml new file mode 100644 index 00000000000..03dd37894de --- /dev/null +++ b/spec/lib/task_helpers/imports/data/widgets/Test_Widget.yaml @@ -0,0 +1,128 @@ +--- +- MiqWidget: + guid: d52ee383-6906-4312-bf48-b1bfaa4d8391 + description: Test Widget + title: Test Widget + content_type: report + options: + :row_count: 10 + :col_order: + - name + - power_state + - last_scan_on + visibility: + :roles: + - _ALL_ + user_id: + resource_id: 81 + resource_type: MiqReport + enabled: true + read_only: false + MiqReportContent: + - MiqReport: + title: Online VMs (Powered On) + rpt_group: Operations- Virtual Machines + rpt_type: Default + priority: 20 + db: Vm + cols: + - v_datastore_path + - name + - power_state + - last_scan_on + include: + host: + columns: + - name + storage: + columns: + - name + col_order: + - host.name + - storage.name + - v_datastore_path + - name + - power_state + - last_scan_on + headers: + - Host + - Datastore + - Datastore Path + - VM Name + - Vm Power State + - Last Analysis Time + conditions: !ruby/object:MiqExpression + exp: + and: + - "=": + field: Vm-power_state + value: 'on' + - "=": + field: Vm-active + value: 'true' + order: Ascending + sortby: + - name + group: + graph: + dims: + filename: 400_Operations- Virtual Machines/020_Online VMs.yaml + file_mtime: !ruby/object:ActiveSupport::TimeWithZone + utc: &1 2018-04-27 20:10:20.000000000 Z + zone: !ruby/object:ActiveSupport::TimeZone + name: Etc/UTC + time: *1 + categories: + timeline: + template_type: report + where_clause: + db_options: + generate_cols: + generate_rows: + col_formats: + tz: + time_profile_id: + display_filter: + col_options: + rpt_options: + miq_group_id: + user_id: + menu_name: Online VMs (Powered On) + MiqSchedule: + name: Test Widget + description: Test Widget + sched_action: + :method: generate_widget + filter: !ruby/object:MiqExpression + exp: + "=": + field: MiqWidget-id + value: 25 + context_type: + col_details: + MiqWidget-id: + :data_type: :integer + :virtual_reflection: false + :virtual_column: false + :sql_support: true + :excluded_by_preprocess_options: false + :tag: false + :include: {} + :format_sub_type: :integer + pexp: + "=": + field: MiqWidget-id + value: 25 + resource_type: MiqWidget + run_at: + :start_time: 2018-12-21 00:00:00.000000000 Z + :tz: UTC + :interval: + :unit: hourly + :value: 1 + enabled: true + userid: system + prod_default: system + adhoc: + file_depot_id: + resource_id: diff --git a/spec/lib/task_helpers/imports/data/widgets/Test_Widget_Import.yaml b/spec/lib/task_helpers/imports/data/widgets/Test_Widget_Import.yaml new file mode 100644 index 00000000000..d2bea7ca8d4 --- /dev/null +++ b/spec/lib/task_helpers/imports/data/widgets/Test_Widget_Import.yaml @@ -0,0 +1,127 @@ +--- +- MiqWidget: + guid: d52ee383-6906-4312-bf48-b1bfaa4d8391 + description: Test Widget Import + title: Test Widget Import + content_type: report + options: + :row_count: 10 + :col_order: + - name + - power_state + visibility: + :roles: + - _ALL_ + user_id: + resource_id: 81 + resource_type: MiqReport + enabled: true + read_only: false + MiqReportContent: + - MiqReport: + title: Online VMs (Powered On) + rpt_group: Operations- Virtual Machines + rpt_type: Default + priority: 20 + db: Vm + cols: + - v_datastore_path + - name + - power_state + - last_scan_on + include: + host: + columns: + - name + storage: + columns: + - name + col_order: + - host.name + - storage.name + - v_datastore_path + - name + - power_state + - last_scan_on + headers: + - Host + - Datastore + - Datastore Path + - VM Name + - Vm Power State + - Last Analysis Time + conditions: !ruby/object:MiqExpression + exp: + and: + - "=": + field: Vm-power_state + value: 'on' + - "=": + field: Vm-active + value: 'true' + order: Ascending + sortby: + - name + group: + graph: + dims: + filename: 400_Operations- Virtual Machines/020_Online VMs.yaml + file_mtime: !ruby/object:ActiveSupport::TimeWithZone + utc: &1 2018-04-27 20:10:20.000000000 Z + zone: !ruby/object:ActiveSupport::TimeZone + name: Etc/UTC + time: *1 + categories: + timeline: + template_type: report + where_clause: + db_options: + generate_cols: + generate_rows: + col_formats: + tz: + time_profile_id: + display_filter: + col_options: + rpt_options: + miq_group_id: + user_id: + menu_name: Online VMs (Powered On) + MiqSchedule: + name: Test Widget Import + description: Test Widget Import + sched_action: + :method: generate_widget + filter: !ruby/object:MiqExpression + exp: + "=": + field: MiqWidget-id + value: 25 + context_type: + col_details: + MiqWidget-id: + :data_type: :integer + :virtual_reflection: false + :virtual_column: false + :sql_support: true + :excluded_by_preprocess_options: false + :tag: false + :include: {} + :format_sub_type: :integer + pexp: + "=": + field: MiqWidget-id + value: 25 + resource_type: MiqWidget + run_at: + :start_time: 2018-12-21 00:00:00.000000000 Z + :tz: UTC + :interval: + :unit: hourly + :value: 1 + enabled: true + userid: system + prod_default: system + adhoc: + file_depot_id: + resource_id: diff --git a/spec/lib/task_helpers/imports/data/widgets/Test_Widget_attr_error.yml b/spec/lib/task_helpers/imports/data/widgets/Test_Widget_attr_error.yml new file mode 100644 index 00000000000..d06e3834f1a --- /dev/null +++ b/spec/lib/task_helpers/imports/data/widgets/Test_Widget_attr_error.yml @@ -0,0 +1,128 @@ +--- +- MiqWidget: + guid: d52ee383-6906-4312-bf48-b1bfaa4d8391 + description: Test Widget + invalid_title: Test Widget + content_type: report + options: + :row_count: 10 + :col_order: + - name + - power_state + - last_scan_on + visibility: + :roles: + - _ALL_ + user_id: + resource_id: 81 + resource_type: MiqReport + enabled: true + read_only: false + MiqReportContent: + - MiqReport: + title: Online VMs (Powered On) + rpt_group: Operations- Virtual Machines + rpt_type: Default + priority: 20 + db: Vm + cols: + - v_datastore_path + - name + - power_state + - last_scan_on + include: + host: + columns: + - name + storage: + columns: + - name + col_order: + - host.name + - storage.name + - v_datastore_path + - name + - power_state + - last_scan_on + headers: + - Host + - Datastore + - Datastore Path + - VM Name + - Vm Power State + - Last Analysis Time + conditions: !ruby/object:MiqExpression + exp: + and: + - "=": + field: Vm-power_state + value: 'on' + - "=": + field: Vm-active + value: 'true' + order: Ascending + sortby: + - name + group: + graph: + dims: + filename: 400_Operations- Virtual Machines/020_Online VMs.yaml + file_mtime: !ruby/object:ActiveSupport::TimeWithZone + utc: &1 2018-04-27 20:10:20.000000000 Z + zone: !ruby/object:ActiveSupport::TimeZone + name: Etc/UTC + time: *1 + categories: + timeline: + template_type: report + where_clause: + db_options: + generate_cols: + generate_rows: + col_formats: + tz: + time_profile_id: + display_filter: + col_options: + rpt_options: + miq_group_id: + user_id: + menu_name: Online VMs (Powered On) + MiqSchedule: + name: Test Widget + description: Test Widget + sched_action: + :method: generate_widget + filter: !ruby/object:MiqExpression + exp: + "=": + field: MiqWidget-id + value: 25 + context_type: + col_details: + MiqWidget-id: + :data_type: :integer + :virtual_reflection: false + :virtual_column: false + :sql_support: true + :excluded_by_preprocess_options: false + :tag: false + :include: {} + :format_sub_type: :integer + pexp: + "=": + field: MiqWidget-id + value: 25 + resource_type: MiqWidget + run_at: + :start_time: 2018-12-21 00:00:00.000000000 Z + :tz: UTC + :interval: + :unit: hourly + :value: 1 + enabled: true + userid: system + prod_default: system + adhoc: + file_depot_id: + resource_id: diff --git a/spec/lib/task_helpers/imports/data/widgets/Test_Widget_runtime_error.yml b/spec/lib/task_helpers/imports/data/widgets/Test_Widget_runtime_error.yml new file mode 100644 index 00000000000..1196045986c --- /dev/null +++ b/spec/lib/task_helpers/imports/data/widgets/Test_Widget_runtime_error.yml @@ -0,0 +1,128 @@ +--- +- MiqWidgeting: + guid: d52ee383-6906-4312-bf48-b1bfaa4d8391 + description: Test Widget + title: Test Widget + content_type: report + options: + :row_count: 10 + :col_order: + - name + - power_state + - last_scan_on + visibility: + :roles: + - _ALL_ + user_id: + resource_id: 81 + resource_type: MiqReport + enabled: true + read_only: false + MiqReportContent: + - MiqReport: + title: Online VMs (Powered On) + rpt_group: Operations- Virtual Machines + rpt_type: Default + priority: 20 + db: Vm + cols: + - v_datastore_path + - name + - power_state + - last_scan_on + include: + host: + columns: + - name + storage: + columns: + - name + col_order: + - host.name + - storage.name + - v_datastore_path + - name + - power_state + - last_scan_on + headers: + - Host + - Datastore + - Datastore Path + - VM Name + - Vm Power State + - Last Analysis Time + conditions: !ruby/object:MiqExpression + exp: + and: + - "=": + field: Vm-power_state + value: 'on' + - "=": + field: Vm-active + value: 'true' + order: Ascending + sortby: + - name + group: + graph: + dims: + filename: 400_Operations- Virtual Machines/020_Online VMs.yaml + file_mtime: !ruby/object:ActiveSupport::TimeWithZone + utc: &1 2018-04-27 20:10:20.000000000 Z + zone: !ruby/object:ActiveSupport::TimeZone + name: Etc/UTC + time: *1 + categories: + timeline: + template_type: report + where_clause: + db_options: + generate_cols: + generate_rows: + col_formats: + tz: + time_profile_id: + display_filter: + col_options: + rpt_options: + miq_group_id: + user_id: + menu_name: Online VMs (Powered On) + MiqSchedule: + name: Test Widget + description: Test Widget + sched_action: + :method: generate_widget + filter: !ruby/object:MiqExpression + exp: + "=": + field: MiqWidget-id + value: 25 + context_type: + col_details: + MiqWidget-id: + :data_type: :integer + :virtual_reflection: false + :virtual_column: false + :sql_support: true + :excluded_by_preprocess_options: false + :tag: false + :include: {} + :format_sub_type: :integer + pexp: + "=": + field: MiqWidget-id + value: 25 + resource_type: MiqWidget + run_at: + :start_time: 2018-12-21 00:00:00.000000000 Z + :tz: UTC + :interval: + :unit: hourly + :value: 1 + enabled: true + userid: system + prod_default: system + adhoc: + file_depot_id: + resource_id: diff --git a/spec/lib/task_helpers/imports/data/widgets/Test_Widget_update.yml b/spec/lib/task_helpers/imports/data/widgets/Test_Widget_update.yml new file mode 100644 index 00000000000..185592a4498 --- /dev/null +++ b/spec/lib/task_helpers/imports/data/widgets/Test_Widget_update.yml @@ -0,0 +1,115 @@ +--- +- MiqWidget: + guid: d52ee383-6906-4312-bf48-b1bfaa4d8391 + description: Test Widget + title: Test Widget Modified + content_type: report + options: + :row_count: 10 + :col_order: + - name + - power_state + - last_scan_on + - storage.name + visibility: + :roles: + - _ALL_ + user_id: + resource_id: 81 + resource_type: MiqReport + enabled: true + read_only: false + MiqReportContent: + - MiqReport: + title: Online VMs (Powered On) + rpt_group: Operations- Virtual Machines + rpt_type: Default + priority: 20 + db: Vm + cols: + - v_datastore_path + - name + - power_state + - last_scan_on + include: + host: + columns: + - name + storage: + columns: + - name + col_order: + - host.name + - storage.name + - v_datastore_path + - name + - power_state + - last_scan_on + headers: + - Host + - Datastore + - Datastore Path + - VM Name + - Vm Power State + - Last Analysis Time + conditions: !ruby/object:MiqExpression + exp: + and: + - "=": + field: Vm-power_state + value: 'on' + - "=": + field: Vm-active + value: 'true' + order: Ascending + sortby: + - name + group: + graph: + dims: + filename: 400_Operations- Virtual Machines/020_Online VMs.yaml + file_mtime: !ruby/object:ActiveSupport::TimeWithZone + utc: &1 2018-04-27 20:10:20.000000000 Z + zone: !ruby/object:ActiveSupport::TimeZone + name: Etc/UTC + time: *1 + categories: + timeline: + template_type: report + where_clause: + db_options: + generate_cols: + generate_rows: + col_formats: + tz: + time_profile_id: + display_filter: + col_options: + rpt_options: + miq_group_id: + user_id: + menu_name: Online VMs (Powered On) + MiqSchedule: + name: Test Widget + description: Test Widget + sched_action: + :method: generate_widget + filter: !ruby/object:MiqExpression + exp: + "=": + field: MiqWidget-id + value: 25 + context_type: + resource_type: MiqWidget + run_at: + :start_time: 2018-12-21 00:00:00.000000000 Z + :tz: UTC + :interval: + :unit: hourly + :value: 1 + enabled: true + userid: system + prod_default: system + adhoc: + file_depot_id: + resource_id: diff --git a/spec/lib/task_helpers/imports/widgets_spec.rb b/spec/lib/task_helpers/imports/widgets_spec.rb new file mode 100644 index 00000000000..fbc4462ad5b --- /dev/null +++ b/spec/lib/task_helpers/imports/widgets_spec.rb @@ -0,0 +1,103 @@ +describe TaskHelpers::Imports::Widgets do + describe "#import" do + let(:data_dir) { File.join(File.expand_path(__dir__), 'data', 'widgets') } + let(:widget_file1) { "Test_Widget.yaml" } + let(:widget_file2) { "Test_Widget_Import.yaml" } + let(:widget_name1) { "Test Widget" } + let(:widget_name2) { "Test Widget Import" } + let(:widget_title1) { "Test Widget" } + let(:widget_title2) { "Test Widget Import" } + let(:widget_cols1) { %w(name power_state last_scan_on) } + let(:widget_cols2) { %w(name power_state) } + let(:attr_err_file) { "Test_Widget_attr_error.yml" } + let(:runt_err_file) { "Test_Widget_runtime_error.yml" } + let(:options) { { :source => source } } + + before do + _guid, @miq_server, @zone = EvmSpecHelper.create_guid_miq_server_zone + FactoryBot.create(:user_admin, :userid => "admin") + end + + describe "when the source is a directory" do + let(:source) { data_dir } + + it 'imports all .yaml files in a specified directory' do + expect do + TaskHelpers::Imports::Widgets.new.import(options) + end.to_not output.to_stderr + expect(MiqWidget.all.count).to eq(2) + assert_test_widget_one_present + assert_test_widget_two_present + end + end + + describe "when the source is a file" do + let(:source) { "#{data_dir}/#{widget_file1}" } + + it 'imports a specified file' do + expect do + TaskHelpers::Imports::Widgets.new.import(options) + end.to_not output.to_stderr + expect(MiqWidget.all.count).to eq(1) + assert_test_widget_one_present + end + end + + describe "when the source file modifies an existing widget" do + let(:update_file) { "Test_Widget_update.yml" } + let(:source) { "#{data_dir}/#{update_file}" } + + before do + TaskHelpers::Imports::Widgets.new.import(:source => "#{data_dir}/#{widget_file1}") + end + + it 'overwrites an existing widget' do + expect do + TaskHelpers::Imports::Widgets.new.import(options) + end.to_not output.to_stderr + assert_test_widget_one_mod + end + end + + describe "when the source file has invalid settings" do + context "when the object type is invalid" do + let(:source) { "#{data_dir}/#{runt_err_file}" } + + it 'generates an error' do + expect do + TaskHelpers::Imports::Widgets.new.import(options) + end.to output(/Incorrect format/).to_stderr + end + end + + context "when an attribute is invalid" do + let(:source) { "#{data_dir}/#{attr_err_file}" } + + it 'generates an error' do + expect do + TaskHelpers::Imports::Widgets.new.import(options) + end.to output(/unknown attribute 'invalid_title'/).to_stderr + end + end + end + end + + def assert_test_widget_one_present + widget = MiqWidget.find_by(:name => widget_name1) + expect(widget.title).to eq(widget_title1) + expect(widget.options[:col_order]).to eq(widget_cols1) + expect(widget.options[:col_order]).to_not include("storage.name") + end + + def assert_test_widget_two_present + widget = MiqWidget.find_by(:name => widget_name2) + expect(widget.title).to eq(widget_title2) + expect(widget.options[:col_order]).to eq(widget_cols2) + end + + def assert_test_widget_one_mod + widget = MiqWidget.find_by(:name => widget_name1) + expect(widget.title).to include("Modified") + expect(widget.options[:col_order]).to include("storage.name") + end +end