diff --git a/website_product_configurator/static/tests/tours/website_config_tour.esm.js b/website_product_configurator/static/tests/tours/website_config_tour.esm.js index 94efc1ebb..50a180077 100644 --- a/website_product_configurator/static/tests/tours/website_config_tour.esm.js +++ b/website_product_configurator/static/tests/tours/website_config_tour.esm.js @@ -1,7 +1,6 @@ import {registry} from "@web/core/registry"; registry.category("web_tour.tours").add("config", { - test: true, url: "/shop", sequence: 20, @@ -9,42 +8,30 @@ registry.category("web_tour.tours").add("config", { { content: "search 2 series", trigger: 'form input[name="search"]', - run: "text 2 series", + run: "edit 2 series", }, { content: "search 2 series", trigger: 'form:has(input[name="search"]) .oe_search_button', + run: "click", + expectUnloadPage: true, }, { content: "select 2 series", trigger: '.oe_product_cart a:contains("2 Series")', + run: "click", + expectUnloadPage: true, }, { content: "click to select fuel", trigger: ".tab-pane.fade.container.show.active select", - run: function () { - $( - ".tab-pane.fade.container.show.active select:first option:contains(Gasoline)" - )[0].selected = true; - $( - ".tab-pane.fade.container.show.active select:first option:contains(Gasoline)" - ) - .closest("select") - .change(); - }, + run: "selectByLabel Gasoline", }, { content: "click to select engine", trigger: - ".tab-pane.fade.container.show.active select.form-control.config_attribute.cfg-select.required_config_attrib", - run: function () { - $( - ".tab-pane.fade.container.show.active select > option:contains(218i)" - )[0].selected = true; - $(".tab-pane.fade.container.show.active select > option:contains(218i)") - .closest("select") - .change(); - }, + '.tab-pane.fade.container.show.active select.form-control.config_attribute.cfg-select.required_config_attrib:has(option:contains("218i"))', + run: "selectByLabel 218i", }, { content: "click on continue", @@ -54,51 +41,27 @@ registry.category("web_tour.tours").add("config", { { content: "click to select color", trigger: ".tab-pane.fade.container.show.active select", - run: function () { - $( - ".tab-pane.fade.container.show.active select:first option:contains(Silver)" - )[0].selected = true; - $( - ".tab-pane.fade.container.show.active select:first option:contains(Silver)" - ) - .closest("select") - .change(); - }, + run: "selectByLabel Silver", }, { content: "click to select rims", trigger: - ".tab-pane.fade.container.show.active select.form-control.config_attribute.cfg-select.required_config_attrib", - run: function () { - $( - ".tab-pane.fade.container.show.active select > option:contains(V-spoke 16)" - )[0].selected = true; - $( - ".tab-pane.fade.container.show.active select > option:contains(V-spoke 16)" - ) - .closest("select") - .change(); - }, + '.tab-pane.fade.container.show.active select.form-control.config_attribute.cfg-select.required_config_attrib:has(option:contains("V-spoke 16"))', + run: "selectByLabel V-spoke 16", + }, + { + content: "wait for the Lines configuration step", + trigger: ".nav-item.config_step a:contains(Lines)", }, { content: "click on continue", - extra_trigger: ".nav-item.config_step a:contains(Lines)", trigger: "span:contains(Continue)", run: "click", }, { content: "click to select Lines", trigger: ".tab-pane.fade.container.show.active select", - run: function () { - $( - ".tab-pane.fade.container.show.active select option:contains(Sport Line)" - )[0].selected = true; - $( - ".tab-pane.fade.container.show.active select option:contains(Sport Line)" - ) - .closest("select") - .change(); - }, + run: "selectByLabel Sport Line", }, { content: "click on continue", @@ -108,14 +71,7 @@ registry.category("web_tour.tours").add("config", { { content: "click to select tapistry", trigger: ".tab-pane.fade.container.show.active select", - run: function () { - $( - ".tab-pane.fade.container.show.active select option:contains(Black)" - )[0].selected = true; - $(".tab-pane.fade.container.show.active select option:contains(Black)") - .closest("select") - .change(); - }, + run: "selectByLabel Black", }, { content: "click on continue", @@ -125,41 +81,31 @@ registry.category("web_tour.tours").add("config", { { content: "click to select Transmission", trigger: ".tab-pane.fade.container.show.active select", - run: function () { - $( - '.tab-pane.fade.container.show.active select:first option:contains("Automatic (Steptronic)")' - )[0].selected = true; - $( - '.tab-pane.fade.container.show.active select:first option:contains("Automatic (Steptronic)")' - ) - .closest("select") - .change(); - }, + run: "selectByLabel Automatic", }, { content: "click to select Options", trigger: - ".tab-pane.fade.container.show.active select.form-control.config_attribute.cfg-select.required_config_attrib", - run: function () { - $( - ".tab-pane.fade.container.show.active select > option:contains(Armrest)" - )[0].selected = true; - $( - ".tab-pane.fade.container.show.active select > option:contains(Armrest)" - ) - .closest("select") - .change(); - }, - }, - { - content: "click on continue", + '.tab-pane.fade.container.show.active select.form-control.config_attribute.cfg-select.required_config_attrib:has(option:contains("Armrest"))', + run: "selectByLabel Armrest", + }, + { + // This final "Continue" completes the configuration. The + // configurator then submits it (save_configuration with + // submit_configuration=true), creates the product variant and + // redirects to the configured product page via window.location. + // + // That redirect runs asynchronously, from the save_configuration + // RPC promise callback, so it fires a beforeunload roughly 100ms + // after this click. It MUST be the last step and carry + // expectUnloadPage: true: that keeps the tour engine's allowUnload + // flag true (no later step resets it) so the async unload is + // accepted, then the tour resumes on the configured product page + // and completes. + content: "click on continue to finish the configuration", trigger: "span:contains(Continue)", run: "click", - }, - { - content: "click on add to cart", - trigger: "#add_to_cart", - run: "click", + expectUnloadPage: true, }, ], }); diff --git a/website_product_configurator/tests/common.py b/website_product_configurator/tests/common.py index b2692fae9..8ec9f88e0 100644 --- a/website_product_configurator/tests/common.py +++ b/website_product_configurator/tests/common.py @@ -11,7 +11,7 @@ def setUpClass(cls): super().setUpClass() cls.productConfigStepLine = cls.env["product.config.step.line"] cls.productAttributeLine = cls.env["product.template.attribute.line"] - cls.product_category = cls.env.ref("product.product_category_5") + cls.product_category = cls.env.ref("product.product_category_goods") cls.value_diesel = cls.env.ref( "product_configurator.product_attribute_value_diesel" ) diff --git a/website_product_configurator/tests/test_product_config.py b/website_product_configurator/tests/test_product_config.py index 1adcf70a2..9980d00c9 100644 --- a/website_product_configurator/tests/test_product_config.py +++ b/website_product_configurator/tests/test_product_config.py @@ -64,7 +64,7 @@ def test_remove_inactive_config_sessions(self): ) session_id2 = self.productConfigSession.create( { - "product_tmpl_id": self.config_product_1.id, + "product_tmpl_id": self.product_tmpl_id.id, "value_ids": [ ( 6, diff --git a/website_product_configurator/tests/test_sale_order.py b/website_product_configurator/tests/test_sale_order.py index 90dee4aa0..f345abe1e 100644 --- a/website_product_configurator/tests/test_sale_order.py +++ b/website_product_configurator/tests/test_sale_order.py @@ -7,14 +7,13 @@ class TestSaleOrder(TestProductConfiguratorValues): @classmethod def setUpClass(cls): super().setUpClass() - cls.partner = cls.env.ref("base.res_partner_1") + cls.partner = cls.env["res.partner"].create({"name": "Test Customer"}) cls.product = cls.env["product.product"].create({"name": "test product"}) cls.product_uom_unit = cls.env.ref("uom.product_uom_unit") cls.pricelist = cls.env["product.pricelist"].create( { "name": "New Pricelist", "currency_id": cls.env.user.company_id.currency_id.id, - "discount_policy": "without_discount", } ) cls.sale_order = cls.env["sale.order"].create( @@ -31,7 +30,7 @@ def setUpClass(cls): { "product_id": cls.product.id, "name": "Test Line", - "product_uom": cls.product_uom_unit.id, + "product_uom_id": cls.product_uom_unit.id, "product_uom_qty": 2.0, "price_unit": 400.00, "config_session_id": cls.session_id.id, @@ -41,68 +40,26 @@ def setUpClass(cls): } ) - def test_cart_update(self): - product_id = ( - self.sale_order.order_line.product_id.product_tmpl_id.product_variant_id.id - ) - self.sale_order._cart_update( - product_id=product_id, - line_id=self.sale_order.order_line.id, - set_qty=0, - add_qty=0, - ) - self.assertFalse( - self.product.product_tmpl_id.config_ok, "product is config_ok True" - ) - self.product.product_tmpl_id.write({"config_ok": True}) - cart_update = self.sale_order._cart_update( - product_id=product_id, - line_id=self.sale_order.order_line.id, - set_qty=2, - add_qty=2, - ) - self.assertEqual(cart_update.get("line_id"), self.sale_order.order_line.id) - self.assertEqual( - cart_update.get("quantity"), self.sale_order.order_line.product_uom_qty - ) + def test_cart_update_line_quantity(self): + """The cart overrides keep the configuration session on the line and + honour quantity updates (including removal on a zero quantity).""" + order = self.sale_order.with_context(skip_cart_verification=True) + order_line = order.order_line + product_id = order_line.product_id.id - self.sale_order.write({"order_line": False}) - self.sale_order._cart_update( - product_id=product_id, - set_qty=1, - add_qty=1, - ) - self.assertTrue(self.sale_order.order_line, "No Sale Order Line created.") + # The line created in setUpClass carries the configuration session. + self.assertEqual(order_line.config_session_id, self.session_id) - self.sale_order._cart_update( - product_id=product_id, - line_id=self.sale_order.order_line.id, - set_qty=-1, - add_qty=1, - ) - self.assertFalse( - self.sale_order.order_line, - "Order Line is exist for quantity is less than equal zero.", - ) + # Increasing the quantity preserves the configuration session. + order._cart_update_line_quantity(line_id=order_line.id, quantity=5) + self.assertEqual(order_line.product_uom_qty, 5) + self.assertEqual(order_line.config_session_id, self.session_id) - self.sale_order._cart_update( - line_id=self.sale_order.order_line.id, - product_id=product_id, - add_qty="test", - ) - self.assertEqual( - self.sale_order.order_line.product_uom_qty, - 1, - "If wrong value is added then 1 quantity is deducted from Order Line.", - ) + # Adding the same product again matches the existing line. + result = order._cart_add(product_id=product_id, quantity=2) + self.assertEqual(result.get("line_id"), order_line.id) + self.assertEqual(order_line.product_uom_qty, 7) - self.sale_order._cart_update( - line_id=self.sale_order.order_line.id, - product_id=product_id, - set_qty="test", - ) - self.assertEqual( - self.sale_order.order_line.product_uom_qty, - 1, - "If wrong value is added then Order Line quantity as it is.", - ) + # Setting the quantity to zero removes the line from the cart. + order._cart_update_line_quantity(line_id=order_line.id, quantity=0) + self.assertFalse(order.order_line, "Order line was not removed.")