frappe.tests = {
	data: {},
	make: function(doctype, data) {
		let dialog_is_active = () => {
			return (
				cur_dialog && (!cur_frm || cur_frm.doc.doctype != doctype)
			);
		};
		return frappe.run_serially([
			() => frappe.set_route('List', doctype),
			() => frappe.new_doc(doctype),
			() => {
				if (frappe.quick_entry) {
					frappe.quick_entry.dialog.$wrapper.find('.edit-full').click();
					return frappe.timeout(1);
				} else {
					let root_node;
					if (cur_tree) {
						for (const key in cur_tree.nodes) {
							if (cur_tree.nodes[key].parent_label && cur_tree.nodes[key].expandable) {
								root_node = cur_tree.nodes[key].label;
								break;
							}
						}
					}
					if (root_node){
						frappe.tests.open_add_child_dialog(root_node);
						return frappe.timeout(1);
					}
				}
			},
			() => {
				if(dialog_is_active()) {
					return frappe.tests.set_dialog_values(cur_dialog, data);
				} else {
					return frappe.tests.set_form_values(cur_frm, data);
				}
			},

			() => {
				if(dialog_is_active()) {
					return cur_dialog.get_primary_btn().click();
				} else {
					return frappe.quick_entry ? frappe.quick_entry.insert() : cur_frm.save();
				}
			}
		]);
	},
	open_add_child_dialog: (root_node) => {
		frappe.tests.click_link(root_node);
		frappe.timeout(1);
		frappe.tests.click_button('Add Child');
	},
	set_form_values: (frm, data) => {
		let tasks = [];

		data.forEach(item => {
			for (let key in item) {
				let task = () => {
					let value = item[key];
					if ($.isArray(value)) {
						return frappe.tests.set_grid_values(frm, key, value);
					} else {
						// single value
						return frm.set_value(key, value);
					}
				};
				tasks.push(task);
				tasks.push(frappe.after_ajax);
				tasks.push(() => frappe.timeout(0.4));
			}
		});

		// set values
		return frappe.run_serially(tasks);

	},
	set_dialog_values: (dialog, data) => {
		let tasks = [];

		data.forEach(item => {
			for (let key in item) {
				let task = () => {
					let value = item[key];
					return dialog.set_value(key, value);
				};
				tasks.push(task);
				tasks.push(frappe.after_ajax);
				tasks.push(() => frappe.timeout(0.4));
			}
		});

		return frappe.run_serially(tasks);
	},
	set_grid_values: (frm, key, value) => {
		// set value in grid
		let grid = frm.get_field(key).grid;
		grid.remove_all();

		let grid_row_tasks = [];

		// build tasks for each row
		value.forEach(d => {
			grid_row_tasks.push(() => {

				let grid_value_tasks = [];
				grid_value_tasks.push(() => grid.add_new_row());
				grid_value_tasks.push(() => grid.get_row(-1).toggle_view(true));
				grid_value_tasks.push(() => frappe.timeout(0.5));

				// build tasks to set each row value
				d.forEach(child_value => {
					for (let child_key in child_value) {
						grid_value_tasks.push(() => {
							let grid_row = grid.get_row(-1);
							return frappe.model.set_value(grid_row.doc.doctype,
								grid_row.doc.name, child_key, child_value[child_key]);
						});
						grid_value_tasks.push(frappe.after_ajax);
						grid_value_tasks.push(() => frappe.timeout(0.4));
					}
				});

				return frappe.run_serially(grid_value_tasks);
			});
		});
		return frappe.run_serially(grid_row_tasks);
	},
	setup_doctype: (doctype, data) => {
		return frappe.run_serially([
			() => frappe.set_route('List', doctype),
			() => frappe.timeout(1),
			() => {
				frappe.tests.data[doctype] = [];
				let expected = Object.keys(data);
				cur_list.data.forEach((d) => {
					frappe.tests.data[doctype].push(d.name);
					if(expected.indexOf(d.name) !== -1) {
						expected[expected.indexOf(d.name)] = null;
					}
				});

				let tasks = [];

				expected.forEach(function(d) {
					if(d) {
						tasks.push(() => frappe.tests.make(doctype,
							data[d]));
					}
				});

				return frappe.run_serially(tasks);
			}]);
	},
	click_page_head_item: (text) => {
		// Method to items present on the page header like New, Save, Delete etc.
		let  possible_texts = ["New", "Delete", "Save", "Yes"];
		return frappe.run_serially([
			() => {
				if (text == "Menu"){
					$(`span.menu-btn-group-label:contains('Menu'):visible`).click();
				} else if (text == "Refresh") {
					$(`.btn-secondary:contains('Refresh'):visible`).click();
				} else if (possible_texts.includes(text)) {
					$(`.btn-primary:contains("${text}"):visible`).click();
				}
			},
			() => 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`).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(1)
		]);
	},
	is_visible: (text, tag='a') => {
		// Method to check the visibility of an element
		return $(`${tag}:contains("${text}")`).is(`:visible`);
	},
	/**
	 * Clicks a button on a form.
	 * @param {String} text - The button's text
	 * @return {frappe.timeout}
	 * @throws will throw an exception if a matching visible button is not found
	 */
	click_button: function(text) {
		let element = $(`.btn:contains("${text}"):visible`);
		if(!element.length) {
			throw `did not find any button containing ${text}`;
		}
		element.click();
		return frappe.timeout(0.5);
	},
	/**
	 * Clicks a link on a form.
	 * @param {String} text - The text of the link to be clicked
	 * @return {frappe.timeout}
	 * @throws will throw an exception if a link with the given text is not found
	 */
	click_link: function(text) {
		let element = $(`a:contains("${text}"):visible`);
		if(!element.length) {
			throw `did not find any link containing ${text}`;
		}
		element.get(0).click();
		return frappe.timeout(0.5);
	},
	/**
	 * Sets the given control to the value given.
	 * @param {String} fieldname - The Doctype's field name
	 * @param {String} value - The value the control should be changed to
	 * @return {frappe.timeout}
	 * @throws will throw an exception if the field is not found or is not visible
	 */
	set_control: function(fieldname, value) {
		let control = $(`.form-control[data-fieldname="${fieldname}"]:visible`);
		if(!control.length) {
			throw `did not find any control with fieldname ${fieldname}`;
		}
		control.val(value).trigger('change');
		return frappe.timeout(0.5);
	},
	/**
	 * Checks if given field is disabled.
	 * @param {String} fieldname - The Doctype field name
	 * @return {Boolean} true if condition is met
	 * @throws will throw an exception if the field is not found or is not a form control
	 */
	is_disabled_field: function(fieldname){
		let control = $(`.form-control[data-fieldname="${fieldname}"]:disabled`);
		if(!control.length) {
			throw `did not find any control with fieldname ${fieldname}`;
		} else {
			return true;
		}
	}
};