fix: Error state and passing args for product listing

- Show error state in case of unexpected errors in query engine
- Pass args appropriately from `view.js`
- Use args correctly in `api.py`
- Error handling in `api.py` if query engine raises error
- Instantiated product data engine tests
- Fix dotted path for search api call in `search.js`
diff --git a/erpnext/e_commerce/api.py b/erpnext/e_commerce/api.py
index 4c9f4a7..728d336 100644
--- a/erpnext/e_commerce/api.py
+++ b/erpnext/e_commerce/api.py
@@ -3,6 +3,7 @@
 # For license information, please see license.txt
 
 import frappe
+import json
 from frappe.utils import cint
 
 from erpnext.e_commerce.product_data_engine.query import ProductQuery
@@ -10,23 +11,25 @@
 from erpnext.setup.doctype.item_group.item_group import get_child_groups
 
 @frappe.whitelist(allow_guest=True)
-def get_product_filter_data():
-	"""Get pre-rendered filtered products and discount filters on load."""
-	if frappe.form_dict:
-		search = frappe.form_dict.search
-		field_filters = frappe.parse_json(frappe.form_dict.field_filters)
-		attribute_filters = frappe.parse_json(frappe.form_dict.attribute_filters)
-		start = cint(frappe.parse_json(frappe.form_dict.start)) if frappe.form_dict.start else 0
-		item_group = frappe.form_dict.item_group
-		from_filters = frappe.parse_json(frappe.form_dict.from_filters)
+def get_product_filter_data(query_args=None):
+	"""Get filtered products and discount filters."""
+	if isinstance(query_args, str):
+		query_args = json.loads(query_args)
+
+	if query_args:
+		search = query_args.get("search")
+		field_filters = query_args.get("field_filters", {})
+		attribute_filters = query_args.get("attribute_filters", {})
+		start = cint(query_args.start) if query_args.get("start") else 0
+		item_group = query_args.get("item_group")
+		from_filters = query_args.get("from_filters")
 	else:
 		search, attribute_filters, item_group, from_filters = None, None, None, None
 		field_filters = {}
 		start = 0
 
+	# if new filter is checked, reset start to show filtered items from page 1
 	if from_filters:
-		# if filter is checked, go to start
-		# and show filtered items from page 1
 		start = 0
 
 	sub_categories = []
@@ -35,8 +38,18 @@
 		sub_categories = get_child_groups(item_group)
 
 	engine = ProductQuery()
-	result = engine.query(attribute_filters, field_filters, search_term=search,
-		start=start, item_group=item_group)
+	try:
+		result = engine.query(
+			attribute_filters,
+			field_filters,
+			search_term=search,
+			start=start,
+			item_group=item_group
+		)
+	except Exception as e:
+		traceback = frappe.get_traceback()
+		frappe.log_error(traceback, frappe._("Product Engine Error"))
+		return {"exc": "Something went wrong!"}
 
 	# discount filter data
 	filters = {}
diff --git a/erpnext/e_commerce/product_configurator/test_product_configurator.py b/erpnext/e_commerce/product_configurator/test_product_configurator.py
index 2f5a24f..fe4ef08 100644
--- a/erpnext/e_commerce/product_configurator/test_product_configurator.py
+++ b/erpnext/e_commerce/product_configurator/test_product_configurator.py
@@ -1,13 +1,9 @@
-import unittest
-
-import frappe
-from bs4 import BeautifulSoup
 import frappe, unittest
-from frappe.utils import get_html_for_route
 from erpnext.e_commerce.product_data_engine.query import ProductQuery
 from erpnext.e_commerce.doctype.website_item.website_item import make_website_item
 
 test_dependencies = ["Item"]
+#TODO: Rename to test item variant configurator
 
 class TestProductConfigurator(unittest.TestCase):
 	def setUp(self):
