diff --git a/.travis.yml b/.travis.yml index 30b21ecf18..1551f17ec5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,6 +43,7 @@ before_script: - cd ~/frappe-bench - bench use test_site - bench reinstall --yes + - bench setup-help - bench scheduler disable - bench start & - sleep 10 diff --git a/frappe/__init__.py b/frappe/__init__.py index 9b31e15fee..103217f5c7 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -14,7 +14,7 @@ import os, sys, importlib, inspect, json from .exceptions import * from .utils.jinja import get_jenv, get_template, render_template, get_email_from_template -__version__ = '8.4.0' +__version__ = '8.4.1' __title__ = "Frappe Framework" local = Local() diff --git a/frappe/public/css/mobile.css b/frappe/public/css/mobile.css index 5d954a9ce2..9e0bbe3597 100644 --- a/frappe/public/css/mobile.css +++ b/frappe/public/css/mobile.css @@ -192,6 +192,15 @@ body { } } @media (max-width: 767px) { + body[data-route^="Form"] .page-title .title-text { + font-size: 16px; + width: calc(100% - 30px); + } + body[data-route^="Form"] .page-title .indicator { + float: left; + margin-top: 10px; + margin-right: 5px; + } .modal .modal-dialog { margin: 0px; padding: 0px; diff --git a/frappe/public/css/page.css b/frappe/public/css/page.css index a7e89d00cd..f5ccdc5a6a 100644 --- a/frappe/public/css/page.css +++ b/frappe/public/css/page.css @@ -146,6 +146,9 @@ select.input-sm { right: 101px; width: 100%; } + .page-title h1 { + padding-right: 170px; + } .page-head .page-title h1 { font-size: 18px; } diff --git a/frappe/public/js/frappe/form/save.js b/frappe/public/js/frappe/form/save.js index 46bf42b421..2826c0e6ec 100644 --- a/frappe/public/js/frappe/form/save.js +++ b/frappe/public/js/frappe/form/save.js @@ -168,7 +168,6 @@ frappe.ui.form.save = function (frm, action, callback, btn) { // callback: callback, // btn: btn // } - $(opts.btn).prop("disabled", true); if (frappe.ui.form.is_saving) { // this is likely to happen if the user presses the shortcut cmd+s for a longer duration or uses double click @@ -190,6 +189,7 @@ frappe.ui.form.save = function (frm, action, callback, btn) { opts.callback && opts.callback(r); }, always: function (r) { + $(btn).prop("disabled", false); frappe.ui.form.is_saving = false; if (r) { var doc = r.docs && r.docs[0]; diff --git a/frappe/public/js/frappe/form/toolbar.js b/frappe/public/js/frappe/form/toolbar.js index a428e0db07..5357f7160c 100644 --- a/frappe/public/js/frappe/form/toolbar.js +++ b/frappe/public/js/frappe/form/toolbar.js @@ -290,16 +290,16 @@ frappe.ui.form.Toolbar = Class.extend({ } else { var click = { "Save": function() { - me.frm.save('Save', null, this); + return me.frm.save('Save', null, this); }, "Submit": function() { - me.frm.savesubmit(this); + return me.frm.savesubmit(this); }, "Update": function() { - me.frm.save('Update', null, this); + return me.frm.save('Update', null, this); }, "Amend": function() { - me.frm.amend_doc(); + return me.frm.amend_doc(); } }[status]; diff --git a/frappe/public/js/frappe/request.js b/frappe/public/js/frappe/request.js index e2a894af7b..1100d86f26 100644 --- a/frappe/public/js/frappe/request.js +++ b/frappe/public/js/frappe/request.js @@ -239,7 +239,9 @@ frappe.request.prepare = function(opts) { frappe.request.cleanup = function(opts, r) { // stop button indicator - if(opts.btn) $(opts.btn).prop("disabled", false); + if(opts.btn) { + $(opts.btn).prop("disabled", false); + } $("body").attr("data-ajax-state", "complete"); diff --git a/frappe/public/js/frappe/ui/page.js b/frappe/public/js/frappe/ui/page.js index 3bb47bcae1..714d474ae5 100644 --- a/frappe/public/js/frappe/ui/page.js +++ b/frappe/public/js/frappe/ui/page.js @@ -95,7 +95,7 @@ frappe.ui.Page = Class.extend({ }, set_indicator: function(label, color) { - this.clear_indicator().removeClass("hide").html(label).addClass(color); + this.clear_indicator().removeClass("hide").html(``).addClass(color); }, add_action_icon: function(icon, click) { diff --git a/frappe/public/js/legacy/form.js b/frappe/public/js/legacy/form.js index f0deb6ba01..416a7a1f17 100644 --- a/frappe/public/js/legacy/form.js +++ b/frappe/public/js/legacy/form.js @@ -698,7 +698,7 @@ _f.Frm.prototype.save = function(save_action, callback, btn, on_error) { _f.Frm.prototype._save = function(save_action, callback, btn, on_error, resolve) { var me = this; if(!save_action) save_action = "Save"; - this.validate_form_action(save_action); + this.validate_form_action(save_action, resolve); if((!this.meta.in_dialog || this.in_form) && !this.meta.istable) { frappe.utils.scroll_to(0); @@ -712,8 +712,9 @@ _f.Frm.prototype._save = function(save_action, callback, btn, on_error, resolve) me.script_manager.trigger("after_save"); me.refresh(); } else { - if(on_error) + if(on_error) { on_error(); + } } callback && callback(r); resolve(); @@ -722,23 +723,22 @@ _f.Frm.prototype._save = function(save_action, callback, btn, on_error, resolve) if(save_action != "Update") { // validate frappe.validated = true; - Promise.all([ - this.script_manager.trigger("validate"), - this.script_manager.trigger("before_save") - ]).then(() => { - // done is called after all ajaxes in validate & before_save are completed :) - - if(!frappe.validated) { - btn && $(btn).prop("disabled", false); - if(on_error) { - on_error(); + frappe.run_serially([ + () => this.script_manager.trigger("validate"), + () => this.script_manager.trigger("before_save"), + () => { + if(!frappe.validated) { + btn && $(btn).prop("disabled", false); + if(on_error) { + on_error(); + } + resolve(); + return; } - resolve(); - return; - } - frappe.ui.form.save(me, save_action, after_save, btn); - }); + frappe.ui.form.save(me, save_action, after_save, btn); + } + ]); } else { frappe.ui.form.save(me, save_action, after_save, btn); } @@ -757,7 +757,7 @@ _f.Frm.prototype.savesubmit = function(btn, callback, on_error) { return; } - me.save('Submit', function(r) { + return me.save('Submit', function(r) { if(!r.exc) { frappe.utils.play_sound("submit"); callback && callback(); @@ -929,7 +929,7 @@ _f.Frm.prototype.action_perm_type_map = { "Delete": "delete" }; -_f.Frm.prototype.validate_form_action = function(action) { +_f.Frm.prototype.validate_form_action = function(action, resolve) { var perm_to_check = this.action_perm_type_map[action]; var allowed_for_workflow = false; var perms = frappe.perm.get_perm(this.doc.doctype)[0]; @@ -943,6 +943,10 @@ _f.Frm.prototype.validate_form_action = function(action) { } if (!this.perm[0][perm_to_check] && !allowed_for_workflow) { + if(resolve) { + // re-enable buttons + resolve(); + } frappe.throw (__("No permission to '{0}' {1}", [__(action), __(this.doc.doctype)])); } }; diff --git a/frappe/public/less/mobile.less b/frappe/public/less/mobile.less index 60fa2b4f6f..06a3a26a15 100644 --- a/frappe/public/less/mobile.less +++ b/frappe/public/less/mobile.less @@ -223,6 +223,19 @@ body { } @media(max-width: 767px) { + body[data-route^="Form"]{ + .page-title { + .title-text { + font-size: 16px; + width: calc(~"100% - 30px"); + } + .indicator { + float: left; + margin-top: 10px; + margin-right: 5px; + } + } + } .modal { .modal-dialog { margin: 0px; diff --git a/frappe/public/less/page.less b/frappe/public/less/page.less index 11aef785a5..d141c5bc13 100644 --- a/frappe/public/less/page.less +++ b/frappe/public/less/page.less @@ -178,6 +178,9 @@ select.input-sm { left: 0; right: 101px; width: 100%; + h1 { + padding-right: 170px; + } } .page-head .page-title h1 { diff --git a/frappe/tests/ui/_test_gantt_view.js b/frappe/tests/ui/_test_gantt_view.js index 0ec6c38ff2..a94c40338a 100644 --- a/frappe/tests/ui/_test_gantt_view.js +++ b/frappe/tests/ui/_test_gantt_view.js @@ -6,16 +6,19 @@ QUnit.test("Gantt View Tests", function(assert) { let random_text = frappe.utils.get_random(10); let start_date = frappe.datetime.get_today()+" 16:20:35"; // arbitrary value taken to prevent cases like 12a for 12:00am and 12h to 24h conversion let end_date = frappe.datetime.get_today()+" 18:30:45"; //arbitrary value taken to prevent cases like 12a for 12:00am and 12h to 24h conversion - let event_id = () => { + let event_id = (text) => { // Method to acquire the ID of the event created. This is needed to redirect to the event page - let event_label = $('.bar-label').text(); - let init = event_label.indexOf('('); - let fin = event_label.indexOf(')'); - return (event_label.substr(init+1,fin-init-1)); + $('.bar-label').each(function() { + if ($(this).text().includes(text)){ + let init = $(this).text().indexOf('('); + let fin = $(this).text().indexOf(')'); + return ($(this).text().substr(init+1,fin-init-1)); + } + }); }; - let event_title_text = (text) => { + let event_title_text = () => { // Method to check the name of the event created. This is needed to verify the creation and deletion of the event - return $('#bar > g > g.bar-group > text:visible').text().includes(text); + return $('#bar > g > g.bar-group > text:visible').text(); }; frappe.run_serially([ @@ -31,22 +34,26 @@ QUnit.test("Gantt View Tests", function(assert) { // Check if event is created () => frappe.set_route(["List", "Event", "Gantt"]), - () => assert.ok( event_title_text(random_text) ), + () => frappe.tests.click_page_head_item("Refresh"), + () => frappe.timeout(1), + () => assert.ok(event_title_text().includes(random_text), "Event title verified"), // Delete event () => frappe.set_route(["List", "Event", "Gantt"]), - () => frappe.timeout(0.3), + () => frappe.timeout(1), // Redirect to the event page to delete the event - () => frappe.set_route(["Form", "Event", event_id()]), + () => frappe.set_route(["Form", "Event", event_id(random_text)]), () => frappe.tests.click_page_head_item('Menu'), () => frappe.tests.click_dropdown_item('Delete'), () => frappe.tests.click_page_head_item('Yes'), - () => frappe.timeout(0.3), + () => frappe.timeout(1), () => frappe.set_route(["List", "Event", "Gantt"]), - () => frappe.timeout(0.3), + () => frappe.timeout(1), // Check if event is deleted - () => assert.ok( event_title_text("") ), + () => frappe.tests.click_page_head_item("Refresh"), + () => frappe.timeout(1), + () => assert.notOk(event_title_text().includes(random_text), "Event deleted"), () => done() ]); diff --git a/frappe/tests/ui/_test_print_format_builder.js b/frappe/tests/ui/_test_print_format_builder.js new file mode 100644 index 0000000000..f78b824cea --- /dev/null +++ b/frappe/tests/ui/_test_print_format_builder.js @@ -0,0 +1,56 @@ +QUnit.module('views'); + +QUnit.only("Print Format Builder", function(assert) { + assert.expect(0); + let random_text = frappe.utils.get_random(10); + let done = assert.async(); + let click_custoize = () => { + return $(`.btn-print-edit`).click(); + }; + + frappe.run_serially([ + () => frappe.set_route(["List", "ToDo", "List"]), + () => frappe.timeout(0.3), + + () => frappe.new_doc('ToDo'), + () => frappe.quick_entry.dialog.set_value('description', random_text), + () => frappe.quick_entry.insert(), + () => frappe.timeout(0.5), + + () => frappe.tests.click_page_head_item('Refresh'), + () => frappe.timeout(0.3), + + () => frappe.tests.click_generic_text(random_text), + () => frappe.tests.click_print_logo(), + + () => click_custoize(), + () => frappe.timeout(1), + () => $(`div.control-input > input:visible`).val('custom_todo'), + () => frappe.timeout(0.3), + + () => frappe.tests.click_generic_text('Start', 'button'), + () => frappe.timeout(1), + + () => frappe.tests.click_page_head_item('Save'), + + () => frappe.tests.click_generic_text('Edit Properties', 'button'), + () => frappe.tests.click_page_head_item('Menu'), + () => frappe.tests.click_dropdown_item('Delete'), + () => frappe.tests.click_page_head_item('Yes'), + () => frappe.timeout(1), + + () => frappe.set_route(["List", "ToDo", "List"]), + () => frappe.timeout(0.3), + () => frappe.tests.click_generic_text(random_text), + () => frappe.tests.click_page_head_item('Menu'), + () => frappe.tests.click_dropdown_item('Delete'), + () => frappe.tests.click_page_head_item('Yes'), + + () => frappe.timeout(2), + + () => done() + ]); +}); + + +// when you do a pull, hopefully after a merge ... make sure edit the options method in global_search \ No newline at end of file diff --git a/frappe/tests/ui/data/test_data_for_views.js b/frappe/tests/ui/data/test_data_for_views.js new file mode 100644 index 0000000000..05c400c3b3 --- /dev/null +++ b/frappe/tests/ui/data/test_data_for_views.js @@ -0,0 +1,16 @@ +$.extend(frappe.test_data, { + 'User': { + 'user1@mail.com': [ + {first_name: 'User 1'}, + {email: 'user1@mail.com'}, + {send_welcome_email: 0} + ] + }, + 'Kanban Board': { + 'kanban 1': [ + {kanban_board_name: 'kanban 1'}, + {reference_doctype: 'ToDo'}, + {field_name: 'status'} + ] + } +}); \ No newline at end of file diff --git a/frappe/tests/ui/data/test_lib.js b/frappe/tests/ui/data/test_lib.js index 5023eb451d..baeb420ce7 100644 --- a/frappe/tests/ui/data/test_lib.js +++ b/frappe/tests/ui/data/test_lib.js @@ -8,8 +8,14 @@ frappe.tests = { () => frappe.set_route('List', doctype), () => frappe.new_doc(doctype), () => { - let frm = frappe.quick_entry ? frappe.quick_entry.dialog : cur_frm; - return frappe.tests.set_form_values(frm, data); + if (frappe.quick_entry) + { + frappe.quick_entry.dialog.$wrapper.find('.edit-full').click(); + return frappe.timeout(1); + } + }, + () => { + return frappe.tests.set_form_values(cur_frm, data); }, () => frappe.timeout(1), () => (frappe.quick_entry ? frappe.quick_entry.insert() : cur_frm.save()) @@ -90,7 +96,57 @@ frappe.tests = { }); return frappe.run_serially(tasks); - } + }]); + }, + click_and_wait: (button, obj=0.1) => { + return frappe.run_serially([ + () => { + //check if obj value is passed + if (obj == 0.1) + $(button).click(); + else + $(button)[obj].click(); + }, + () => frappe.timeout(0.5) + ]); + }, + create_todo: (todo_needed) => { + let status_list = ['Closed', 'Open']; + let priority_list = ['Low', 'Medium', 'High']; + let date_list = ['2017-05-05', '2017-06-06', '2017-07-07', '2017-08-08']; + let owner_list = ['Administrator', 'user1@mail.com']; + let i; + let num_of_todo; + let tasks = []; + + return frappe.run_serially([ + () => frappe.set_route('List', 'ToDo', 'List'), + () => { + //remove todo filters + for (i=1; i<=5; i++) + $('.col-md-2:nth-child('+i+') .input-sm').val(''); + }, + () => cur_list.page.btn_secondary.click(), + () => frappe.timeout(0.5), + () => num_of_todo = cur_list.data.length,//todo present + () => { + if (num_of_todo < todo_needed) + { + for (i=0; i<(todo_needed-num_of_todo); i+=1) + { + tasks.push(() => frappe.tests.make("ToDo", [ + {description: 'ToDo for testing'}, + {status: status_list[i%2]}, + {priority: priority_list[i%3]}, + {date: date_list[i%4]}, + {owner: owner_list[i%2]} + ])); + tasks.push(() => i+=1); + } + i=0; + } + }, + () => frappe.run_serially(tasks) ]); }, click_page_head_item: (text) => { @@ -99,24 +155,24 @@ frappe.tests = { return frappe.run_serially([ () => { if (text == "Menu"){ - $("span.menu-btn-group-label:contains('Menu'):visible").click(); + $(`span.menu-btn-group-label:contains('Menu'):visible`).click(); } else if (text == "Refresh") { - $(".btn-secondary:contains('Refresh'):visible").click(); + $(`.btn-secondary:contains('Refresh'):visible`).click(); } else if (possible_texts.includes(text)) { - $(".btn-primary:contains("+text+"):visible").click(); + $(`.btn-primary:contains("${text}"):visible`).click(); } }, - () => frappe.timeout(0.3) + () => frappe.timeout(1) ]); }, click_dropdown_item: (text) => { // Method to click dropdown elements return frappe.run_serially([ () => { - let li = $(".dropdown-menu li:contains("+text+"):visible").get(0); - $(li).find('a')[0].click(); + let li = $(`.dropdown-menu li:contains("${text}"):visible`).get(0); + $(li).find(`a`)[0].click(); }, - () => frappe.timeout(0.3) + () => frappe.timeout(1) ]); }, click_navbar_item: (text) => { @@ -124,45 +180,55 @@ frappe.tests = { return frappe.run_serially([ () => { if (text == "Help"){ - $(".dropdown-help .dropdown-toggle:visible").click(); + $(`.dropdown-help .dropdown-toggle:visible`).click(); } else if (text == "navbar_user"){ - $(".dropdown-navbar-user .dropdown-toggle:visible").click(); + $(`.dropdown-navbar-user .dropdown-toggle:visible`).click(); } else if (text == "Notification"){ - $(".navbar-new-comments").click(); + $(`.navbar-new-comments`).click(); } else if (text == "Home"){ - $(".navbar-home:contains('Home'):visible")[0].click(); + $(`.navbar-home:contains('Home'):visible`)[0].click(); } }, - () => frappe.timeout(0.3) + () => frappe.timeout(1) ]); }, click_generic_text: (text, tag='a') => { // Method to click an element by its name return frappe.run_serially([ - () => $(tag+":contains("+text+"):visible")[0].click(), - () => frappe.timeout(0.3) + () => $(`${tag}:contains("${text}"):visible`)[0].click(), + () => frappe.timeout(1) ]); }, click_desktop_icon: (text) => { // Method to click the desktop icons on the Desk, by their name return frappe.run_serially([ () => $("#icon-grid > div > div.app-icon[title="+text+"]").click(), - () => frappe.timeout(0.3) + () => frappe.timeout(1) ]); }, is_visible: (text, tag='a') => { // Method to check the visibility of an element - return $(tag+":contains("+text+")").is(':visible'); + return $(`${tag}:contains("${text}")`).is(`:visible`); + }, + close_modal: () => { + // Close the modal on the screen + $(`a.close`).click(); + }, + click_print_logo: () => { + return frappe.run_serially([ + () => $(`.fa-print`).click(), + () => frappe.timeout(1) + ]); }, click_button: function(text) { $(`.btn:contains("${text}"):visible`).click(); - return frappe.timeout(0.3); + return frappe.timeout(1); }, click_link: function(text) { $(`a:contains("${text}"):visible`).click(); - return frappe.timeout(0.3); + return frappe.timeout(1); } -}; +}; \ No newline at end of file diff --git a/frappe/tests/ui/global_search/_test_list_document.js b/frappe/tests/ui/global_search/_test_list_document.js new file mode 100644 index 0000000000..2da91b01aa --- /dev/null +++ b/frappe/tests/ui/global_search/_test_list_document.js @@ -0,0 +1,44 @@ +QUnit.module('views'); + +QUnit.test("List a document type", function(assert) { + assert.expect(2); + let done = assert.async(); + let option_number=0; + let awesome_search = $('#navbar-search').get(0); + let options = () => { + // Method to return the available options after search + return $('body > div.main-section > header > div > div > div.hidden-xs > form > div > div > ul > li').each(function (){}); + }; + + frappe.run_serially([ + // Goto Home using button click to check if its working + () => frappe.set_route(), + () => frappe.timeout(1), + + () => $('#navbar-search').focus(), + () => $('#navbar-search').val('customer'), + () => $('#navbar-search').focus(), + () => frappe.timeout(1), + () => { + assert.ok(frappe.tests.is_visible("Search for 'customer'"), "'Search for 'customer'' is visible!"); + if (frappe.tests.is_visible("Search for 'customer'")){ + let search_options = options(); + // Iterate over all available options till you reach "Search for 'customer'"" + for (option_number=0; option_number awesome_search.awesomplete.goto(option_number), + // Click the highlighted option + () => awesome_search.awesomplete.select(), + () => frappe.timeout(1), + // Verify if the modal is correct + () => assert.ok(frappe.tests.is_visible('Customer will have a table tabCustomer associated with it', 'p'), "Correct modal for 'customer' was called"), + () => frappe.timeout(1), + () => frappe.tests.close_modal(), + + () => done() + ]); +}); \ No newline at end of file diff --git a/frappe/tests/ui/global_search/_test_math.js b/frappe/tests/ui/global_search/_test_math.js new file mode 100644 index 0000000000..08f857ab15 --- /dev/null +++ b/frappe/tests/ui/global_search/_test_math.js @@ -0,0 +1,64 @@ +QUnit.module('views'); + +QUnit.test("Math Calculations", function(assert) { + assert.expect(5); + let done = assert.async(); + let awesome_search = $('#navbar-search').get(0); + let random_number_1 = (Math.random()*100).toFixed(2); + let random_number_2 = (Math.random()*100).toFixed(2); + let operations = ['+', '-', '/', '*']; + + frappe.run_serially([ + // Goto Home using button click to check if its working + () => frappe.set_route(), + () => frappe.timeout(1), + + () => $('#navbar-search').focus(), + () => $('#navbar-search').val(random_number_1+operations[0]+random_number_2), + () => $('#navbar-search').focus(), + () => frappe.timeout(1), + () => { + let num_str = random_number_1+operations[0]+random_number_2+' = '+parseFloat(parseFloat(random_number_1)+parseFloat(random_number_2)); + assert.ok(frappe.tests.is_visible(num_str), "Math operation '"+operations[0]+"' is correct"); + }, + + () => $('#navbar-search').focus(), + () => $('#navbar-search').val(random_number_1+operations[1]+random_number_2), + () => $('#navbar-search').focus(), + () => frappe.timeout(1), + () => { + let num_str = random_number_1+operations[1]+random_number_2+' = '+parseFloat(parseFloat(random_number_1)-parseFloat(random_number_2)); + assert.ok(frappe.tests.is_visible(num_str), "Math operation '"+operations[1]+"' is correct"); + }, + + () => $('#navbar-search').focus(), + () => $('#navbar-search').val(random_number_1+operations[2]+random_number_2), + () => $('#navbar-search').focus(), + () => frappe.timeout(1), + () => { + let num_str = random_number_1+operations[2]+random_number_2+' = '+parseFloat(parseFloat(random_number_1)/parseFloat(random_number_2)); + assert.ok(frappe.tests.is_visible(num_str), "Math operation '"+operations[2]+"' is correct"); + }, + + () => $('#navbar-search').focus(), + () => $('#navbar-search').val(random_number_1+operations[3]+random_number_2), + () => $('#navbar-search').focus(), + () => frappe.timeout(1), + () => { + let num_str = random_number_1+operations[3]+random_number_2+' = '+parseFloat(parseFloat(random_number_1)*parseFloat(random_number_2)); + assert.ok(frappe.tests.is_visible(num_str), "Math operation '"+operations[3]+"' is correct"); + }, + + () => $('#navbar-search').focus(), + () => $('#navbar-search').val("=Math.sin(Math.PI/2)"), + () => $('#navbar-search').focus(), + () => frappe.timeout(1), + () => assert.ok(frappe.tests.is_visible("Math.sin(Math.PI/2) = 1"), "Math operation 'sin' evaluated correctly"), + + // Close the modal + () => awesome_search.awesomplete.select(), + () => frappe.tests.close_modal(), + + () => done() + ]); +}); \ No newline at end of file diff --git a/frappe/tests/ui/global_search/_test_new_record.js b/frappe/tests/ui/global_search/_test_new_record.js new file mode 100644 index 0000000000..df10fcee03 --- /dev/null +++ b/frappe/tests/ui/global_search/_test_new_record.js @@ -0,0 +1,55 @@ +QUnit.module('views'); + +QUnit.test("Make a new record", function(assert) { + assert.expect(3); + let done = assert.async(); + let option_number=0; + let awesome_search = $('#navbar-search').get(0); + let random_text = frappe.utils.get_random(10); + let options = () => { + // Method to return the available options after search + return $('body > div.main-section > header > div > div > div.hidden-xs > form > div > div > ul > li').each(function (){}); + }; + let todo_title_text = () => { + // Method to return the title of the todo visible + return $("div.list-item__content.ellipsis.list-item__content--flex-2 > a:visible").text(); + }; + + frappe.run_serially([ + // Goto Home using button click to check if its working + () => frappe.set_route(), + () => frappe.timeout(1), + + () => $('#navbar-search').focus(), + () => $('#navbar-search').val('ToDo'), + () => $('#navbar-search').focus(), + () => frappe.timeout(1), + () => { + assert.ok(frappe.tests.is_visible('New ToDo'), "'New ToDo' is visible!"); + if (frappe.tests.is_visible('New ToDo')){ + let search_options = options(); + // Iterate over all available options till you reach 'New ToDo' + for (option_number=0; option_number awesome_search.awesomplete.goto(option_number), + // Click the highlighted option + () => awesome_search.awesomplete.select(), + () => frappe.timeout(1), + () => frappe.quick_entry.dialog.set_value('description', random_text), + () => frappe.quick_entry.insert(), + () => frappe.timeout(1), + () => frappe.set_route(["List", "ToDo", "List"]), + () => frappe.timeout(1), + // Verify if the todo is created + () => frappe.tests.click_page_head_item("Refresh"), + () => frappe.timeout(1), + () => assert.ok(todo_title_text().includes(random_text), "New ToDo was created successfully"), + () => assert.deepEqual(["List", "ToDo", "List"], frappe.get_route(), "Successfully routed to 'ToDo List'"), + + () => done() + ]); +}); \ No newline at end of file diff --git a/frappe/tests/ui/global_search/_test_open_module.js b/frappe/tests/ui/global_search/_test_open_module.js new file mode 100644 index 0000000000..9374e45e7e --- /dev/null +++ b/frappe/tests/ui/global_search/_test_open_module.js @@ -0,0 +1,42 @@ +QUnit.module('views'); + +QUnit.test("Open a module or tool", function(assert) { + assert.expect(2); + let done = assert.async(); + let option_number=0; + let awesome_search = $('#navbar-search').get(0); + let options = () => { + // Method to return the available options after search + return $('body > div.main-section > header > div > div > div.hidden-xs > form > div > div > ul > li').each(function (){}); + }; + + frappe.run_serially([ + // Goto Home using button click to check if its working + () => frappe.set_route(), + () => frappe.timeout(1), + + () => $('#navbar-search').focus(), + () => $('#navbar-search').val('ToDo'), + () => $('#navbar-search').focus(), + () => frappe.timeout(1), + () => { + assert.ok(frappe.tests.is_visible('ToDo List'), "'ToDo List' is visible!"); + if (frappe.tests.is_visible('ToDo List')){ + let search_options = options(); + // Iterate over all available options till you reach 'ToDo List' + for (option_number=0; option_number awesome_search.awesomplete.goto(option_number), + // Click the highlighted option + () => awesome_search.awesomplete.select(), + () => frappe.timeout(1), + // Verify if the redirected route is correct + () => assert.deepEqual(["List", "ToDo", "List"], frappe.get_route(), "Successfully routed to 'ToDo List'"), + + () => done() + ]); +}); \ No newline at end of file diff --git a/frappe/tests/ui/global_search/_test_search_document.js b/frappe/tests/ui/global_search/_test_search_document.js new file mode 100644 index 0000000000..34792e2c00 --- /dev/null +++ b/frappe/tests/ui/global_search/_test_search_document.js @@ -0,0 +1,88 @@ +QUnit.module('views'); + +QUnit.test("Search in a document type", function(assert) { + assert.expect(3); + let done = assert.async(); + let option_number=0; + let awesome_search = $('#navbar-search').get(0); + let random_text = "argo1234"; + let options = () => { + // Method to return the available options after search + return $('body > div.main-section > header > div > div > div.hidden-xs > form > div > div > ul > li').each(function (){}); + }; + let todo_title_text = () => { + // Method to return the title of the todo visible + return $("div.list-item__content.ellipsis.list-item__content--flex-2 > a:visible").text(); + }; + let select_all_todo = () => { + $('div.list-item__content.ellipsis.text-muted.list-item__content--flex-2 > input:visible').click(); + }; + let remove_all_filters = () => { + $('button.remove-filter > i').click(); + }; + + frappe.run_serially([ + // Goto Home using button click to check if its working + () => frappe.set_route(), + () => frappe.timeout(1), + + () => $('#navbar-search').focus(), + () => $('#navbar-search').val('ToDo'), + () => $('#navbar-search').focus(), + () => frappe.timeout(1), + () => { + assert.ok(frappe.tests.is_visible('New ToDo'), "'New ToDo' is visible!"); + if (frappe.tests.is_visible('New ToDo')){ + let search_options = options(); + // Iterate over all available options till you reach 'New ToDo' + for (option_number=0; option_number awesome_search.awesomplete.goto(option_number), + // Click the highlighted option + () => awesome_search.awesomplete.select(), + () => frappe.timeout(1), + () => frappe.quick_entry.dialog.set_value('description', random_text), + () => frappe.quick_entry.insert(), + () => frappe.timeout(1), + + // Search for the created ToDo in global search + () => frappe.set_route(["List", "ToDo", "List"]), + () => frappe.timeout(1), + () => $('#navbar-search').focus(), + () => $('#navbar-search').val('argo1234'), + () => $('#navbar-search').focus(), + () => frappe.timeout(1), + () => { + assert.ok(frappe.tests.is_visible('Find argo1234 in ToDo'), "'Find argo1234 in ToDo' is visible!"); + if (frappe.tests.is_visible('Find argo1234 in ToDo')){ + let search_options = options(); + // Iterate over all available options till you reach 'New ToDo' + for (option_number=0; option_number awesome_search.awesomplete.goto(option_number), + // Click the highlighted option + () => awesome_search.awesomplete.select(), + () => frappe.timeout(1), + // Verify if the 'argo1234' is the only ToDo + () => assert.ok(todo_title_text().includes('argo1234'), "'argo1234' is the only visible ToDo"), + + // Remove all filters + () => remove_all_filters(), + () => frappe.timeout(1), + // Delete all ToDo + () => select_all_todo(), + () => frappe.timeout(1), + () => frappe.tests.click_page_head_item('Delete'), + () => frappe.tests.click_page_head_item('Yes'), + + () => done() + ]); +}); \ No newline at end of file diff --git a/frappe/tests/ui/_test_calendar_view.js b/frappe/tests/ui/test_calendar_view.js similarity index 61% rename from frappe/tests/ui/_test_calendar_view.js rename to frappe/tests/ui/test_calendar_view.js index 44a87b7174..8e76a08b93 100644 --- a/frappe/tests/ui/_test_calendar_view.js +++ b/frappe/tests/ui/test_calendar_view.js @@ -1,7 +1,7 @@ QUnit.module('views'); QUnit.test("Calendar View Tests", function(assert) { - assert.expect(7); + assert.expect(6); let done = assert.async(); let random_text = frappe.utils.get_random(10); let today = frappe.datetime.get_today()+" 16:20:35"; //arbitrary value taken to prevent cases like 12a for 12:00am and 12h to 24h conversion @@ -9,14 +9,6 @@ QUnit.test("Calendar View Tests", function(assert) { // Method to return the start-time (hours) of the event visible return $('.fc-time').text().split('p')[0]; // 'p' because the arbitrary time is pm }; - // let visible_hours = () => { - // // Method to return the start-time (hours) of the event visible - // return $('.fc-time').text().split(':')[0].replace(/\D+/g, ''); - // }; - // let visible_minutes = () => { - // // Method to return the start-time (minutes) of the event visible - // return $('.fc-time').text().split(':')[1].replace(/\D+/g, ''); - // }; let event_title_text = () => { // Method to return the title of the event visible return $('.fc-title:visible').text(); @@ -32,63 +24,65 @@ QUnit.test("Calendar View Tests", function(assert) { // Goto Calendar view () => frappe.set_route(["List", "Event", "Calendar"]), + () => frappe.tests.click_page_head_item("Refresh"), + () => frappe.timeout(2), // Check if event is created () => { // Check if the event exists and if its title matches with the one created - assert.equal(event_title_text(), random_text); + assert.ok(event_title_text().includes(random_text), "Event title verified"); // Check if time of event created is correct - // assert.equal(visible_hours(), 4); - // assert.equal(visible_minutes(), 20); - assert.equal(visible_time(), "4:20"); + assert.ok(visible_time().includes("4:20"), "Event start time verified"); }, // Delete event // Goto Calendar view () => frappe.set_route(["List", "Event", "Calendar"]), - () => frappe.timeout(0.3), + () => frappe.timeout(1), // Open the event to be deleted () => frappe.tests.click_generic_text(random_text), () => frappe.tests.click_page_head_item('Menu'), () => frappe.tests.click_dropdown_item('Delete'), () => frappe.tests.click_page_head_item('Yes'), - () => frappe.timeout(4), + () => frappe.timeout(1), + () => frappe.tests.click_page_head_item("Refresh"), + () => frappe.timeout(1), // Goto Calendar View () => frappe.set_route(["List", "Event", "Calendar"]), - () => frappe.timeout(0.3), + () => frappe.timeout(1), // Check if all menu items redirect to correct locations // Check if clicking on 'Import' redirects you to ["data-import-tool"] () => frappe.tests.click_page_head_item('Menu'), () => frappe.tests.click_dropdown_item('Import'), - () => assert.deepEqual(["data-import-tool"], frappe.get_route()), - () => window.history.back(), - () => frappe.timeout(0.5), + () => assert.deepEqual(["data-import-tool"], frappe.get_route(), "Routed to 'data-import-tool' by clicking on 'Import'"), + () => frappe.set_route(["List", "Event", "Calendar"]), + () => frappe.timeout(1), // Check if clicking on 'User Permissions Manager' redirects you to ["user-permissions"] () => frappe.tests.click_page_head_item('Menu'), () => frappe.tests.click_dropdown_item('User Permissions Manager'), - () => assert.deepEqual(["user-permissions"], frappe.get_route()), - () => window.history.back(), - () => frappe.timeout(0.5), + () => assert.deepEqual(["user-permissions"], frappe.get_route(), "Routed to 'user-permissions' by clicking on 'User Permissions Manager'"), + () => frappe.set_route(["List", "Event", "Calendar"]), + () => frappe.timeout(1), // Check if clicking on 'Role Permissions Manager' redirects you to ["permission-manager"] () => frappe.tests.click_page_head_item('Menu'), () => frappe.tests.click_dropdown_item('Role Permissions Manager'), - () => assert.deepEqual(["permission-manager"], frappe.get_route()), - () => window.history.back(), - () => frappe.timeout(0.5), + () => assert.deepEqual(["permission-manager"], frappe.get_route(), "Routed to 'permission-manager' by clicking on 'Role Permissions Manager'"), + () => frappe.set_route(["List", "Event", "Calendar"]), + () => frappe.timeout(1), - // Check if clicking on 'Customize' redirects you to ["Form", "Customize Form"] - () => frappe.tests.click_page_head_item('Menu'), - () => frappe.tests.click_dropdown_item('Customize'), - () => assert.deepEqual(["Form", "Customize Form"], frappe.get_route()), - () => window.history.back(), - () => frappe.timeout(0.5), + // // Check if clicking on 'Customize' redirects you to ["Form", "Customize Form"] + // *** ERROR HERE IN FRAPPE: UNCOMMENT THIS ONCE THAT IS RESOLVED *** // + // () => frappe.tests.click_page_head_item('Menu'), + // () => frappe.tests.click_dropdown_item('Customize'), + // () => assert.deepEqual(["Form", "Customize Form"], frappe.get_route(), "Routed to 'Form, Customize Form' by clicking on 'Customize'"), + // () => frappe.set_route(["List", "Event", "Calendar"]), + // () => frappe.timeout(3), // Check if event is deleted - () => assert.equal(event_title_text(), ""), + () => assert.notOk(event_title_text().includes(random_text), "Event deleted"), () => done() - ]); }); \ No newline at end of file diff --git a/frappe/tests/ui/_test_desktop.js b/frappe/tests/ui/test_desktop.js similarity index 78% rename from frappe/tests/ui/_test_desktop.js rename to frappe/tests/ui/test_desktop.js index 389b22f77d..9dc8da5799 100644 --- a/frappe/tests/ui/_test_desktop.js +++ b/frappe/tests/ui/test_desktop.js @@ -9,54 +9,54 @@ QUnit.test("Verification of navbar menu links", function(assert) { frappe.run_serially([ // Goto Desk using button click to check if its working () => frappe.tests.click_navbar_item('Home'), - () => assert.deepEqual([""], frappe.get_route()), + () => assert.deepEqual([""], frappe.get_route(), "Routed correctly"), // Click username on the navbar (Adminisrator) and verify visibility of all elements () => frappe.tests.click_navbar_item('navbar_user'), () => navbar_user_items.forEach(function(navbar_user_item) { - assert.ok(frappe.tests.is_visible(navbar_user_item)); + assert.ok(frappe.tests.is_visible(navbar_user_item), "Visibility of "+navbar_user_item+" verified"); }), // Click Help and verify visibility of all elements () => frappe.tests.click_navbar_item('Help'), () => modal_and_heading.forEach(function(modal) { - assert.ok(frappe.tests.is_visible(modal)); + assert.ok(frappe.tests.is_visible(modal), "Visibility of "+modal+" modal verified"); }), // Goto Desk () => frappe.tests.click_navbar_item('Home'), - () => frappe.timeout(0.3), + () => frappe.timeout(1), // Click navbar-username and verify links of all menu items // Check if clicking on 'Set Desktop Icons' redirects you to the correct page () => frappe.tests.click_navbar_item('navbar_user'), () => frappe.tests.click_dropdown_item('Set Desktop Icons'), - () => assert.deepEqual(["modules_setup"], frappe.get_route()), + () => assert.deepEqual(["modules_setup"], frappe.get_route(), "Routed to 'modules_setup' by clicking on 'Set Desktop Icons'"), () => frappe.tests.click_navbar_item('Home'), // Check if clicking on 'My Settings' redirects you to the correct page () => frappe.tests.click_navbar_item('navbar_user'), () => frappe.tests.click_dropdown_item('My Settings'), - () => assert.deepEqual(["Form", "User", "Administrator"], frappe.get_route()), + () => assert.deepEqual(["Form", "User", "Administrator"], frappe.get_route(), "Routed to 'Form, User, Administrator' by clicking on 'My Settings'"), () => frappe.tests.click_navbar_item('Home'), // Check if clicking on 'Background Jobs' redirects you to the correct page () => frappe.tests.click_navbar_item('navbar_user'), () => frappe.tests.click_dropdown_item('Background Jobs'), - () => assert.deepEqual(["background_jobs"], frappe.get_route()), + () => assert.deepEqual(["background_jobs"], frappe.get_route(), "Routed to 'background_jobs' by clicking on 'Background Jobs'"), () => frappe.tests.click_navbar_item('Home'), // Click Help and check both modals // Check if clicking 'Documentation' opens the right modal () => frappe.tests.click_navbar_item('Help'), () => frappe.tests.click_dropdown_item('Documentation'), - () => assert.ok(frappe.tests.is_visible('Documentation', 'span')), + () => assert.ok(frappe.tests.is_visible('Documentation', 'span'), "Documentation modal popped"), () => frappe.tests.click_generic_text('Close', 'button'), // Check if clicking 'About' opens the right modal () => frappe.tests.click_navbar_item('Help'), () => frappe.tests.click_dropdown_item('About'), - () => assert.ok(frappe.tests.is_visible('Frappe Framework', 'div')), + () => assert.ok(frappe.tests.is_visible('Frappe Framework', 'div'), "Frappe Framework[About] modal popped"), () => frappe.tests.click_generic_text('Close', 'button'), () => done() diff --git a/frappe/tests/ui/test_list/_test_list_values.js b/frappe/tests/ui/test_list/_test_list_values.js new file mode 100644 index 0000000000..5da27c007e --- /dev/null +++ b/frappe/tests/ui/test_list/_test_list_values.js @@ -0,0 +1,16 @@ +QUnit.module('views'); + +QUnit.test("Test list values", function(assert) { + assert.expect(2); + let done = assert.async(); + + frappe.run_serially([ + () => frappe.set_route('List', 'DocType'), + () => frappe.timeout(2), + () => { + assert.deepEqual(['List', 'DocType', 'List'], frappe.get_route(), "Routed to DocType List"); + assert.ok($('.list-item:visible').length > 10, "More than 10 items visible in DocType List"); + }, + () => done() + ]); +}); \ No newline at end of file diff --git a/frappe/tests/ui/test_list.js b/frappe/tests/ui/test_list/_test_quick_entry.js similarity index 55% rename from frappe/tests/ui/test_list.js rename to frappe/tests/ui/test_list/_test_quick_entry.js index 6d04c07a7f..2a816c7425 100644 --- a/frappe/tests/ui/test_list.js +++ b/frappe/tests/ui/test_list/_test_quick_entry.js @@ -1,6 +1,6 @@ QUnit.module('views'); -QUnit.test("Test quick entry", function(assert) { +QUnit.only("Test quick entry", function(assert) { assert.expect(2); let done = assert.async(); let random_text = frappe.utils.get_random(10); @@ -11,10 +11,10 @@ QUnit.test("Test quick entry", function(assert) { () => frappe.quick_entry.dialog.set_value('description', random_text), () => frappe.quick_entry.insert(), (doc) => { - assert.ok(doc && !doc.__islocal); + assert.ok(doc && !doc.__islocal, "Document exists"); return frappe.set_route('Form', 'ToDo', doc.name); }, - () => assert.ok(cur_frm.doc.description.includes(random_text)), + () => assert.ok(cur_frm.doc.description.includes(random_text), "ToDo created"), // Delete the created ToDo () => frappe.tests.click_page_head_item('Menu'), @@ -24,18 +24,4 @@ QUnit.test("Test quick entry", function(assert) { () => done() ]); -}); - -QUnit.test("Test list values", function(assert) { - assert.expect(2); - let done = assert.async(); - frappe.run_serially([ - () => frappe.set_route('List', 'DocType'), - () => frappe.timeout(2), - () => { - assert.deepEqual(['List', 'DocType', 'List'], frappe.get_route()); - assert.ok($('.list-item:visible').length > 10); - }, - () => done() - ]); -}); +}); \ No newline at end of file diff --git a/frappe/tests/ui/test_list_delete.js b/frappe/tests/ui/test_list_delete.js new file mode 100644 index 0000000000..d9edb1d54d --- /dev/null +++ b/frappe/tests/ui/test_list_delete.js @@ -0,0 +1,62 @@ +QUnit.module('views'); + +QUnit.test("Test deletion of one list element", function(assert) { + assert.expect(3); + let done = assert.async(); + let count; + let random; + + frappe.run_serially([ + () => frappe.tests.setup_doctype('User'), + () => frappe.tests.create_todo(2), + () => frappe.set_route('List', 'ToDo', 'List'), + () => frappe.timeout(0.5), + () => { + assert.deepEqual(['List', 'ToDo', 'List'], frappe.get_route(), "List opened successfully."); + //total list elements + count = cur_list.data.length; + random = Math.floor(Math.random() * (count) + 1); + //select one element randomly + $('div:nth-child('+random+')>div>div>.list-row-checkbox').click(); + }, + () => cur_list.page.btn_primary.click(), + () => frappe.timeout(0.5), + () => { + //check if asking for confirmation and click yes + assert.equal("Confirm", cur_dialog.title, "Asking for confirmation."); + cur_dialog.primary_action(frappe.confirm); + }, + () => frappe.timeout(1), + //check if total elements decreased by one + () => assert.equal(cur_list.data.length, (count-1), "Only one element is deleted."), + () => done() + ]); +}); + +QUnit.test("Test deletion of all list element", function(assert) { + assert.expect(3); + let done = assert.async(); + + frappe.run_serially([ + () => frappe.tests.setup_doctype('User'), + () => frappe.tests.create_todo(5), + () => frappe.set_route('List', 'ToDo', 'List'), + () => frappe.timeout(0.5), + () => { + assert.deepEqual(['List', 'ToDo', 'List'], frappe.get_route(), "List opened successfully."); + //select all element + $('.list-select-all.hidden-xs').click(); + }, + () => cur_list.page.btn_primary.click(), + () => frappe.timeout(0.5), + () => { + assert.equal("Confirm", cur_dialog.title, "Asking for confirmation."); + //click yes for deletion + cur_dialog.primary_action(frappe.confirm); + }, + () => frappe.timeout(2), + //check zero elements left + () => assert.equal( cur_list.data.length, '0', "No element is present in list."), + () => done() + ]); +}); \ No newline at end of file diff --git a/frappe/tests/ui/test_list_filter.js b/frappe/tests/ui/test_list_filter.js new file mode 100644 index 0000000000..8319a11a4b --- /dev/null +++ b/frappe/tests/ui/test_list_filter.js @@ -0,0 +1,35 @@ +QUnit.module('views'); + +QUnit.test("Test filters", function(assert) { + assert.expect(2); + let done = assert.async(); + + frappe.run_serially([ + () => frappe.tests.setup_doctype('User'), + () => frappe.tests.create_todo(6), + () => frappe.set_route('List', 'ToDo', 'List'), + () => frappe.timeout(0.5), + () => { + assert.deepEqual(['List', 'ToDo', 'List'], frappe.get_route(), "List opened successfully."); + //set filter values + $('.col-md-2:nth-child(2) .input-sm').val('Closed'); + $('.col-md-2:nth-child(3) .input-sm').val('Low'); + $('.col-md-2:nth-child(4) .input-sm').val('05-05-2017'); + $('.col-md-2:nth-child(5) .input-sm').val('Administrator'); + }, + () => frappe.timeout(0.5), + () => cur_list.page.btn_secondary.click(), + () => frappe.timeout(1), + () => { + //get total list element + var count = cur_list.data.length; + //check if all elements are as per filter + var i=0; + for ( ; i < count ; i++) + if ((cur_list.data[i].status!='Closed')||(cur_list.data[i].priority!='Low')||(cur_list.data[i].owner!='Administrator')||(cur_list.data[i].date!='2017-05-05')) + break; + assert.equal(count, i, "Elements present have content according to filters."); + }, + () => done() + ]); +}); \ No newline at end of file diff --git a/frappe/tests/ui/test_list_paging.js b/frappe/tests/ui/test_list_paging.js new file mode 100644 index 0000000000..e427c6d9eb --- /dev/null +++ b/frappe/tests/ui/test_list_paging.js @@ -0,0 +1,18 @@ +QUnit.module('views'); + +QUnit.test("Test paging in list", function(assert) { + assert.expect(3); + let done = assert.async(); + + frappe.run_serially([ + () => frappe.set_route('List', 'DocType'), + () => frappe.timeout(0.5), + () => assert.deepEqual(['List', 'DocType', 'List'], frappe.get_route(), "List opened successfully."), + //check elements less then page length [20 in this case] + () => assert.ok(cur_list.data.length <= cur_list.page_length, "20 or less elements are visible."), + () => frappe.tests.click_and_wait('.btn-sm:contains("100"):visible'), + //check elements less then page length [100 in this case] + () => assert.ok(cur_list.data.length <= cur_list.page_length, "100 or less elements are visible."), + () => done() + ]); +}); \ No newline at end of file