def test_get_listed_products_cache_bump(): supplier = get_default_supplier() shop = get_default_shop() product_1 = create_product("test-sku-1", supplier=supplier, shop=shop,) from shuup.front.template_helpers import general filter_dict = {"id": product_1.pk} cache.clear() context = get_jinja_context() set_cached_value_mock = mock.Mock(wraps=context_cache.set_cached_value) def set_cache_value(key, value, timeout=None): if "listed_products" in key: return set_cached_value_mock(key, value, timeout) with mock.patch.object(context_cache, "set_cached_value", new=set_cache_value): assert set_cached_value_mock.call_count == 0 for cache_test in range(2): assert general.get_listed_products(context, n_products=2, filter_dict=filter_dict, orderable_only=False) assert set_cached_value_mock.call_count == 1 # bump cache product_1.save() for cache_test in range(2): assert general.get_listed_products(context, n_products=2, filter_dict=filter_dict, orderable_only=False) assert set_cached_value_mock.call_count == 2 # use other filters from django.db.models import Q for cache_test in range(2): assert general.get_listed_products(context, n_products=2, extra_filters=Q(translations__name__isnull=False)) assert set_cached_value_mock.call_count == 3
def test_shop_form_part(rf): cache.clear() shop = factories.get_default_shop() request = apply_request_middleware(rf.get("/")) # nothing changed form_part = StripeConfigurationFormPart(request, shop) form = list(form_part.get_form_defs())[0].instantiate(prefix="stripe") assert form.has_changed() is False assert not get_checkout_payment_details_message(shop) assert not get_checkout_payment_phase_message(shop) assert not get_checkout_saved_card_message(shop) assert not get_saved_card_message(shop) request = apply_request_middleware(rf.post("/")) data = { "stripe-checkout_payment_details_message": "A", "stripe-checkout_payment_phase_message": "B", "stripe-checkout_saved_card_message": "C", "stripe-saved_card_message": "D" } form_part = StripeConfigurationFormPart(request, shop) form = list(form_part.get_form_defs())[0].instantiate(prefix="stripe", data=data) assert form.is_valid() form_part.form_valid({StripeConfigurationFormPart.name: form}) assert get_checkout_payment_details_message(shop) == "A" assert get_checkout_payment_phase_message(shop) == "B" assert get_checkout_saved_card_message(shop) == "C" assert get_saved_card_message(shop) == "D"
def test_sorts_and_filter_in_shop_edit(rf, admin_user): cache.clear() activate("en") with override_provides("front_extend_product_list_form", DEFAULT_FORM_MODIFIERS): shop = get_default_shop() view = ShopEditView.as_view() assert get_configuration(shop=shop) == settings.SHUUP_FRONT_DEFAULT_SORT_CONFIGURATION data = { "base-name__en": shop.name, "base-public_name__en": shop.public_name, "base-status": shop.status.value, "base-currency": shop.currency, "base-prices_include_tax": shop.prices_include_tax, "base-languages": "en", "product_list_facets-sort_products_by_name": True, "product_list_facets-sort_products_by_name_ordering": 11, "product_list_facets-sort_products_by_price": False, "product_list_facets-sort_products_by_price_ordering": 32, "product_list_facets-filter_products_by_manufacturer": False, "product_list_facets-filter_products_by_manufacturer_ordering": 1 } request = apply_request_middleware(rf.post("/", data=data), user=admin_user) response = view(request, pk=shop.pk) if hasattr(response, "render"): response.render() assert response.status_code in [200, 302] expected_configurations = { "sort_products_by_name": True, "sort_products_by_name_ordering": 11, "sort_products_by_price": False, "sort_products_by_price_ordering": 32, "filter_products_by_manufacturer": False, "filter_products_by_manufacturer_ordering": 1 } assert get_configuration(shop=shop) == expected_configurations
def test_get_random_products_cache_bump(): from shuup.front.template_helpers import general supplier = get_default_supplier() shop = get_default_shop() products = [create_product("sku-%d" % x, supplier=supplier, shop=shop) for x in range(2)] children = [create_product("SimpleVarChild-%d" % x, supplier=supplier, shop=shop) for x in range(2)] for child in children: child.link_to_parent(products[0]) context = get_jinja_context() cache.clear() set_cached_value_mock = mock.Mock(wraps=context_cache.set_cached_value) def set_cache_value(key, value, timeout=None): if "random_products" in key: return set_cached_value_mock(key, value, timeout) with mock.patch.object(context_cache, "set_cached_value", new=set_cache_value): assert set_cached_value_mock.call_count == 0 assert general.get_random_products(context, n_products=10) assert set_cached_value_mock.call_count == 1 # call again, the cache should be returned instead and the set_cached_value shouldn't be called again assert general.get_random_products(context, n_products=10) assert set_cached_value_mock.call_count == 1 # change a shop product, the cache should be bumped ShopProduct.objects.filter(shop=shop).first().save() assert general.get_random_products(context, n_products=10) assert set_cached_value_mock.call_count == 2
def test_simple_search_word_finder(rf): cache.clear() view = SearchView.as_view() name = "Savage Garden" sku = UNLIKELY_STRING prod = create_product( sku=sku, name=name, keywords="truly, madly, deeply", description="Descriptive text", shop=get_default_shop() ) resp = view(apply_request_middleware(rf.get("/"))) assert prod not in resp.context_data["object_list"], "No query no results" partial_sku = sku[:int(len(sku)/2)] valid_searches = ["Savage", "savage", "truly", "madly", "truly madly", "truly garden", "text", sku, partial_sku] for query in valid_searches: resp = view(apply_request_middleware(rf.get("/", {"q": query}))) assert name in resp.rendered_content invalid_searches = ["saavage", "", sku[::-1]] for query in invalid_searches: resp = view(apply_request_middleware(rf.get("/", {"q": query}))) assert name not in resp.rendered_content
def second_category_sort_test(browser, live_server, shop, category): url = reverse("shuup:category", kwargs={"pk": category.pk, "slug": category.slug}) browser.visit("%s%s" % (live_server, url)) wait_until_condition(browser, lambda x: x.is_element_present_by_css("button[data-id='id_limit']"), timeout=30) # Set limit to 24 click_element(browser, "button[data-id='id_limit']") click_element(browser, "button[data-id='id_limit'] + .dropdown-menu li[data-original-index='1'] a") wait_until_condition(browser, lambda x: len(x.find_by_css(".product-card")) == 13, timeout=30) # Check that visibility change affects the product count shop_products = ShopProduct.objects.filter(primary_category_id=category.id)[:3] for sp in shop_products: sp.visibility = ShopProductVisibility.NOT_VISIBLE sp.save() cache.clear() browser.reload() wait_until_condition(browser, lambda x: len(x.find_by_css(".product-card")) == 10) for sp in shop_products: sp.visibility = ShopProductVisibility.ALWAYS_VISIBLE sp.save() cache.clear() browser.reload() wait_until_condition(browser, lambda x: len(x.find_by_css(".product-card")) == 13, timeout=30)
def test_get_orderable_variation_children(rf): supplier = get_default_supplier() shop = get_default_shop() variable_name = "Color" parent = create_product("test-sku-1", shop=shop) variation_variable = ProductVariationVariable.objects.create(product=parent, identifier="color", name=variable_name) red_value = ProductVariationVariableValue.objects.create(variable=variation_variable, identifier="red", value="Red") blue_value =ProductVariationVariableValue.objects.create(variable=variation_variable, identifier="blue", value="Blue") combinations = list(parent.get_all_available_combinations()) assert len(combinations) == 2 for combo in combinations: assert not combo["result_product_pk"] child = create_product("xyz-%s" % combo["sku_part"], shop=shop, supplier=get_default_supplier(), default_price=20) child.link_to_parent(parent, combination_hash=combo["hash"]) combinations = list(parent.get_all_available_combinations()) assert len(combinations) == 2 parent.refresh_from_db() assert parent.is_variation_parent() request = apply_request_middleware(rf.get("/")) cache.clear() for time in range(2): orderable_children, is_orderable = get_orderable_variation_children(parent, request, None) assert len(orderable_children) for var_variable, var_values in dict(orderable_children).items(): assert var_variable == variation_variable assert red_value in var_values assert blue_value in var_values
def test_sorts_and_filter_in_category_edit(rf, admin_user): get_default_shop() cache.clear() activate("en") with override_provides("front_extend_product_list_form", DEFAULT_FORM_MODIFIERS): category = get_default_category() view = CategoryEditView.as_view() assert get_configuration(category=category) == settings.SHUUP_FRONT_DEFAULT_SORT_CONFIGURATION data = { "base-name__en": category.name, "base-status": category.status.value, "base-visibility": category.visibility.value, "base-ordering": category.ordering, "product_list_facets-sort_products_by_name": True, "product_list_facets-sort_products_by_name_ordering": 6, "product_list_facets-sort_products_by_price": False, "product_list_facets-sort_products_by_price_ordering": 32, "product_list_facets-filter_products_by_manufacturer": True, "product_list_facets-filter_products_by_manufacturer_ordering": 1 } request = apply_request_middleware(rf.post("/", data=data), user=admin_user) response = view(request, pk=category.pk) if hasattr(response, "render"): response.render() assert response.status_code in [200, 302] expected_configurations = { "sort_products_by_name": True, "sort_products_by_name_ordering": 6, "sort_products_by_price": False, "sort_products_by_price_ordering": 32, "filter_products_by_manufacturer": True, "filter_products_by_manufacturer_ordering": 1 } assert get_configuration(category=category) == expected_configurations
def test_product_descriptions(browser, live_server, settings): activate("en") cache.clear() shop = get_default_shop() product = create_product("product1", shop=shop, description="<b>My HTML description</b>", short_description="some short of description instead", supplier=get_default_supplier()) sp = ShopProduct.objects.get(product=product, shop=shop) sp.primary_category = get_default_category() sp.categories.add(get_default_category()) sp.save() # initialize test and go to front page browser = initialize_front_browser_test(browser, live_server) # view product detail page url = reverse("shuup:product", kwargs={"pk": product.pk, "slug": product.slug}) browser.visit("%s%s" % (live_server, url)) wait_until_condition(browser, lambda x: x.is_text_present(product.short_description)) assert product.description in browser.html # ensure the version is in static files assert "style.css?v=%s" % shuup.__version__ in browser.html # product preview url = reverse("shuup:xtheme_extra_view", kwargs={"view": "products"}) browser.visit("%s%s" % (live_server, url)) product_div_name = "product-{}".format(product.pk) wait_until_condition(browser, lambda x: x.find_by_css("#{} button.btn".format(product_div_name))) browser.execute_script("$('#{} button.btn').click();".format(product_div_name)) assert product.short_description == browser.find_by_css("#{} p.description".format(product_div_name))[0].html
def test_get_listed_products_filter(): context = get_jinja_context() shop = get_default_shop() supplier = get_default_supplier() product_1 = create_product( "test-sku-1", supplier=supplier, shop=shop, ) product_2 = create_product( "test-sku-2", supplier=supplier, shop=shop, ) cache.clear() from shuup.front.template_helpers import general filter_dict = {"id": product_1.id} for cache_test in range(2): product_list = general.get_listed_products(context, n_products=2, filter_dict=filter_dict) assert product_1 in product_list assert product_2 not in product_list for cache_test in range(2): product_list = general.get_listed_products(context, n_products=2, filter_dict=filter_dict, orderable_only=False) assert product_1 in product_list assert product_2 not in product_list
def test_simple_search_get_ids_works(rf): cache.clear() prod = get_default_product() bit = prod.name[:5] request = apply_request_middleware(rf.get("/")) assert prod.pk in get_search_product_ids(request, bit) assert prod.pk in get_search_product_ids(request, bit) # Should use cache
def test_basic_order_flow(with_company): cache.clear() create_default_order_statuses() n_orders_pre = Order.objects.count() populate_if_required() c = SmartClient() product_ids = _populate_client_basket(c) addresses_path = reverse("shuup:checkout", kwargs={"phase": "addresses"}) addresses_soup = c.soup(addresses_path) inputs = fill_address_inputs(addresses_soup, with_company=with_company) response = c.post(addresses_path, data=inputs) assert response.status_code == 302 # Should redirect forth methods_path = reverse("shuup:checkout", kwargs={"phase": "methods"}) methods_soup = c.soup(methods_path) assert c.post(methods_path, data=extract_form_fields(methods_soup)).status_code == 302 # Should redirect forth confirm_path = reverse("shuup:checkout", kwargs={"phase": "confirm"}) confirm_soup = c.soup(confirm_path) Product.objects.get(pk=product_ids[0]).soft_delete() assert c.post(confirm_path, data=extract_form_fields(confirm_soup)).status_code == 200 # user needs to reconfirm data = extract_form_fields(confirm_soup) data['product_ids'] = ','.join(product_ids[1:]) assert c.post(confirm_path, data=data).status_code == 302 # Should redirect forth n_orders_post = Order.objects.count() assert n_orders_post > n_orders_pre, "order was created"
def test_category_product_filters_2(browser, live_server, settings): cache.clear() # Avoid cache from past tests shop, first_cat, second_cat, third_cat, first_manufacturer = initialize_db() # Activate limit page size changer for the shop set_configuration( shop=shop, data={ "sort_products_by_name": True, "sort_products_by_name_ordering": 1, "sort_products_by_price": True, "sort_products_by_price_ordering": 2, "limit_product_list_page_size": True } ) # initialize test and go to front page browser = initialize_front_browser_test(browser, live_server) # check that front page actually loaded wait_until_condition(browser, lambda x: x.is_text_present("Welcome to Default!")) url = reverse("shuup:category", kwargs={"pk": first_cat.pk, "slug": first_cat.slug}) browser.visit("%s%s" % (live_server, url)) wait_until_condition(browser, lambda x: x.is_text_present("First Category")) wait_until_condition(browser, lambda x: x.is_text_present("Sort")) assert not browser.is_text_present("Manufacturers") # Since not in default configuration second_category_sort_test(browser, live_server, shop, second_cat) second_category_sort_with_price_filter(browser, second_cat)
def initialize_admin_browser_test(browser, live_server, settings, username="******", password="******", onboarding=False, language="en", shop=None, tour_complete=True): if not onboarding: settings.SHUUP_SETUP_WIZARD_PANE_SPEC = [] activate("en") cache.clear() shop = shop or get_default_shop() if tour_complete: from django.contrib.auth import get_user_model user = get_user_model().objects.get(username=username) set_tour_complete(shop, "dashboard", True, user) set_tour_complete(shop, "home", True, user) set_tour_complete(shop, "product", True, user) set_tour_complete(shop, "category", True, user) url = live_server + "/sa" browser.visit(url) browser.fill('username', username) browser.fill('password', password) browser.find_by_css(".btn.btn-primary.btn-lg.btn-block").first.click() if not onboarding: # set shop language to eng browser.find_by_id("dropdownMenu").click() browser.find_by_xpath('//a[@data-value="%s"]' % language).first.click() return browser
def initialize_db(): activate("en") # initialize cache.clear() shop = get_default_shop() for name, identifier in CATEGORY_DATA: category = Category() category.name = name category.identifier = identifier category.status = CategoryStatus.VISIBLE category.save() category.shops.add(shop) for name, identifier in MANUFACTURER_DATA: Manufacturer.objects.create(name=name, identifier=identifier) first_cat = Category.objects.filter(identifier="cat-1").first() second_cat = Category.objects.filter(identifier="cat-2").first() third_cat = Category.objects.filter(identifier="cat-3").first() assert first_cat.pk != second_cat.pk for name, sku, price in FIRST_CATEGORY_PRODUCT_DATA: product = create_orderable_product(name, sku, price=price) shop_product = product.get_shop_instance(shop) cat = Category.objects.first() shop_product.primary_category = first_cat shop_product.save() shop_product.categories.add(first_cat) # Add some variation products add_variations( shop, Product.objects.filter(sku="test-sku-1").first(), ["Black", "Yellow"], ["Big", "Small"] ) add_variations( shop, Product.objects.filter(sku="test-sku-2").first(), ["Brown", "Pink"], ["S", "L", "XL"] ) add_variations( shop, Product.objects.filter(sku="test-sku-3").first(), ["Brown", "Black"], ["S", "L", "XL", "Big"] ) for i in range(1, 14): product = create_orderable_product("Test product", "sku-%s" % i, price=i) shop_product = product.get_shop_instance(shop) cat = Category.objects.first() shop_product.primary_category = second_cat shop_product.save() shop_product.categories.add(second_cat) # Set manufacturer for first product only first_manufacturer = Manufacturer.objects.first() Product.objects.filter(sku="test-sku-1").update(manufacturer_id=first_manufacturer.id) return shop, first_cat, second_cat, third_cat, first_manufacturer
def test_simple_search_no_results(rf): cache.clear() with translation.override("xx"): # use built-in translation get_default_shop() view = SearchView.as_view() resp = view(apply_request_middleware(rf.get("/", {"q": UNLIKELY_STRING}))) assert NO_RESULTS_FOUND_STRING in resp.rendered_content resp = view(apply_request_middleware(rf.get("/"))) assert NO_RESULTS_FOUND_STRING in resp.rendered_content, "No query string no results"
def test_configuration_set_and_get(): cache.clear() shop = get_default_shop() test_conf_data = {"data": "test"} configuration.set(shop, "key", test_conf_data) # Get the configuration via configuration API assert configuration.get(shop, "key") == test_conf_data # Check that configuration is saved to database assert ConfigurationItem.objects.get(shop=shop, key="key").value == test_conf_data
def test_normalize_spaces(rf): cache.clear() view = SearchView.as_view() create_product(sku=UNLIKELY_STRING, name="Savage Garden", shop=get_default_shop()) query = "\t Savage \t \t \n \r Garden \n" resp = view(apply_request_middleware(rf.get("/"))) assert query not in resp.rendered_content resp = view(apply_request_middleware(rf.get("/", {"q": query}))) assert query in resp.rendered_content
def test_simple_search_view_works(rf): cache.clear() view = SearchView.as_view() prod = create_product(sku=UNLIKELY_STRING, shop=get_default_shop()) query = prod.name[:8] # This test is pretty cruddy. TODO: Un-cruddify this test. resp = view(apply_request_middleware(rf.get("/"))) assert query not in resp.rendered_content resp = view(apply_request_middleware(rf.get("/", {"q": query}))) assert query in resp.rendered_content
def test_basic_order_flow_registered(regular_user): cache.clear() create_default_order_statuses() n_orders_pre = Order.objects.count() populate_if_required() get_test_script("test script", "order_received") # paths addresses_path = reverse("shuup:checkout", kwargs={"phase": "addresses"}) methods_path = reverse("shuup:checkout", kwargs={"phase": "methods"}) confirm_path = reverse("shuup:checkout", kwargs={"phase": "confirm"}) template_data = STEP_DATA[0]["actions"][0]["template_data"] LANG_CODE = { "en": "US", "fi": "FI" } for lang in ["en", "fi"]: n_outbox_pre = len(mail.outbox) contact = get_person_contact(regular_user) contact.language = lang contact.save() c = SmartClient() c.login(username=REGULAR_USER_USERNAME, password=REGULAR_USER_PASSWORD) product_ids = _populate_client_basket(c) addresses_soup = c.soup(addresses_path) address = get_address(country=LANG_CODE[lang]) inputs = fill_address_inputs(addresses_soup, address) response = c.post(addresses_path, data=inputs) assert response.status_code == 302 # Should redirect forth methods_soup = c.soup(methods_path) assert c.post(methods_path, data=extract_form_fields(methods_soup)).status_code == 302 # Should redirect forth confirm_soup = c.soup(confirm_path) Product.objects.get(pk=product_ids[0]).soft_delete() assert c.post(confirm_path, data=extract_form_fields(confirm_soup)).status_code == 200 # user needs to reconfirm data = extract_form_fields(confirm_soup) data['product_ids'] = ','.join(product_ids[1:]) assert c.post(confirm_path, data=data).status_code == 302 # Should redirect forth n_orders_post = Order.objects.count() assert n_orders_post > n_orders_pre, "order was created" assert (len(mail.outbox) == n_outbox_pre + 1), "Sending email failed" latest_mail = mail.outbox[-1] # mail is always sent in fallback language since user is not registered assert latest_mail.subject == template_data[lang]["subject"], "Subject doesn't match" assert latest_mail.body == template_data[lang]["body"], "Body doesn't match"
def test_configuration_cache(): cache.clear() shop = get_default_shop() configuration.set(None, "key1", "test1") configuration.set(shop, "key2", "test2") # Shop configurations cache should be bumped assert cache.get(configuration._get_cache_key(shop)) is None configuration.get(shop, "key1") # Now shop configurations and key2 should found from cache assert cache.get(configuration._get_cache_key(shop)).get("key2") == "test2"
def test_configuration_update(): cache.clear() shop = get_default_shop() configuration.set(shop, "key1", {"data": "test1"}) configuration.set(shop, "key2", {"data": "test2"}) configuration.set(shop, "key3", {"data": "test3"}) assert configuration.get(shop, "key1").get("data") == "test1" assert configuration.get(shop, "key3").get("data") == "test3" # Update configuration configuration.set(shop, "key3", {"data": "test_bump"}) assert configuration.get(shop, "key3").get("data") == "test_bump"
def test_xtheme_snippet_injection(browser, admin_user, live_server, settings): shop = factories.get_default_shop() initialize_admin_browser_test(browser, live_server, settings) url = reverse("shuup_admin:xtheme_snippet.new") browser.visit("%s%s" % (live_server, url)) wait_until_condition(browser, lambda x: x.is_text_present("New Snippet")) browser.execute_script("$(\"[name='location']\").val('body_end').trigger('change')") browser.execute_script("$(\"[name='snippet_type']\").val('inline_js').trigger('change')") browser.execute_script("window.CodeMirror.editors['id_snippet-snippet'].setValue('alert(\"works\")');") click_element(browser, "button[type='submit']") wait_until_appeared(browser, "div[class='message success']") url = reverse("shuup:index") browser.visit("%s%s" % (live_server, url)) def has_alert(browser): try: return browser.get_alert().text == "works" except: return False wait_until_condition(browser, has_alert) browser.get_alert().accept() theme = get_current_theme(shop) snippet = Snippet.objects.filter(shop=shop).first() snippet.themes = [theme.identifier] snippet.save() cache.clear() browser.visit("%s%s" % (live_server, url)) wait_until_condition(browser, has_alert) browser.get_alert().accept() snippet.themes = ["doesnt-exist"] snippet.save() cache.clear() browser.visit("%s%s" % (live_server, url)) wait_until_condition(browser, lambda x: x.is_text_present("Welcome to Default!")) with pytest.raises(Exception): browser.get_alert() # delete the snippet url = reverse("shuup_admin:xtheme_snippet.edit", kwargs=dict(pk=snippet.pk)) browser.visit("%s%s" % (live_server, url)) assert Snippet.objects.filter(shop=shop).exists() click_element(browser, ".shuup-toolbar button.btn.btn-danger") browser.get_alert().accept() wait_until_condition(browser, lambda x: not Snippet.objects.filter(shop=shop).exists())
def test_stripe_checkout_phase(rf): cache.clear() shop = factories.get_default_shop() contact = factories.create_random_person() request = apply_request_middleware(rf.post("/"), shop=get_default_shop()) request.session = {} request.basket = get_basket(request) payment_processor = StripeCheckoutPaymentProcessor.objects.create(secret_key="secret", publishable_key="12", name="Stripe") service = payment_processor.create_service("stripe", shop=request.shop, tax_class=get_default_tax_class(), enabled=True) checkout_phase = StripeCheckoutPhase(request=request, service=service) with mock.patch.object(checkout_phase, "get_context_data") as mocked_get_context_data: mocked_get_context_data.return_value = { "view": checkout_phase, "stripe": { "publishable_key": "ha", "name": "he", "description": "hi", }, "customer": contact, "stripe_customer_data": { "id": "testing", "sources": { "data": { "?": True } } } } response = checkout_phase.get(request) assert response.status_code == 200 response.render() content = response.content.decode("utf-8") assert "We use Stripe for secure payment handling. You will only be charged when your order completes" in content assert "Click the button below to enter your card details" in content assert "Use saved card details by clicking button below" in content set_checkout_payment_details_message(shop, "ABC123") set_checkout_payment_phase_message(shop, "XYZ987") set_checkout_saved_card_message(shop, "QWERTY456") response = checkout_phase.get(request) assert response.status_code == 200 response.render() content = response.content.decode("utf-8") assert "We use Stripe for secure payment handling. You will only be charged when your order completes" not in content assert "Click the button below to enter your card details" not in content assert "Use saved card details by clicking button below" not in content assert "ABC123" in content assert "XYZ987" in content assert "QWERTY456" in content
def test_cross_sell_plugin_count(): shop = get_default_shop() supplier = get_default_supplier() product = create_product("test-sku", shop=shop, supplier=supplier) context = get_jinja_context(product=product) total_count = 5 trim_count = 3 type = ProductCrossSellType.RELATED _create_cross_sell_products(product, shop, supplier, type, total_count) assert ProductCrossSell.objects.filter(product1=product, type=type).count() == total_count cache.clear() assert len(list(product_helpers.get_product_cross_sells(context, product, type, trim_count))) == trim_count
def test_get_best_selling_products(): context = get_jinja_context() cache.clear() # No products sold assert len(list(general.get_best_selling_products(context, n_products=2))) == 0 supplier = get_default_supplier() shop = get_default_shop() product = get_default_product() create_order_with_product(product, supplier, quantity=1, taxless_base_unit_price=10, shop=shop) cache.clear() # One product sold assert len(list(general.get_best_selling_products(context, n_products=2))) == 1
def test_best_selling_products_with_multiple_orders(): context = get_jinja_context() supplier = get_default_supplier() shop = get_default_shop() n_products = 2 price = 10 product_1 = create_product("test-sku-1", supplier=supplier, shop=shop) product_2 = create_product("test-sku-2", supplier=supplier, shop=shop) create_order_with_product(product_1, supplier, quantity=1, taxless_base_unit_price=price, shop=shop) create_order_with_product(product_2, supplier, quantity=1, taxless_base_unit_price=price, shop=shop) cache.clear() # Two initial products sold assert product_1 in general.get_best_selling_products(context, n_products=n_products) assert product_2 in general.get_best_selling_products(context, n_products=n_products) product_3 = create_product("test-sku-3", supplier=supplier, shop=shop) create_order_with_product(product_3, supplier, quantity=2, taxless_base_unit_price=price, shop=shop) cache.clear() # Third product sold in greater quantity assert product_3 in general.get_best_selling_products(context, n_products=n_products) create_order_with_product(product_1, supplier, quantity=4, taxless_base_unit_price=price, shop=shop) create_order_with_product(product_2, supplier, quantity=4, taxless_base_unit_price=price, shop=shop) cache.clear() # Third product outsold by first two products assert product_3 not in general.get_best_selling_products(context, n_products=n_products) children = [create_product("SimpleVarChild-%d" % x, supplier=supplier, shop=shop) for x in range(5)] for child in children: child.link_to_parent(product_3) create_order_with_product(child, supplier, quantity=1, taxless_base_unit_price=price, shop=shop) cache.clear() # Third product now sold in greatest quantity assert product_3 == general.get_best_selling_products(context, n_products=n_products)[0]
def test_category_product_filters_4(browser, live_server, settings): """ Do not show manufacturer option if there is any product """ cache.clear() # Avoid cache from past tests shop, first_cat, second_cat, third_cat, first_manufacturer = initialize_db() # remove manufacturers from all products Product.objects.all().update(manufacturer=None) # show manufacturer filter set_configuration( category=first_cat, data={ "override_default_configuration": True, "sort_products_by_name": True, "sort_products_by_name_ordering": 1, "sort_products_by_price": True, "sort_products_by_price_ordering": 2, "filter_products_by_manufacturer": True } ) # initialize test and go to front page browser = initialize_front_browser_test(browser, live_server) # check that front page actually loaded wait_until_condition(browser, lambda x: x.is_text_present("Welcome to Default!")) url = reverse("shuup:category", kwargs={"pk": first_cat.pk, "slug": first_cat.slug}) browser.visit("%s%s" % (live_server, url)) wait_until_condition(browser, lambda x: x.is_text_present("First Category")) wait_until_condition(browser, lambda x: x.is_text_present("Sort")) assert not browser.is_text_present("Manufacturers") # Since there is no product with manufacturer # add the manufacturer to the last product so the manufacturer filter is show last_product = Product.objects.last() last_product.manufacturer = first_manufacturer last_product.save() browser.visit("%s%s" % (live_server, url)) assert browser.is_text_present("Manufacturers") # set the shop product hidden shop_product = last_product.get_shop_instance(shop) shop_product.visibility = ShopProductVisibility.NOT_VISIBLE shop_product.save() # the manufacturer filter is removed browser.visit("%s%s" % (live_server, url)) assert not browser.is_text_present("Manufacturers")
def test_product_searchability(rf, visibility, show_in_search): cache.clear() view = SearchView.as_view() name = "Savage Garden" sku = UNLIKELY_STRING shop = get_default_shop() product = create_product(sku, name=name, shop=shop) shop_product = product.get_shop_instance(shop) shop_product.visibility = visibility shop_product.save() resp = view(apply_request_middleware(rf.get("/", {"q": "savage"}))) assert (name in resp.rendered_content) == show_in_search
def test_simple_search_with_non_public_products(rf): cache.clear() shop = get_default_shop() name = "Some Test Name For Product" product = create_product("sku", name=name, shop=shop) shop_product = product.get_shop_instance(shop) shop_product.visibility = ShopProductVisibility.SEARCHABLE shop_product.visibility_limit = ProductVisibility.VISIBLE_TO_LOGGED_IN shop_product.save() view = SearchView.as_view() request = apply_request_middleware(rf.get("/", {"q": "Test name"})) request.customer = create_random_person() resp = view(request) assert bool(name in resp.rendered_content)
def test_category_product_filters(browser, live_server, settings): activate("en") # initialize cache.clear() shop = get_default_shop() for name, identifier in CATEGORY_DATA: category = Category() category.name = name category.identifier = identifier category.status = CategoryStatus.VISIBLE category.save() category.shops.add(shop) for name, identifier in MANUFACTURER_DATA: Manufacturer.objects.create(name=name, identifier=identifier) first_cat = Category.objects.filter(identifier="cat-1").first() second_cat = Category.objects.filter(identifier="cat-2").first() third_cat = Category.objects.filter(identifier="cat-3").first() assert first_cat.pk != second_cat.pk for name, sku, price in FIRST_CATEGORY_PRODUCT_DATA: product = create_orderable_product(name, sku, price=price) shop_product = product.get_shop_instance(shop) cat = Category.objects.first() shop_product.primary_category = first_cat shop_product.save() shop_product.categories.add(first_cat) # Add some variation products add_variations(shop, Product.objects.filter(sku="test-sku-1").first(), ["Black", "Yellow"], ["Big", "Small"]) add_variations(shop, Product.objects.filter(sku="test-sku-2").first(), ["Brown", "Pink"], ["S", "L", "XL"]) add_variations(shop, Product.objects.filter(sku="test-sku-3").first(), ["Brown", "Black"], ["S", "L", "XL", "Big"]) for i in range(1, 14): product = create_orderable_product("Test product", "sku-%s" % i, price=i) shop_product = product.get_shop_instance(shop) cat = Category.objects.first() shop_product.primary_category = second_cat shop_product.save() shop_product.categories.add(second_cat) # Set manufacturer for first product only first_manufacturer = Manufacturer.objects.first() Product.objects.filter(sku="test-sku-1").update( manufacturer_id=first_manufacturer.id) # initialize test and go to front page browser = initialize_front_browser_test(browser, live_server) # check that front page actually loaded wait_until_condition(browser, lambda x: x.is_text_present("Welcome to Default!")) url = reverse("shuup:category", kwargs={ "pk": first_cat.pk, "slug": first_cat.slug }) browser.visit("%s%s" % (live_server, url)) wait_until_condition(browser, lambda x: x.is_text_present("First Category")) wait_until_condition(browser, lambda x: x.is_text_present("Sort")) assert not browser.is_text_present( "Manufacturers") # Since not in default configuration hide_sorts_for_shop(browser, shop) show_sorts_for_the_category_only(browser, first_cat) # All sorts for first_cat is available test sorting sort_category_products_test(browser, first_cat) manufacturer_filter_test(browser, first_cat, first_manufacturer) variations_filter_test(browser, first_cat) categories_filter_test(browser, first_cat, second_cat, third_cat) second_category_sort_test(browser, live_server, shop, second_cat) second_category_sort_with_price_filter(browser, second_cat)
def test_get_best_selling_products_cache_bump(): supplier = get_default_supplier() shop = get_default_shop() shop2 = get_shop(identifier="shop2") product1 = create_product("product1", shop, supplier, 10) product2 = create_product("product2", shop, supplier, 20) product3 = create_product("product3", shop2, supplier, 20) shop1_product1 = product1.get_shop_instance(shop) shop2_product3 = product3.get_shop_instance(shop2) create_order_with_product(product1, supplier, quantity=1, taxless_base_unit_price=10, shop=shop) create_order_with_product(product2, supplier, quantity=2, taxless_base_unit_price=20, shop=shop) create_order_with_product(product3, supplier, quantity=2, taxless_base_unit_price=30, shop=shop2) cache.clear() from shuup.front.template_helpers import general context = get_jinja_context() set_cached_value_mock = mock.Mock(wraps=context_cache.set_cached_value) def set_cache_value(key, value, timeout=None): if "best_selling_products" in key: return set_cached_value_mock(key, value, timeout) with mock.patch.object(context_cache, "set_cached_value", new=set_cache_value): assert set_cached_value_mock.call_count == 0 assert general.get_best_selling_products(context, 2, orderable_only=False) assert set_cached_value_mock.call_count == 1 # call again, the cache should be returned instead and the set_cached_value shouldn't be called again assert general.get_best_selling_products(context, 2, orderable_only=False) assert set_cached_value_mock.call_count == 1 # save the shop2 product and see whether the cache is bumped shop2_product3.save() # neve SHOULD be changed and things should be cached assert general.get_best_selling_products(context, 2, orderable_only=False) assert set_cached_value_mock.call_count == 1 # now change shop1 product, it should bump the cache shop1_product1.save() assert general.get_best_selling_products(context, 2, orderable_only=False) assert set_cached_value_mock.call_count == 2
def test_best_selling_products_with_multiple_orders(): from shuup.front.template_helpers import general context = get_jinja_context() supplier = get_default_supplier() shop = get_default_shop() n_products = 2 price = 10 product_1 = create_product("test-sku-1", supplier=supplier, shop=shop, default_price=price) product_2 = create_product("test-sku-2", supplier=supplier, shop=shop, default_price=price) create_order_with_product(product_1, supplier, quantity=1, taxless_base_unit_price=price, shop=shop) create_order_with_product(product_2, supplier, quantity=1, taxless_base_unit_price=price, shop=shop) # Two initial products sold for cache_test in range(2): assert product_1 in general.get_best_selling_products( context, n_products=n_products) assert product_2 in general.get_best_selling_products( context, n_products=n_products) product_3 = create_product("test-sku-3", supplier=supplier, shop=shop, default_price=price) create_order_with_product(product_3, supplier, quantity=2, taxless_base_unit_price=price, shop=shop) # Third product sold in greater quantity cache.clear() assert product_3 in general.get_best_selling_products( context, n_products=n_products) create_order_with_product(product_1, supplier, quantity=4, taxless_base_unit_price=price, shop=shop) create_order_with_product(product_2, supplier, quantity=4, taxless_base_unit_price=price, shop=shop) cache.clear() # Third product outsold by first two products for cache_test in range(2): assert product_3 not in general.get_best_selling_products( context, n_products=n_products) children = [ create_product("SimpleVarChild-%d" % x, supplier=supplier, shop=shop) for x in range(5) ] for child in children: child.link_to_parent(product_3) create_order_with_product(child, supplier, quantity=1, taxless_base_unit_price=price, shop=shop) cache.clear() # Third product now sold in greatest quantity for cache_test in range(2): assert product_3 == general.get_best_selling_products( context, n_products=n_products)[0] # add a new product with discounted amount product_4 = create_product("test-sku-4", supplier=supplier, shop=shop, default_price=price) create_order_with_product(product_4, supplier, quantity=2, taxless_base_unit_price=price, shop=shop) from shuup.customer_group_pricing.models import CgpDiscount CgpDiscount.objects.create(shop=shop, product=product_4, group=AnonymousContact.get_default_group(), discount_amount_value=(price * 0.1))
def test_best_selling_products_with_multiple_orders(): context = get_jinja_context() supplier = get_default_supplier() shop = get_default_shop() n_products = 2 price = 10 product_1 = create_product("test-sku-1", supplier=supplier, shop=shop) product_2 = create_product("test-sku-2", supplier=supplier, shop=shop) create_order_with_product(product_1, supplier, quantity=1, taxless_base_unit_price=price, shop=shop) create_order_with_product(product_2, supplier, quantity=1, taxless_base_unit_price=price, shop=shop) cache.clear() # Two initial products sold assert product_1 in general.get_best_selling_products( context, n_products=n_products) assert product_2 in general.get_best_selling_products( context, n_products=n_products) product_3 = create_product("test-sku-3", supplier=supplier, shop=shop) create_order_with_product(product_3, supplier, quantity=2, taxless_base_unit_price=price, shop=shop) cache.clear() # Third product sold in greater quantity assert product_3 in general.get_best_selling_products( context, n_products=n_products) create_order_with_product(product_1, supplier, quantity=4, taxless_base_unit_price=price, shop=shop) create_order_with_product(product_2, supplier, quantity=4, taxless_base_unit_price=price, shop=shop) cache.clear() # Third product outsold by first two products assert product_3 not in general.get_best_selling_products( context, n_products=n_products) children = [ create_product("SimpleVarChild-%d" % x, supplier=supplier, shop=shop) for x in range(5) ] for child in children: child.link_to_parent(product_3) create_order_with_product(child, supplier, quantity=1, taxless_base_unit_price=price, shop=shop) cache.clear() # Third product now sold in greatest quantity assert product_3 == general.get_best_selling_products( context, n_products=n_products)[0]
def test_order_flow_with_phases(get_shipping_method, shipping_data, get_payment_method, payment_data): cache.clear() create_default_order_statuses() populate_if_required() c = SmartClient() _populate_client_basket(c) # Create methods shipping_method = get_shipping_method() payment_method = get_payment_method() # Resolve paths addresses_path = reverse("shuup:checkout", kwargs={"phase": "addresses"}) methods_path = reverse("shuup:checkout", kwargs={"phase": "methods"}) shipping_path = reverse("shuup:checkout", kwargs={"phase": "shipping"}) payment_path = reverse("shuup:checkout", kwargs={"phase": "payment"}) confirm_path = reverse("shuup:checkout", kwargs={"phase": "confirm"}) # Phase: Addresses addresses_soup = c.soup(addresses_path) inputs = fill_address_inputs(addresses_soup, with_company=False) response = c.post(addresses_path, data=inputs) assert response.status_code == 302, "Address phase should redirect forth to methods" # Phase: Methods response = c.get(methods_path) assert response.status_code == 200 response = c.post(methods_path, data={ "shipping_method": shipping_method.pk, "payment_method": payment_method.pk }) assert response.status_code == 302, "Methods phase should redirect forth" if isinstance(shipping_method.carrier, CarrierWithCheckoutPhase): # Phase: Shipping response = c.get(shipping_path) assert response.status_code == 200 response = c.post(shipping_path, data=shipping_data) assert response.status_code == 302, "Payments phase should redirect forth" if isinstance(payment_method.payment_processor, PaymentWithCheckoutPhase): # Phase: payment response = c.get(payment_path) assert response.status_code == 200 response = c.post(payment_path, data=payment_data) assert response.status_code == 302, "Payments phase should redirect forth" # Phase: Confirm assert Order.objects.count() == 0 confirm_soup = c.soup(confirm_path) response = c.post(confirm_path, data=extract_form_fields(confirm_soup)) assert response.status_code == 302, "Confirm should redirect forth" order = Order.objects.first() if isinstance(shipping_method.carrier, CarrierWithCheckoutPhase): assert order.shipping_data.get("input_value") == "20540" if isinstance(payment_method.payment_processor, PaymentWithCheckoutPhase): assert order.payment_data.get("input_value") assert order.payment_status == PaymentStatus.NOT_PAID # Resolve order specific paths (payment and complete) process_payment_path = reverse("shuup:order_process_payment", kwargs={ "pk": order.pk, "key": order.key }) process_payment_return_path = reverse( "shuup:order_process_payment_return", kwargs={ "pk": order.pk, "key": order.key }) order_complete_path = reverse("shuup:order_complete", kwargs={ "pk": order.pk, "key": order.key }) # Check confirm redirection to payment page assert response.url.endswith(process_payment_path), ( "Confirm should have redirected to payment page") # Visit payment page response = c.get(process_payment_path) assert response.status_code == 302, "Payment page should redirect forth" assert response.url.endswith(process_payment_return_path) # Check payment return response = c.get(process_payment_return_path) assert response.status_code == 302, "Payment return should redirect forth" assert response.url.endswith(order_complete_path) # Check payment status has changed to DEFERRED order = Order.objects.get(pk=order.pk) # reload assert order.payment_status == PaymentStatus.DEFERRED
def setup_function(fn): cache.clear()
def test_get_best_selling_products(admin_user): shop1 = get_default_shop() shop2 = get_shop(True) person1 = create_random_person() person1.user = admin_user person1.save() supplier = create_simple_supplier("supplier1") client = _get_client(admin_user) # list best selling products response = client.get("/api/shuup/front/shop_products/best_selling/", {"shop": shop2.pk}) assert response.status_code == status.HTTP_200_OK products = json.loads(response.content.decode("utf-8")) assert len(products) == 0 # THIS IS IMPORTANT! cache.clear() products = [ create_product("Standard-%d" % x, supplier=supplier, shop=shop2) for x in range(10) ] # create 1 product with 4 variations parent_product = create_product("ParentProduct1", supplier=supplier, shop=shop2) children = [ create_product("SimpleVarChild-%d" % x, supplier=supplier, shop=shop2) for x in range(4) ] for child in children: child.link_to_parent(parent_product) best_selling = defaultdict(int) # create orders with standard products for p_index in range(len(products)): order = create_empty_order(shop=shop2) order.save() qty = (len(products) - p_index) add_product_to_order(order, supplier, products[p_index], qty, Decimal(1.0)) order.create_shipment_of_all_products() order.status = OrderStatus.objects.get_default_complete() order.save(update_fields=("status", )) best_selling[products[p_index].id] = qty # create orders with variation products - the parent product is counted instead of its children for p_index in range(2): variation = random.choice(children) qty = 5 order = create_empty_order(shop=shop2) order.save() add_product_to_order(order, supplier, variation, qty, Decimal(1.0)) order.create_shipment_of_all_products() order.status = OrderStatus.objects.get_default_complete() order.save(update_fields=("status", )) best_selling[parent_product.id] = best_selling[parent_product.id] + qty # get the top 100 best selling products response = client.get("/api/shuup/front/shop_products/best_selling/", { "shop": shop2.pk, "limit": 100 }) assert response.status_code == status.HTTP_200_OK products = json.loads(response.content.decode("utf-8")) assert len(products) == len( best_selling) # as we added less then 100, this must be true # check the if all IDS are part of best selling for ix in range(len(products)): assert products[ix]["product_id"] in best_selling.keys() # get the top 5 best selling products response = client.get("/api/shuup/front/shop_products/best_selling/", { "shop": shop2.pk, "limit": 5 }) assert response.status_code == status.HTTP_200_OK products = json.loads(response.content.decode("utf-8")) assert len(products) == 5 sorted_best_selling_ids = [ prod[0] for prod in sorted(best_selling.items(), key=lambda prod: -prod[1]) ][:5] # check the if all the 5 best sellers are part of best selling for ix in range(len(products)): assert products[ix]["product_id"] in sorted_best_selling_ids
def setup_function(fn): activate("en") cache.clear()
def test_get_best_selling_products_per_supplier(): from shuup.front.template_helpers import general context = get_jinja_context() # No products sold assert len(list(general.get_best_selling_products(context, n_products=3))) == 0 shop = get_default_shop() supplier = get_default_supplier() supplier2 = Supplier.objects.create(name="supplier2", enabled=True) supplier2.shops.add(shop) product1 = create_product("product1", shop, supplier, 10) product2 = create_product("product2", shop, supplier2, 20) create_order_with_product(product1, supplier, quantity=1, taxless_base_unit_price=10, shop=shop) create_order_with_product(product2, supplier2, quantity=2, taxless_base_unit_price=20, shop=shop) cache.clear() # Two products sold, but only one supplier for cache_test in range(2): best_selling_products = list( general.get_best_selling_products(context, n_products=3, supplier=supplier)) assert len(best_selling_products) == 1 assert product1 in best_selling_products assert product2 not in best_selling_products # Two products sold, but only one supplier for cache_test in range(2): best_selling_products = list( general.get_best_selling_products(context, n_products=3, supplier=supplier2)) assert len(best_selling_products) == 1 assert product1 not in best_selling_products assert product2 in best_selling_products # Make product 1 also sold by supplier2 shop_product = product1.get_shop_instance(shop) shop_product.suppliers.add(supplier2) cache.clear() for cache_test in range(2): best_selling_products = list( general.get_best_selling_products(context, n_products=3, supplier=supplier2)) assert len(best_selling_products ) == 1 # Since there isn't any orders yet for supplier 2 assert product2 in best_selling_products create_order_with_product(product1, supplier2, quantity=2, taxless_base_unit_price=20, shop=shop) cache.clear() for cache_test in range(2): best_selling_products = list( general.get_best_selling_products(context, n_products=3, supplier=supplier2)) assert len(best_selling_products) == 2 assert product1 in best_selling_products assert product2 in best_selling_products
def setup_function(fn): get_jwt_payload.cache_clear() cache.clear()
def test_get_best_selling_products(): from shuup.front.template_helpers import general context = get_jinja_context() # No products sold assert len(list(general.get_best_selling_products(context, n_products=3))) == 0 shop = get_default_shop() supplier = get_default_supplier() supplier2 = Supplier.objects.create(name="supplier2", enabled=True) supplier3 = Supplier.objects.create(name="supplier3", enabled=True) supplier2.shops.add(shop) supplier3.shops.add(shop) product1 = create_product("product1", shop, supplier, 10) product2 = create_product("product2", shop, supplier, 20) create_order_with_product(product1, supplier, quantity=1, taxless_base_unit_price=10, shop=shop) create_order_with_product(product2, supplier, quantity=2, taxless_base_unit_price=20, shop=shop) cache.clear() # Two products sold for cache_test in range(2): best_selling_products = list( general.get_best_selling_products(context, n_products=3)) assert len(best_selling_products) == 2 assert product1 in best_selling_products assert product2 in best_selling_products # Make order unorderable shop_product = product1.get_shop_instance(shop) shop_product.visibility = ShopProductVisibility.NOT_VISIBLE shop_product.save() cache.clear() for cache_test in range(2): best_selling_products = list( general.get_best_selling_products(context, n_products=3)) assert len(best_selling_products) == 1 assert product1 not in best_selling_products assert product2 in best_selling_products # add a new product with discounted amount product3 = create_product("product3", supplier=supplier, shop=shop, default_price=30) create_order_with_product(product3, supplier, quantity=1, taxless_base_unit_price=30, shop=shop) from shuup.customer_group_pricing.models import CgpDiscount CgpDiscount.objects.create(shop=shop, product=product3, group=AnonymousContact.get_default_group(), discount_amount_value=5) cache.clear() for cache_test in range(2): best_selling_products = list( general.get_best_selling_products(context, n_products=3, orderable_only=True)) assert len(best_selling_products) == 2 assert product1 not in best_selling_products assert product2 in best_selling_products assert product3 in best_selling_products