diff --git a/erpnext/e_commerce/product_data_engine/test_product_data_engine.py b/erpnext/e_commerce/product_data_engine/test_product_data_engine.py
new file mode 100644
index 0000000..8bca046
--- /dev/null
+++ b/erpnext/e_commerce/product_data_engine/test_product_data_engine.py
@@ -0,0 +1,38 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import frappe
+
+test_dependencies = ["Item"]
+
+class TestProductDataEngine(unittest.TestCase):
+	"Test Products Querying for Product Listing."
+	def test_product_list_ordering(self):
+		"Check if website items appear by ranking."
+		pass
+
+	def test_product_list_paging(self):
+		pass
+
+	def test_product_list_with_field_filter(self):
+		pass
+
+	def test_product_list_with_attribute_filter(self):
+		pass
+
+	def test_product_list_with_discount_filter(self):
+		pass
+
+	def test_product_list_with_mixed_filtes(self):
+		pass
+
+	def test_product_list_with_mixed_filtes_item_group(self):
+		pass
+
+	def test_products_in_multiple_item_groups(self):
+		"Check if product is visible on multiple item group pages barring its own."
+		pass
+
+	def test_product_list_with_variants(self):
+		pass
+
diff --git a/erpnext/e_commerce/product_ui/search.js b/erpnext/e_commerce/product_ui/search.js
index b93c975..ebe0076 100644
--- a/erpnext/e_commerce/product_ui/search.js
+++ b/erpnext/e_commerce/product_ui/search.js
@@ -49,7 +49,7 @@
 
 			// Fetch and populate product results
 			frappe.call({
-				method: "erpnext.templates.pages.e_commerce.product_search.search",
+				method: "erpnext.templates.pages.product_search.search",
 				args: {
 					query: query
 				},
@@ -61,7 +61,7 @@
 			// Populate categories
 			if (me.category_container) {
 				frappe.call({
-					method: "erpnext.templates.pages.e_commerce.product_search.get_category_suggestions",
+					method: "erpnext.templates.pages.product_search.get_category_suggestions",
 					args: {
 						query: query
 					},
diff --git a/erpnext/e_commerce/product_ui/views.js b/erpnext/e_commerce/product_ui/views.js
index 0a9ae1f..993fd5c 100644
--- a/erpnext/e_commerce/product_ui/views.js
+++ b/erpnext/e_commerce/product_ui/views.js
@@ -47,10 +47,14 @@
 		this.disable_view_toggler(true);
 
 		frappe.call({
-			method: 'erpnext.e_commerce.api.get_product_filter_data',
-			args: args,
+			method: "erpnext.e_commerce.api.get_product_filter_data",
+			args: {
+				query_args: args
+			},
 			callback: function(result) {
-				if (!result.exc && result && result.message) {
+				if (!result || result.exc || !result.message || result.message.exc) {
+					me.render_no_products_section(true);
+				} else {
 					// Sub Category results are independent of Items
 					if (me.item_group && result.message["sub_categories"].length) {
 						me.render_item_sub_categories(result.message["sub_categories"]);
@@ -82,8 +86,6 @@
 
 					// Bottom paging
 					me.add_paging_section(result.message["settings"]);
-				} else {
-					me.render_no_products_section();
 				}
 
 				me.disable_view_toggler(false);
@@ -189,7 +191,7 @@
 
 	prepare_search() {
 		$(".toolbar").append(`
-			<div class="input-group col-6">
+			<div class="input-group col-6 p-0">
 				<div class="dropdown w-100" id="dropdownMenuSearch">
 					<input type="search" name="query" id="search-box" class="form-control font-md"
 						placeholder="Search for Products"
@@ -211,7 +213,7 @@
 	}
 
 	render_view_toggler() {
-		$(".toolbar").append(`<div class="toggle-container col-6"></div>`);
+		$(".toolbar").append(`<div class="toggle-container col-6 p-0"></div>`);
 
 		["btn-list-view", "btn-grid-view"].forEach(view => {
 			let icon = view === "btn-list-view" ? "list" : "image-view";
@@ -473,16 +475,22 @@
 		}
 	}
 
-	render_no_products_section() {
-		this.products_section.append(`
-			<br><br><br>
-			<div class="cart-empty frappe-card">
+	render_no_products_section(error=false) {
+		let error_section = `
+			<div class="mt-4 w-100 alert alert-error font-md">
+				Something went wrong. Please refresh or contact us.
+			</div>
+		`;
+		let no_results_section = `
+			<div class="cart-empty frappe-card mt-4">
 				<div class="cart-empty-state">
 					<img src="/assets/erpnext/images/ui-states/cart-empty-state.png" alt="Empty Cart">
 				</div>
 				<div class="cart-empty-message mt-4">${ __('No products found') }</p>
 			</div>
-		`);
+		`;
+
+		this.products_section.append(error ? error_section : no_results_section);
 	}
 
 	render_item_sub_categories(categories) {
diff --git a/erpnext/public/scss/shopping_cart.scss b/erpnext/public/scss/shopping_cart.scss
index 4de16ca..c719d50 100644
--- a/erpnext/public/scss/shopping_cart.scss
+++ b/erpnext/public/scss/shopping_cart.scss
@@ -1316,6 +1316,12 @@
 	font-size: 14px;
 }
 
+.alert-error {
+	color: #e27a84;
+	background-color: #fff6f7;
+	border-color: #f5c6cb;
+}
+
 .font-md {
 	font-size: 14px !important;
 }