// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. (function($) { module('List view', { setup: function() { $.fx.off = true; cloudStack.debug = true; }, teardown: function() { // Cleanup notification box $('.notification-box').remove(); } }); test('Basic', function() { var listView = { listView: { section: 'test123', fields: { fieldA: { label: 'testFieldA' }, fieldB: { label: 'testFieldB' } }, dataProvider: function(args) { args.response.success({ data: [] }); } } }; var $listView; ok($listView = $('
').listView(listView), 'Initialize list view'); equal($listView.find('.list-view').size(), 1, 'List view has container div'); equal($listView.find('.list-view.test123').size(), 1, 'Container div has section ID as CSS class'); equal($listView.find('.list-view table').size(), 2, 'List view has split tables'); equal($listView.find('.list-view .fixed-header table thead tr').size(), 1, 'List view has fixed table header'); equal($listView.find('.list-view .fixed-header table thead th').size(), 2, 'List view has correct column headers'); equal($listView.find('.list-view .fixed-header table thead th:first').html(), 'testFieldA', 'First header has correct label'); ok($listView.find('.list-view .fixed-header table thead th:first').hasClass('fieldA'), 'First header has correct class'); ok($listView.find('.list-view .fixed-header table thead th:last').hasClass('fieldB'), 'First header has correct class'); equal($listView.find('.list-view .fixed-header table thead th:last').html(), 'testFieldB', 'First header has correct label'); equal($listView.find('.list-view table tbody tr').size(), 1, 'List view has table body'); equal($listView.find('.toolbar').size(), 1, 'List view has toolbar'); equal($listView.find('.toolbar .text-search .search-bar input[type=text]').size(), 1, 'Toolbar has search box'); equal($listView.find('.toolbar .text-search .search-bar input[type=text]').size(), 1, 'Toolbar has search box'); equal($listView.find('.toolbar .text-search .button.search').size(), 1, 'Toolbar has search button'); ok(!$listView.find('.toolbar .filters').size(), 'Toolbar doesn\'t have filters'); }); test('Data provider', function() { var $listView = $('
'); stop(); $listView.listView({ context: { tests: [] }, listView: { section: 'test', fields: { fieldA: { label: 'testFieldA' }, fieldB: { label: 'testFieldB' } }, dataProvider: function(args) { start(); equal(args.page, 1, 'Correct page # passed'); equal(args.filterBy.search.value, '', 'No search params specified'); ok($.isArray(args.context.tests), 'Context passed'); args.response.success({ data: [ { fieldA: '1A', fieldB: '1B' }, { fieldA: '2A', fieldB: '2B' }, { fieldA: '3A', fieldB: '3B' } ] }); stop(); setTimeout(function() { start(); equal($listView.find('tbody tr').size(), 3, 'Data row count is correct'); equal($listView.find('tbody tr:first td.fieldA span').html(), '1A', 'Correct table item value for first row'); equal($listView.find('tbody tr:first td.fieldB span').html(), '1B', 'Correct table item value for first row'); equal($listView.find('tbody tr:last td.fieldA span').html(), '3A', 'Correct table item value for last row'); equal($listView.find('tbody tr:last td.fieldB span').html(), '3B', 'Correct table item value for last row'); }); } } }); }); test('Pre-filter', function() { var $listView = $('
'); stop(); $listView.listView({ listView: { section: 'test', fields: { fieldA: { label: 'testFieldA' }, fieldB: { label: 'testFieldB' }, fieldC: { label: 'testFieldC' } }, preFilter: function(args) { return ['fieldC']; }, dataProvider: function(args) { args.response.success({ data: [ { fieldA: '1A', fieldB: '1B', fieldC: '1C' } ] }); setTimeout(function() { start(); equal($listView.find('thead th').size(), 2, 'Correct # of filtered columns present'); equal($listView.find('tbody tr:first td').size(), 2, 'Correct # of body columns present'); equal($listView.find('tbody tr:first td.fieldA span').html(), '1A', 'Correct table item value for data row'); equal($listView.find('tbody tr:first td.fieldB span').html(), '1B', 'Correct table item value for data row'); }); } } }); }); test('Section select', function() { var $listView = $('
'); ok($listView.listView({ sectionSelect: { label: 'testSectionSelect' }, sections: { sectionA: { type: 'select', title: 'sectionA', listView: { fields: { fieldA: { label: 'testFieldA' }, fieldB: { label: 'testFieldB' } }, dataProvider: function(args) { args.response.success({ data: [ { fieldA: '1A', fieldB: '1B' }, { fieldA: '2A', fieldB: '2B' } ] }); } } }, sectionB: { type: 'select', title: 'sectionB', listView: { fields: { fieldC: { label: 'testFieldC' }, fieldD: { label: 'testFieldD' }, fieldE: { label: 'testFieldE' } }, dataProvider: function(args) { args.response.success({ data: [ { fieldC: '1C', fieldD: '1D', fieldE: '1E' }, { fieldC: '2C', fieldD: '2D', fieldE: '2E' }, { fieldC: '3C', fieldD: '3D', fieldE: '3E' }, { fieldC: '4C', fieldD: '4D', fieldE: '4E' } ] }); } } } } })); equal($listView.find('.toolbar .section-switcher').size(), 1, 'Section switcher present in toolbar'); equal($listView.find('.toolbar .section-switcher .section-select select').size(), 1, 'Section select present'); equal($listView.find('.toolbar .section-switcher .section-select label').html(), 'testSectionSelect' + ':', 'Section select label is correct'); equal($listView.find('.toolbar .section-switcher .section-select select option').size(), 2, 'Selectable sections present as options'); equal($listView.find('.toolbar .section-switcher .section-select select option:first').html(), 'sectionA', 'First select has correct title'); equal($listView.find('.toolbar .section-switcher .section-select select option:selected')[0], $listView.find('.toolbar .section-switcher .section-select select option:first')[0], 'First section option is selected by default'); equal($listView.find('.toolbar .section-switcher .section-select select option:last').html(), 'sectionB', 'Last select has correct title'); equal($listView.find('.list-view thead th').size(), 2, 'Correct list view column count present'); equal($listView.find('.list-view thead th:first').html(), 'testFieldA', 'Column 1 is labeled correctly'); equal($listView.find('.list-view thead th:last').html(), 'testFieldB', 'Column 2 is labeled correctly'); equal($listView.find('.list-view tbody tr').size(), 2, 'Correct # of data rows present'); equal($listView.find('.list-view tbody tr:first td').size(), 2, 'Correct # of data columns present'); $listView.find('.toolbar .section-switcher select').val('sectionB'); stop(); ok($listView.find('.toolbar .section-switcher select').change(), 'Change section'); start(); equal($listView.find('.list-view thead th').size(), 3, 'Correct list view column count present'); equal($listView.find('.list-view thead th:first').html(), 'testFieldC', 'Column 1 is labeled correctly'); equal($($listView.find('.list-view thead th')[1]).html(), 'testFieldD', 'Column 2 is labeled correctly'); equal($listView.find('.list-view thead th:last').html(), 'testFieldE', 'Column 3 is labeled correctly'); equal($listView.find('.list-view tbody tr').size(), 4, 'Correct # of data rows present'); equal($listView.find('.list-view tbody tr:first td').size(), 3, 'Correct # of data columns present'); equal($listView.find('.list-view tbody tr:first td:first span').html(), '1C', 'First table cell has correct data'); equal($listView.find('.list-view tbody tr:last td:last span').html(), '4E', 'Last table cell has correct data'); $listView.find('.toolbar .section-switcher select').val('sectionA'); stop(); ok($listView.find('.toolbar .section-switcher select').change(), 'Change section'); start(); equal($listView.find('.toolbar .section-switcher .section-select select option:last').html(), 'sectionB', 'Last select has correct title'); equal($listView.find('.list-view thead th').size(), 2, 'Correct list view column count present'); equal($listView.find('.list-view thead th:first').html(), 'testFieldA', 'Column 1 is labeled correctly'); equal($listView.find('.list-view thead th:last').html(), 'testFieldB', 'Column 2 is labeled correctly'); equal($listView.find('.list-view tbody tr').size(), 2, 'Correct # of data rows present'); equal($listView.find('.list-view tbody tr:first td').size(), 2, 'Correct # of data columns present'); equal($listView.find('.list-view tbody tr:first td:first span').html(), '1A', 'First table cell has correct data'); equal($listView.find('.list-view tbody tr:last td:last span').html(), '2B', 'Last table cell has correct data'); }); test('Basic sync action', function() { var $cloudStack = $('
').appendTo('#qunit-fixture'); var listView = { listView: { section: 'test123', fields: { fieldA: { label: 'testFieldA' }, fieldB: { label: 'testFieldB' } }, actions: { basicSync: { label: 'basicAction', messages: { confirm: function() { return 'basicActionConfirm'; }, notification: function() { return 'basicActionNotification'; } }, action: function(args) { args.response.success(); } } }, dataProvider: function(args) { args.response.success({ data: [ { fieldA: '1A', fieldB: '1B', fieldC: '1C' }, { fieldA: '2A', fieldB: '2B', fieldC: '2C' } ] }); } } }; // CloudStack object is needed for notification handling for actions var cloudStack = { sections: { testActions: listView }, home: 'testActions' }; ok($cloudStack.cloudStack(cloudStack), 'Initialize cloudStack widget w/ list view'); var $listView = $cloudStack.find('.list-view'); equal($listView.find('table thead th').size(), 3, 'Correct header column count'); equal($listView.find('table thead th.actions').size(), 1, 'Action header column present'); equal($listView.find('table tbody tr:first td').size(), 3, 'Correct data column count'); equal($listView.find('table tbody tr:first td.actions').size(), 1, 'Action data column present'); equal($listView.find('table tbody tr:first td.actions .action').size(), 1, 'Correct action count'); equal($listView.find('table tbody tr:first td.actions .action:first .icon').size(), 1, 'Action has icon'); ok($listView.find('table tbody tr:first td.actions .action:first').hasClass('basicSync'), 'First action has ID as CSS class'); ok($listView.find('td.actions .action:first').click(), 'Click first action'); equal($('.ui-dialog.confirm .message').html(), 'basicActionConfirm', 'Confirmation message present'); // Make sure dialog is cleaned up -- always put below all confirm tests $(':ui-dialog, .overlay').appendTo('#qunit-fixture'); ok($('.ui-dialog.confirm .ui-button.ok').click(), 'Confirm action'); equal($cloudStack.find('.notifications .total span').html(), '1', 'Notification total increased'); equal($('.notification-box ul li').size(), 1, 'Notification list has 1 item'); equal($('.notification-box ul li span').html(), 'basicActionNotification', 'Notification list item has correct label'); ok($('.notification-box ul li').hasClass('pending'), 'Notification has pending state'); stop(); setTimeout(function() { start(); ok(!$('.notification-box ul li').hasClass('pending'), 'Notification has completed state'); }); }); test('Async action', function() { var $cloudStack = $('
').appendTo('#qunit-fixture'); var listView = { listView: { id: 'testAsyncListView', fields: { fieldA: { label: 'testFieldA' }, fieldB: { label: 'testFieldB' } }, actions: { asyncTest: { label: 'asyncAction', messages: { confirm: function() { return 'asyncActionConfirm'; }, notification: function() { return 'asyncActionNotification'; } }, action: function(args) { ok($.isArray(args.context.testAsyncListView), 'List view context passed'); args.response.success({ _custom: { jobId: 'job123' } }); }, notification: { interval: 0, poll: function(args) { start(); equal(args._custom.jobId, 'job123', 'Job ID passed correctly'); args.complete(); } } } }, dataProvider: function(args) { args.response.success({ data: [ { fieldA: '1A', fieldB: '1B', fieldC: '1C' }, { fieldA: '2A', fieldB: '2B', fieldC: '2C' } ] }); } } }; // CloudStack object is needed for notification handling for actions var cloudStack = { sections: { testActions: listView }, home: 'testActions' }; ok($cloudStack.cloudStack(cloudStack), 'Initialize cloudStack widget w/ list view'); var $listView = $cloudStack.find('.list-view'); equal($listView.find('table thead th').size(), 3, 'Correct header column count'); equal($listView.find('table thead th.actions').size(), 1, 'Action header column present'); equal($listView.find('table tbody tr:first td').size(), 3, 'Correct data column count'); equal($listView.find('table tbody tr:first td.actions').size(), 1, 'Action data column present'); equal($listView.find('table tbody tr:first td.actions .action').size(), 1, 'Correct action count'); equal($listView.find('table tbody tr:first td.actions .action:first .icon').size(), 1, 'Action has icon'); ok($listView.find('table tbody tr:first td.actions .action:first').hasClass('asyncTest'), 'First action has ID as CSS class'); ok($listView.find('td.actions .action:first').click(), 'Click first action'); equal($('.ui-dialog.confirm .message').html(), 'asyncActionConfirm', 'Confirmation message present'); // Make sure dialog is cleaned up -- always put below all confirm tests $(':ui-dialog, .overlay').appendTo('#qunit-fixture'); ok($('.ui-dialog.confirm .ui-button.ok').click(), 'Confirm action'); equal($cloudStack.find('.notifications .total span').html(), '1', 'Notification total increased'); equal($('.notification-box ul li').size(), 1, 'Notification list has 1 item'); equal($('.notification-box ul li span').html(), 'asyncActionNotification', 'Notification list item has correct label'); ok($('.notification-box ul li').hasClass('pending'), 'Notification has pending state'); stop(); }); test('Action filter', function() { var $cloudStack = $('
').appendTo('#qunit-fixture'); var listView = { listView: { id: 'testAsyncListView', fields: { fieldA: { label: 'testFieldA' }, fieldB: { label: 'testFieldB' } }, actions: { actionA: { label: 'actionA', messages: { confirm: function() { return ''; }, notification: function() { return ''; } }, action: function(args) { args.response.success(); } }, actionB: { label: 'actionB [HIDDEN]', messages: { confirm: function() { return ''; }, notification: function() { return ''; } }, action: function(args) { args.response.success(); } }, actionC: { label: 'actionC', messages: { confirm: function() { return ''; }, notification: function() { return ''; } }, action: function(args) { args.response.success(); } } }, dataProvider: function(args) { args.response.success({ actionFilter: function() { return ['actionA', 'actionC']; }, data: [ { fieldA: '1A', fieldB: '1B', fieldC: '1C' }, { fieldA: '2A', fieldB: '2B', fieldC: '2C' } ] }); } } }; // CloudStack object is needed for notification handling for actions var cloudStack = { sections: { testActions: listView }, home: 'testActions' }; ok($cloudStack.cloudStack(cloudStack), 'Initialize cloudStack widget w/ list view'); var $listView = $cloudStack.find('.list-view'); equal($listView.find('table thead th').size(), 3, 'Correct header column count'); equal($listView.find('table thead th.actions').size(), 1, 'Action header column present'); equal($listView.find('table tbody tr:first td.actions').size(), 1, 'Action data column present'); equal($listView.find('table tbody tr:first td.actions .action').size(), 3, 'Correct action count (all)'); equal($listView.find('table tbody tr:first td.actions .action:not(.disabled)').size(), 2, 'Correct action count (enabled)'); ok($listView.find('table tbody tr:first td.actions .action:first').hasClass('actionA'), 'First action has correct ID'); ok($listView.find('table tbody tr:first td.actions .action:last').hasClass('actionC'), 'Last action has correct ID'); }); }(jQuery));