def _get_shop_product_sample_data():
    return {
        "shop": get_default_shop().id,
        "suppliers": [
            get_default_supplier().id
        ],
        "visibility": ShopProductVisibility.ALWAYS_VISIBLE.value,
        "purchasable": False,
        "visibility_limit": ProductVisibility.VISIBLE_TO_ALL.value,
        "visibility_groups": [
            create_random_contact_group().id,
            create_random_contact_group().id,
        ],
        "backorder_maximum": 0,
        "purchase_multiple": 1,
        "minimum_purchase_quantity": 1,
        "limit_shipping_methods": False,
        "limit_payment_methods": False,
        "shipping_methods": [],
        "payment_methods": [],
        "primary_category": get_default_category().id,
        "categories": [
            get_default_category().id,
            CategoryFactory().id
        ],
        "default_price_value": 12.45,
        "minimum_price_value": 5.35
    }
Exemple #2
0
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
Exemple #3
0
def test_carousel_create(browser, admin_user, live_server, settings):
    shop = factories.get_default_shop()
    filer_image = factories.get_random_filer_image()
    factories.get_default_category()

    initialize_admin_browser_test(browser, live_server, settings, shop=shop)
    wait_until_condition(browser, lambda x: x.is_text_present("Welcome!"))

    assert not Carousel.objects.exists()

    browser.visit(live_server + "/sa/carousels/new")
    wait_until_condition(browser, lambda x: x.is_text_present("New Carousel"))
    browser.fill("base-name", "Carrot")
    click_element(browser, "button[form='carousel_form']")
    wait_until_appeared(browser, "div[class='message success']")

    assert Carousel.objects.count() == 1
    carousel = Carousel.objects.first()

    browser.visit(live_server + "/sa/carousels/%d/" % carousel.pk)
    wait_until_condition(browser, lambda x: x.is_text_present(carousel.name))
    click_element(browser, "a[href='#slides-section']")
    wait_until_appeared(browser, ".slide-add-new-panel")

    # add 1 slide
    click_element(browser, ".slide-add-new-panel")
    wait_until_condition(browser, lambda x: x.is_text_present("Slide 1"))
    wait_until_appeared(browser, "a[href='#collapse1']")
    click_element(browser, "a[href='#collapse1']")

    browser.find_by_css("#slide_1-en [name='slides-__slide_prefix__-caption__en']").fill("New Slide")
    click_element(browser, "[name='slides-__slide_prefix__-category_link'] + .select2")
    wait_until_appeared(browser, ".select2-container #select2-id_slides-__slide_prefix__-category_link-results li")
    click_element(browser, ".select2-container #select2-id_slides-__slide_prefix__-category_link-results li:last-child")

    browser.find_by_css("#slide_1-en [data-dropzone='true']").click()
    wait_until_condition(browser, lambda b: len(b.windows) == 2)
    # change to the media browser window
    browser.windows.current = browser.windows[1]
    # click to select the picture
    wait_until_appeared(browser, "a.file-preview")
    click_element(browser, "a.file-preview")
    # back to the main window
    wait_until_condition(browser, lambda b: len(b.windows) == 1)
    browser.windows.current = browser.windows[0]
    wait_until_appeared(browser, ".dz-image img[alt='%s']" % filer_image.name)

    click_element(browser, "button[form='carousel_form']")
    wait_until_appeared(browser, "div[class='message success']")
def test_manufacturer_filter_get_fields(rf):
    shop = factories.get_default_shop()

    request = apply_request_middleware(rf.get("/"))
    assert ManufacturerProductListFilter().get_fields(request, None) is None

    manufacturer = Manufacturer.objects.create(name="Random Brand Inc")
    assert ManufacturerProductListFilter().get_fields(request, None) is None

    category = factories.get_default_category()
    product = factories.create_product("sku", shop=shop)
    shop_product = product.get_shop_instance(shop=shop)
    shop_product.primary_category = category
    shop_product.save()

    assert ManufacturerProductListFilter().get_fields(request, category) is None

    # Now once we link manufacturer to product we should get
    # form field for manufacturer
    product.manufacturer = manufacturer
    product.save()
    form_field = ManufacturerProductListFilter().get_fields(request, category)[0][1]
    assert form_field is not None
    assert form_field.label == "Manufacturers"
    assert len(form_field.queryset) == 1

    with override_settings(SHUUP_FRONT_OVERRIDE_SORTS_AND_FILTERS_LABELS_LOGIC={"manufacturers": "Brands"}):
        form_field = ManufacturerProductListFilter().get_fields(request, category)[0][1]
        assert form_field is not None
        assert form_field.label == "Brands"
        assert len(form_field.queryset) == 1
def test_category_product_in_basket_condition(rf):
    request, shop, group = initialize_test(rf, False)
    basket = get_basket(request)
    supplier = get_default_supplier()
    category = get_default_category()
    product = create_product("The Product", shop=shop, default_price="200", supplier=supplier)
    basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1)

    shop_product = product.get_shop_instance(shop)
    assert category not in shop_product.categories.all()

    condition = CategoryProductsBasketCondition.objects.create(
        category=category, operator=ComparisonOperator.EQUALS, quantity=1)

    # No match the product does not have the category
    assert not condition.matches(basket, [])

    shop_product.categories.add(category)
    assert condition.matches(basket, [])

    basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1)
    assert not condition.matches(basket, [])

    condition.operator = ComparisonOperator.GTE
    condition.save()

    assert condition.matches(basket, [])
def test_products_form_add_multiple_products():
    shop = get_default_shop()
    category = get_default_category()
    category.shops.add(shop)
    product_ids = []
    for x in range(0, 15):
        product = create_product("%s" % x, shop=shop)
        product_ids.append(product.id)

    for x in range(0, 5):
        product = create_product("parent_%s" % x, shop=shop, mode=ProductMode.SIMPLE_VARIATION_PARENT)
        for y in range(0, 3):
            child_product = create_product("child_%s_%s" % (x, y), shop=shop)
            child_product.link_to_parent(product)
        product_ids.append(product.id)

    assert (category.shop_products.count() == 0)
    data = {
        "additional_products": ["%s" % product_id for product_id in product_ids]
    }
    form = CategoryProductForm(shop=shop, category=category, data=data)
    form.full_clean()
    form.save()

    category.refresh_from_db()
    assert (category.shop_products.count() == 35)  # 15 normal products and 5 parents with 3 children each
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_products_form_add():
    shop = get_default_shop()
    category = get_default_category()
    category.visibility = CategoryVisibility.VISIBLE_TO_LOGGED_IN
    category.status = CategoryStatus.INVISIBLE
    category.shops.add(shop)
    category.save()
    product = create_product("test_product", shop=shop)
    shop_product = product.get_shop_instance(shop)
    product_category = product.category
    assert (product_category is None)
    assert (category not in shop_product.categories.all())
    data = {
        "primary_products": ["%s" % product.id]
    }
    form = CategoryProductForm(shop=shop, category=category, data=data)
    form.full_clean()
    form.save()
    shop_product.refresh_from_db()
    product.refresh_from_db(())
    assert (shop_product.primary_category == category)
    assert (category in shop_product.categories.all())
    assert (shop_product.visibility_limit.value == category.visibility.value)
    assert (shop_product.visible == False)
    assert (product.category is None)
def test_products_form_remove():
    shop = get_default_shop()
    category = get_default_category()
    category.shops.add(shop)
    product = create_product("test_product", shop=shop)
    shop_product = product.get_shop_instance(shop)
    shop_product.primary_category = category
    shop_product.save()
    shop_product.categories.add(category)

    shop_product.refresh_from_db()
    assert (shop_product.primary_category == category)
    assert (shop_product.categories.count() == 1)
    assert (shop_product.categories.first() == category)

    data = {
        "remove_products": ["%s" % shop_product.id]
    }
    form = CategoryProductForm(shop=shop, category=category, data=data)
    form.full_clean()
    form.save()

    category.refresh_from_db()
    assert (category.shop_products.count() == 0)
    shop_product.refresh_from_db()
    assert (shop_product.primary_category is None)
    assert (shop_product.categories.count() == 0)
Exemple #10
0
def test_category_filter(rf):
    request, shop, group = initialize_test(rf, False)

    cat = get_default_category()
    cat_filter = CategoryFilter.objects.create()
    cat_filter.categories.add(cat)
    cat_filter.save()

    assert cat_filter.values.first() == cat
    category = Category.objects.create(
        parent=None,
        identifier="testcat",
        name="catcat",
    )
    cat_filter.values = [cat, category]
    cat_filter.save()

    assert cat_filter.values.count() == 2

    product = create_product("Just-A-Product-Too", shop, default_price="200")
    shop_product = product.get_shop_instance(shop)
    shop_product.categories.add(cat)
    shop_product.save()

    assert cat_filter.filter_queryset(ShopProduct.objects.all()).exists()  # filter matches
def test_category_product_in_basket_condition(rf):
    request, shop, group = initialize_test(rf, False)
    basket = get_basket(request)
    supplier = get_default_supplier()
    category = get_default_category()
    product = create_product("The Product", shop=shop, default_price="200", supplier=supplier)
    basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1)
    basket.shipping_method = get_shipping_method(shop=shop)

    shop_product = product.get_shop_instance(shop)
    assert category not in shop_product.categories.all()

    condition = CategoryProductsBasketCondition.objects.create(operator=ComparisonOperator.EQUALS, quantity=1)
    condition.categories.add(category)

    # No match the product does not have the category
    assert not condition.matches(basket, [])

    category.shop_products.add(shop_product)
    assert condition.matches(basket, [])

    basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1)
    assert not condition.matches(basket, [])

    condition.operator = ComparisonOperator.GTE
    condition.save()

    assert condition.matches(basket, [])

    condition.excluded_categories.add(category)
    assert not condition.matches(basket, [])

    with pytest.raises(CampaignsInvalidInstanceForCacheUpdate):
        update_filter_cache("test", shop)
Exemple #12
0
def test_all_categories_view(rf, admin_user):
    shop = get_default_shop()
    supplier = get_default_supplier()
    category = get_default_category()
    product = get_default_product()
    request = apply_request_middleware(rf.get("/"))
    _check_product_count(request, 0)

    shop_product = product.get_shop_instance(shop)
    shop_product.categories.add(category)
    _check_product_count(request, 1)

    # Create few categories for better test results
    for i in range(10):
        cat = Category.objects.create(name=printable_gibberish())
        cat.shops.add(shop)

    new_product_count = random.randint(1, 3) + 1
    for i in range(1, new_product_count):
        product = create_product("sku-%s" % i, shop=shop, supplier=supplier, default_price=10)
        shop_product = product.get_shop_instance(shop)

        # Add random categories expect default category which we will make
        # hidden to make sure that products linked to hidden categories are
        # not listed
        shop_product.categories = Category.objects.exclude(id=category.pk).order_by("?")[:i]

    _check_product_count(request, new_product_count)

    category.status = CategoryStatus.INVISIBLE
    category.save()
    _check_product_count(request, new_product_count - 1)
Exemple #13
0
def test_category_create_with_parent(rf, admin_user):
    shop = get_default_shop()
    default_category = get_default_category()

    default_category.shops.clear()
    assert shop not in default_category.shops.all()
    with override_settings(LANGUAGES=[("en", "en")]):
        view = CategoryEditView.as_view()
        cat_name = "Random name"
        data = {
            "base-name__en": cat_name,
            "base-status": CategoryStatus.VISIBLE.value,
            "base-visibility": CategoryVisibility.VISIBLE_TO_ALL.value,
            "base-ordering": 1,
            "base-parent": default_category.pk
        }
        assert Category.objects.count() == 1
        request = apply_request_middleware(rf.post("/", data=data), user=admin_user, shop=shop)
        response = view(request, pk=None)
        if hasattr(response, "render"):
            response.render()
        assert response.status_code in [200, 302]
        assert Category.objects.count() == 1

        default_category.shops.add(shop)
        request = apply_request_middleware(rf.post("/", data=data), user=admin_user, shop=shop)
        response = view(request, pk=None)
        if hasattr(response, "render"):
            response.render()
        assert response.status_code in [200, 302]
        assert Category.objects.count() == 2
Exemple #14
0
def test_category_deletion(admin_user):
    admin = get_person_contact(admin_user)
    category = get_default_category()
    category.children.create(identifier="foo")
    shop_product = get_default_shop_product()
    shop_product.categories.add(category)
    shop_product.primary_category = category
    shop_product.save()

    configuration.set(None, get_all_seeing_key(admin), True)

    assert category.status == CategoryStatus.VISIBLE
    assert category.children.count() == 1

    with pytest.raises(NotImplementedError):
        category.delete()

    category.soft_delete()
    shop_product.refresh_from_db()
    shop_product.product.refresh_from_db()

    assert shop_product.categories.count() == 0
    assert shop_product.primary_category is None
    assert category.status == CategoryStatus.DELETED
    assert category.children.count() == 0
    # the child category still exists
    assert Category.objects.all_visible(customer=admin).count() == 1
    assert Category.objects.all_except_deleted().count() == 1
    configuration.set(None, get_all_seeing_key(admin), False)
Exemple #15
0
def test_category_links_plugin_with_customer(rf, show_all_categories):
    """
    Test plugin for categories that is visible for certain group
    """
    shop = get_default_shop()
    group = get_default_customer_group()
    customer = create_random_person()
    customer.groups.add(group)
    customer.save()

    request = rf.get("/")
    request.shop = get_default_shop()
    apply_request_middleware(request)
    request.customer = customer

    category = get_default_category()
    category.status = CategoryStatus.VISIBLE
    category.visibility = CategoryVisibility.VISIBLE_TO_GROUPS
    category.visibility_groups.add(group)
    category.shops.add(shop)
    category.save()

    vars = {"request": request}
    context = get_jinja_context(**vars)
    plugin = CategoryLinksPlugin({"categories": [category.pk], "show_all_categories": show_all_categories})
    assert category.is_visible(customer)
    assert category in plugin.get_context_data(context)["categories"]

    customer_without_groups = create_random_person()
    customer_without_groups.groups.clear()

    assert not category.is_visible(customer_without_groups)
    request.customer = customer_without_groups
    context = get_jinja_context(**vars)
    assert category not in plugin.get_context_data(context)["categories"]
Exemple #16
0
def test_category_removal():
    product = get_product()
    category = get_default_category()
    product.category = category
    product.save()
    with pytest.raises(NotImplementedError):
        category.delete()
    assert Product.objects.filter(pk=product.pk).exists()
Exemple #17
0
def test_shopproduct_primary_category_removal():
    product = get_product()
    category = get_default_category()
    sp = product.get_shop_instance(get_default_shop())
    sp.primary_category = category
    sp.save()
    with pytest.raises(NotImplementedError):
        category.delete()
    assert ShopProduct.objects.filter(pk=sp.pk).exists()
def test_list_view(rf, admin_user):
    shop = factories.get_default_shop()
    product = factories.create_product(sku="test", shop=shop)
    shop_product = product.get_shop_instance(shop)
    shop_product.primary_category = factories.get_default_category()
    shop_product.save()
    shop_product.categories.add(shop_product.primary_category)

    view = load("shuup.admin.modules.products.views:ProductListView").as_view()
    request = apply_request_middleware(rf.get("/", {
        "jq": json.dumps({"perPage": 100, "page": 1})
    }), user=admin_user)
    response = view(request)
    assert 200 <= response.status_code < 300

    data = json.loads(response.content.decode("utf-8"))
    product_data = [item for item in data["items"] if item["_id"] == shop_product.pk][0]
    assert product_data["primary_category"] == factories.get_default_category().name
    assert product_data["categories"] == factories.get_default_category().name
Exemple #19
0
def test_default_layout():
    vc = _get_basic_view_config()
    product = factories.create_product("test", name="Test product name")
    category = factories.get_default_category()

    placeholder_name = "wildhorse"
    context = {"request": get_request(), "product": product, "category": category}
    layout = vc.get_placeholder_layout(Layout, placeholder_name)
    assert isinstance(layout, Layout)
    assert layout.get_help_text({}) == layout.get_help_text(context)

    _add_plugin_and_test_save(vc, layout, placeholder_name, context)
def test_price_info_cache_bump(rf):
    initial_price = 10
    shop = factories.get_default_shop()
    tax = factories.get_default_tax()
    tax_class = factories.get_default_tax_class()
    product = factories.create_product(
        "product",
        shop=shop,
        supplier=factories.get_default_supplier(),
        default_price=initial_price
    )
    request = apply_request_middleware(rf.get("/"))

    def assert_cache_product():
        cache_price_info(request, product, 1, product.get_price_info(request))
        assert get_cached_price_info(request, product, 1).price == shop.create_price(initial_price)

    def assert_nothing_is_cached():
        # nothing is cached
        assert get_cached_price_info(request, product, 1) is None

    # cache the item
    assert_nothing_is_cached()
    assert_cache_product()

    # cache bumped - the cache should be dropped - then, cache again
    tax.save()
    assert_nothing_is_cached()
    assert_cache_product()

    # cache bumped - the cache should be dropped - then, cache again
    tax_class.save()
    assert_nothing_is_cached()
    assert_cache_product()

    # cache bumped - the cache should be dropped - then, cache again
    product.save()
    assert_nothing_is_cached()
    assert_cache_product()

    shop_product = product.get_shop_instance(shop)

    # cache bumped - the cache should be dropped - then, cache again
    shop_product.save()
    assert_nothing_is_cached()
    assert_cache_product()

    category = factories.get_default_category()

    # cache bumped - the cache should be dropped - then, cache again
    shop_product.categories.add(category)
    assert_nothing_is_cached()
    assert_cache_product()
Exemple #21
0
def test_category_layout():
    vc = _get_basic_view_config()
    category = factories.get_default_category()

    placeholder_name = "japanese"
    context = {"category": category}
    layout = vc.get_placeholder_layout(CategoryLayout, placeholder_name, context=context)
    assert isinstance(layout, CategoryLayout)
    assert layout.get_help_text({}) == ""  # Invalid context for help text
    assert category.name in layout.get_help_text(context)
    _assert_empty_layout(layout, placeholder_name)

    _add_plugin_and_test_save(vc, layout, placeholder_name, context)
Exemple #22
0
def test_ajax_select_view_with_categories(rf):
    activate("en")
    view = MultiselectAjaxView.as_view()
    results = _get_search_results(rf, view, "shuup.Category", "some str")
    assert len(results) == 0

    category = get_default_category()
    results = _get_search_results(rf, view, "shuup.Category", category.name)
    assert len(results) == 1

    category.soft_delete()
    results = _get_search_results(rf, view, "shuup.Category", category.name)
    assert len(results) == 0
def test_supplier_filter_get_fields(rf):
    shop = factories.get_default_shop()
    request = apply_request_middleware(rf.get("/"))
    category = factories.get_default_category()

    supplier = Supplier.objects.create(name="Mike Inc")
    supplier.shops.add(shop)
    assert SupplierProductListFilter().get_fields(request, None) is None

    product = factories.create_product("sku", shop=shop)
    shop_product = product.get_shop_instance(shop=shop)
    shop_product.primary_category = category
    shop_product.save()

    assert SupplierProductListFilter().get_fields(request, category) is None

    # Now once we link manufacturer to product we should get
    # form field for manufacturer
    shop_product.suppliers.add(supplier)
    form_field = SupplierProductListFilter().get_fields(request, category)[0][1]
    assert form_field is not None
    assert form_field.label == "Suppliers"
    assert len(form_field.queryset) == 1

    # Add second supplier for new product
    supplier2 = Supplier.objects.create(name="K Inc")
    supplier2.shops.add(shop)
    new_product = factories.create_product("sku1", shop=shop)
    new_shop_product = new_product.get_shop_instance(shop=shop)
    new_shop_product.suppliers.add(supplier2)

    # Still one with category since shop product not linked to category
    form_field = SupplierProductListFilter().get_fields(request, category)[0][1]
    assert form_field is not None
    assert len(form_field.queryset) == 1

    # Without category we get two results
    form_field = SupplierProductListFilter().get_fields(request, None)[0][1]
    assert form_field is not None
    assert len(form_field.queryset) == 2

    new_shop_product.categories.add(category)  # primary category shouldn't be required

    # Now with or without category we get 2 results
    form_field = SupplierProductListFilter().get_fields(request, category)[0][1]
    assert form_field is not None
    assert len(form_field.queryset) == 2

    form_field = SupplierProductListFilter().get_fields(request, None)[0][1]
    assert form_field is not None
    assert len(form_field.queryset) == 2
Exemple #24
0
def test_product_from_category_plugin(rf):
    shop = get_default_shop()
    category1 = get_default_category()
    category2 = CategoryFactory(status=CategoryStatus.VISIBLE)

    category1.shops.add(shop)
    category2.shops.add(shop)

    p1 = create_product("p1", shop, get_default_supplier(), "10")
    p2 = create_product("p2", shop, get_default_supplier(), "20")
    p3 = create_product("p3", shop, get_default_supplier(), "30")

    sp1 = p1.get_shop_instance(shop)
    sp2 = p2.get_shop_instance(shop)
    sp3 = p3.get_shop_instance(shop)

    sp1.categories.add(category1)
    sp2.categories.add(category1)
    sp3.categories.add(category2)

    context = get_context(rf)
    plugin = ProductsFromCategoryPlugin({
        "category": category1.pk
    })
    context_products = plugin.get_context_data(context)["products"]
    assert p1 in context_products
    assert p2 in context_products
    assert p3 not in context_products

    # test the plugin form
    with override_current_theme_class(None):
        theme = get_current_theme(get_default_shop())
        cell = LayoutCell(theme, ProductsFromCategoryPlugin.identifier, sizes={"md": 8})
        lcfg = LayoutCellFormGroup(layout_cell=cell, theme=theme, request=apply_request_middleware(rf.get("/")))
        assert not lcfg.is_valid()

        lcfg = LayoutCellFormGroup(
            data={
                "general-cell_width": "8",
                "general-cell_align": "pull-right",
                "plugin-count": 4,
                "plugin-category": category2.pk
            },
            layout_cell=cell,
            theme=theme,
            request=apply_request_middleware(rf.get("/"))
        )
        assert lcfg.is_valid()
        lcfg.save()
        assert cell.config["category"] == str(category2.pk)
Exemple #25
0
def test_category_links_plugin_show_all(rf):
    """
    Test that show_all_categories forces plugin to return all visible categories
    """
    category = get_default_category()
    category.status = CategoryStatus.VISIBLE
    category.shops.add(get_default_shop())
    category.save()
    context = get_context(rf)
    plugin = CategoryLinksPlugin({"show_all_categories": False})
    assert context["request"].customer.is_anonymous
    assert context["request"].shop in category.shops.all()
    assert not plugin.get_context_data(context)["categories"]

    plugin = CategoryLinksPlugin({"show_all_categories": True})
    assert category in plugin.get_context_data(context)["categories"]
def _get_custom_order(regular_user, **kwargs):
    prices_include_tax = kwargs.pop("prices_include_tax", False)
    include_basket_campaign = kwargs.pop("include_basket_campaign", False)
    include_catalog_campaign = kwargs.pop("include_catalog_campaign", False)

    shop = get_shop(prices_include_tax=prices_include_tax)
    supplier = get_simple_supplier()

    if include_basket_campaign:
        _add_basket_campaign(shop)

    if include_catalog_campaign:
        _add_catalog_campaign(shop)
    _add_taxes()

    contact = get_person_contact(regular_user)
    source = BasketishOrderSource(shop)
    source.status = get_initial_order_status()
    source.customer = contact

    ctx = get_pricing_module().get_context_from_data(shop, contact)
    for product_data in _get_product_data():
        quantity = product_data.pop("quantity")
        product = create_product(
            sku=product_data.pop("sku"),
            shop=shop,
            supplier=supplier,
            stock_behavior=StockBehavior.STOCKED,
            tax_class=get_default_tax_class(),
            **product_data)
        shop_product = product.get_shop_instance(shop)
        shop_product.categories.add(get_default_category())
        shop_product.save()
        supplier.adjust_stock(product.id, INITIAL_PRODUCT_QUANTITY)
        pi = product.get_price_info(ctx)
        source.add_line(
            type=OrderLineType.PRODUCT,
            product=product,
            supplier=supplier,
            quantity=quantity,
            base_unit_price=pi.base_unit_price,
            discount_amount=pi.discount_amount
        )

    oc = OrderCreator()
    order = oc.create_order(source)
    return order
Exemple #27
0
def test_slide_links():
    test_carousel = Carousel.objects.create(name="test")
    test_image_1 = Image.objects.create(original_filename="slide1.jpg")
    with translation.override("en"):
        test_slide = Slide.objects.create(carousel=test_carousel, name="test", image=test_image_1)

    # Test external link
    assert len(test_carousel.slides.all()) == 1
    test_link = "http://example.com"
    test_slide.external_link = test_link
    test_slide.save()
    assert test_slide.get_translated_field("external_link") == test_link
    assert test_slide.get_link_url() == test_link

    # Test Product url and link priorities
    test_product = get_default_product()
    test_slide.product_link = test_product
    test_slide.save()
    assert test_slide.get_link_url() == test_link
    test_slide.external_link = None
    test_slide.save()
    assert test_slide.get_link_url().startswith("/p/")  # Close enough...

    # Test Category url and link priorities
    test_category = get_default_category()
    test_slide.category_link = test_category
    test_slide.save()
    assert test_slide.get_link_url().startswith("/p/")  # Close enough...
    test_slide.product_link = None
    test_slide.save()
    assert test_slide.get_link_url().startswith("/c/")  # Close enough...

    # Test CMS page url and link priorities
    attrs = {"url": "test", "shop": get_default_shop()}
    test_page = create_page(**attrs)
    test_slide.cms_page_link = test_page
    test_slide.save()
    assert test_slide.get_link_url().startswith("/c/")  # Close enough...
    test_slide.category_link = None
    test_slide.save()
    assert test_slide.get_link_url().startswith("/test/")

    # Check that external link overrides everything
    test_slide.external_link = test_link
    test_slide.save()
    assert test_slide.get_link_url() == test_link
def test_products_form_update_default_category():
    shop = get_default_shop()
    category = get_default_category()
    category.shops.add(shop)
    product = create_product("test_product", shop=shop)
    shop_product = product.get_shop_instance(shop)
    assert (category not in shop_product.categories.all())
    data = {
        "primary_products": ["%s" % product.id],
        "update_product_category": True
    }
    form = CategoryProductForm(shop=shop, category=category, data=data)
    form.full_clean()
    form.save()
    shop_product.refresh_from_db()
    product.refresh_from_db()
    assert (shop_product.primary_category == category)
    assert (category in shop_product.categories.all())
Exemple #29
0
def test_category_links_plugin(rf):
    """
    Test that the plugin only displays visible categories
    with shop (since we can't have a request without shop
    or customer)
    """
    category = get_default_category()
    context = get_context(rf)
    plugin = CategoryLinksPlugin({"show_all_categories": True})
    assert context["request"].customer.is_anonymous
    assert category not in plugin.get_context_data(context)["categories"]

    category.status = CategoryStatus.VISIBLE
    category.shops.add(get_default_shop())
    category.save()
    assert context["request"].customer.is_anonymous
    assert context["request"].shop in category.shops.all()
    assert category in plugin.get_context_data(context)["categories"]
def test_recently_viewed_products(browser, live_server, settings):
    shop = get_default_shop()
    category = get_default_category()
    category.shops.add(shop)
    category.status = CategoryStatus.VISIBLE
    category.save()
    category_url = reverse("shuup:category", kwargs={"pk": category.pk, "slug": category.slug})
    browser = initialize_front_browser_test(browser, live_server)
    for i in range(1, 7):
        product = new_product(i, shop, category)
        product_url = reverse("shuup:product", kwargs={"pk": product.pk, "slug": product.slug})
        browser.visit(live_server + product_url)
        wait_until_appeared(browser, ".product-main")
        browser.visit(live_server + category_url)
        wait_until_appeared(browser, ".categories-nav")
        items = browser.find_by_css(".recently-viewed li")
        assert items.first.text == product.name, "recently clicked product on top"
        assert len(items) == min(i, 5)
Exemple #31
0
def test_all_categories_view(rf, reindex_catalog):
    shop = get_default_shop()
    supplier = get_default_supplier()
    category = get_default_category()
    product = get_default_product()
    request = apply_request_middleware(rf.get("/"))
    reindex_catalog()
    _check_product_count(request, 0)

    shop_product = product.get_shop_instance(shop)
    shop_product.categories.add(category)
    reindex_catalog()
    _check_product_count(request, 1)

    # Create few categories for better test results
    for i in range(10):
        cat = Category.objects.create(name=printable_gibberish())
        cat.shops.add(shop)

    new_product_count = random.randint(1, 3) + 1
    for i in range(1, new_product_count):
        product = create_product("sku-%s" % i,
                                 shop=shop,
                                 supplier=supplier,
                                 default_price=10)
        shop_product = product.get_shop_instance(shop)

        # Add random categories expect default category which we will make
        # hidden to make sure that products linked to hidden categories are
        # not listed
        shop_product.categories.set(
            Category.objects.exclude(id=category.pk).order_by("?")[:i])

    reindex_catalog()

    _check_product_count(request, new_product_count)

    category.status = CategoryStatus.INVISIBLE
    category.save()
    reindex_catalog()

    _check_product_count(request, new_product_count - 1)
Exemple #32
0
def test_slide_admin_form(rf, admin_user):
    translation.activate("en")
    shop = get_default_shop()
    category = get_default_category()
    assert category.shops.first() == shop
    page = create_page(**{"url": "test", "shop": shop})
    assert page.shop == shop

    test_carousel = Carousel.objects.create(name="test")
    request = apply_request_middleware(rf.get("/"))
    request.user = admin_user
    request.shop = shop
    slide_form = SlideForm(
        carousel=test_carousel,
        languages=settings.LANGUAGES,
        default_language=settings.PARLER_DEFAULT_LANGUAGE_CODE,
        request=request)

    soup = BeautifulSoup(slide_form.as_table())
    options = soup.find(id="id_category_link").find_all("option")
    assert len(options) == 2
    assert options[0]["value"] == ""
    assert options[1]["value"] == "%s" % category.pk

    options = soup.find(id="id_cms_page_link").find_all("option")
    assert len(options) == 2
    assert options[0]["value"] == ""
    assert options[1]["value"] == "%s" % page.pk

    new_shop = get_shop(identifier="second-shop")
    category.shops = [new_shop]
    page.shop = new_shop
    page.save()

    soup = BeautifulSoup(slide_form.as_table())
    options = soup.find(id="id_category_link").find_all("option")
    assert len(options) == 1
    assert options[0]["value"] == ""

    options = soup.find(id="id_cms_page_link").find_all("option")
    assert len(options) == 1
    assert options[0]["value"] == ""
Exemple #33
0
def test_list_view(rf, admin_user):
    shop = factories.get_default_shop()
    supplier = get_simple_supplier()

    product = factories.create_product(sku="test", shop=shop, supplier=supplier)
    shop_product = product.get_shop_instance(shop)
    shop_product.primary_category = factories.get_default_category()
    shop_product.save()
    shop_product.categories.add(shop_product.primary_category)

    view = load("shuup.simple_supplier.admin_module.views:StocksListView").as_view()
    request = apply_request_middleware(rf.get("/", {
        "jq": json.dumps({"perPage": 100, "page": 1})
    }), user=admin_user)
    response = view(request)
    assert 200 <= response.status_code < 300

    data = json.loads(response.content.decode("utf-8"))
    for item in data["items"]:
        assert item["_url"] == ""
Exemple #34
0
def test_products_form_add():
    shop = get_default_shop()
    category = get_default_category()
    category.visibility = CategoryVisibility.VISIBLE_TO_LOGGED_IN
    category.status = CategoryStatus.INVISIBLE
    category.shops.add(shop)
    category.save()
    product = create_product("test_product", shop=shop)
    shop_product = product.get_shop_instance(shop)
    assert (category not in shop_product.categories.all())
    data = {"primary_products": ["%s" % product.id]}
    form = CategoryProductForm(shop=shop, category=category, data=data)
    form.full_clean()
    form.save()
    shop_product.refresh_from_db()
    product.refresh_from_db(())
    assert (shop_product.primary_category == category)
    assert (category in shop_product.categories.all())
    assert (shop_product.visibility_limit.value == category.visibility.value)
    assert (shop_product.visibility == ShopProductVisibility.NOT_VISIBLE)
Exemple #35
0
def test_products_form_add_multiple_products():
    shop = get_default_shop()
    category = get_default_category()
    category.shops.add(shop)
    product_ids = []
    for x in range(0, 15):
        product = create_product("%s" % x, shop=shop)
        product_ids.append(product.id)

    assert (category.shop_products.count() == 0)
    data = {
        "additional_products":
        ["%s" % product_id for product_id in product_ids]
    }
    form = CategoryProductForm(shop=shop, category=category, data=data)
    form.full_clean()
    form.save()

    category.refresh_from_db()
    assert (category.shop_products.count() == len(product_ids))
Exemple #36
0
def test_generate_slugs_for_category():
    activate("en")
    category = get_default_category()
    assert category.slug == slugify(category.name)
    default_slug = category.slug

    # Test that slug is only generated when it's empty on save
    new_name = "Some new cat name"
    category.name = new_name
    category.save()
    assert category.slug == default_slug, "Old slug"
    category.slug = ""
    category.save()
    assert category.slug == slugify(new_name), "New slug generated"

    # Check that slug is not generated to other languages
    with pytest.raises(ObjectDoesNotExist):
        translation = category.get_translation("fi")
        translation.refresh_from_db(
        )  # If the translation object was returned from cache
Exemple #37
0
def test_products_for_category_cache_bump():
    from shuup.front.template_helpers import general
    supplier = get_default_supplier()
    shop = get_default_shop()
    category = get_default_category()
    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)
    ]
    category.shops.add(shop)

    for child in children:
        child.link_to_parent(products[0])

    context = get_jinja_context()

    for product in products:
        product.get_shop_instance(shop).categories.add(category)

    cache.clear()
    set_cached_value_mock = mock.Mock(wraps=context_cache.set_cached_value)
    with mock.patch.object(context_cache,
                           "set_cached_value",
                           new=set_cached_value_mock):
        assert set_cached_value_mock.call_count == 0

        assert general.get_products_for_categories(context, [category])
        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_products_for_categories(context, [category])
        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_products_for_categories(context, [category])
        assert set_cached_value_mock.call_count == 2
def test_category_product_in_basket_condition(rf):
    request, shop, group = initialize_test(rf, False)
    basket = get_basket(request)
    supplier = get_default_supplier()
    category = get_default_category()
    product = create_product("The Product",
                             shop=shop,
                             default_price="200",
                             supplier=supplier)
    basket.add_product(supplier=supplier,
                       shop=shop,
                       product=product,
                       quantity=1)
    basket.shipping_method = get_shipping_method(shop=shop)

    shop_product = product.get_shop_instance(shop)
    assert category not in shop_product.categories.all()

    condition = CategoryProductsBasketCondition.objects.create(
        operator=ComparisonOperator.EQUALS, quantity=1)
    condition.categories.add(category)

    # No match the product does not have the category
    assert not condition.matches(basket, [])

    shop_product.categories.add(category)
    assert condition.matches(basket, [])

    basket.add_product(supplier=supplier,
                       shop=shop,
                       product=product,
                       quantity=1)
    assert not condition.matches(basket, [])

    condition.operator = ComparisonOperator.GTE
    condition.save()

    assert condition.matches(basket, [])

    condition.excluded_categories.add(category)
    assert not condition.matches(basket, [])
Exemple #39
0
def test_sorts_and_filter_in_category_edit(rf, admin_user):
    get_default_shop()
    cache.clear()
    activate("en")
    with override_settings(SHUUP_ENABLE_MULTIPLE_SHOPS=False):
        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
Exemple #40
0
def test_category_selection_excluded(rf):
    default_price = 10
    request, product = _init_test_for_product(rf, default_price)

    category = factories.get_default_category()
    product_discount_amount = 4
    Discount.objects.create(
        shop=request.shop,
        active=True,
        exclude_selected_category=True,
        category=category,
        discount_amount_value=product_discount_amount,
    )
    # applies to all products, except the selected category
    assert product.get_price_info(request).price == request.shop.create_price(
        default_price - product_discount_amount)

    # Setting default category for product disables the discount
    product.get_shop_instance(request.shop).categories.add(category)
    assert product.get_price_info(request).price == request.shop.create_price(
        default_price)
Exemple #41
0
def test_products_form_update_default_category():
    shop = get_default_shop()
    category = get_default_category()
    category.shops.add(shop)
    product = create_product("test_product", shop=shop)
    shop_product = product.get_shop_instance(shop)
    product_category = product.category
    assert (product_category is None)
    assert (category not in shop_product.categories.all())
    data = {
        "primary_products": ["%s" % product.id],
        "update_product_category": True
    }
    form = CategoryProductForm(shop=shop, category=category, data=data)
    form.full_clean()
    form.save()
    shop_product.refresh_from_db()
    product.refresh_from_db()
    assert (shop_product.primary_category == category)
    assert (category in shop_product.categories.all())
    assert (product.category == category)
def test_recently_viewed_products(browser, live_server, settings):
    shop = get_default_shop()
    category = get_default_category()
    category.shops.add(shop)
    category.status = CategoryStatus.VISIBLE
    category.save()
    category_url = reverse("shuup:category", kwargs={"pk": category.pk, "slug": category.slug})

    products = []
    for i in range(1, 7):
        products.append(new_product(i, shop, category))

    browser = initialize_front_browser_test(browser, live_server)
    for i, product in enumerate(products, 1):
        product_url = reverse("shuup:product", kwargs={"pk": product.pk, "slug": product.slug})
        browser.visit(live_server + product_url)
        wait_until_appeared(browser, ".product-main")
        browser.visit(live_server + category_url)
        wait_until_appeared(browser, ".categories-nav")
        items = browser.find_by_css(".recently-viewed li")
        assert items.first.text == product.name, "recently clicked product on top"
        assert len(items) == min(i, 5)
Exemple #43
0
def _get_order(prices_include_tax=False, include_basket_campaign=False, include_catalog_campaign=False):
    shop = get_shop(prices_include_tax=prices_include_tax)
    supplier = get_simple_supplier()

    if include_basket_campaign:
        _add_basket_campaign(shop)

    if include_catalog_campaign:
        _add_catalog_campaign(shop)
    _add_taxes()

    source = BasketishOrderSource(shop)
    source.status = get_initial_order_status()
    ctx = get_pricing_module().get_context_from_data(shop, AnonymousContact())
    for product_data in _get_product_data():
        quantity = product_data.pop("quantity")
        product = create_product(
            sku=product_data.pop("sku"),
            shop=shop,
            supplier=supplier,
            stock_behavior=StockBehavior.STOCKED,
            tax_class=get_default_tax_class(),
            **product_data)
        shop_product = product.get_shop_instance(shop)
        shop_product.categories.add(get_default_category())
        shop_product.save()
        supplier.adjust_stock(product.id, INITIAL_PRODUCT_QUANTITY)
        pi = product.get_price_info(ctx)
        source.add_line(
            type=OrderLineType.PRODUCT,
            product=product,
            supplier=supplier,
            quantity=quantity,
            base_unit_price=pi.base_unit_price,
            discount_amount=pi.discount_amount
        )
    oc = OrderCreator()
    order = oc.create_order(source)
    return order
def test_slug_is_generate_from_translation():
    activate("en")
    category = get_default_category()
    assert category.slug == slugify(category.name)
    default_slug = category.slug
    activate("fi")
    category.set_current_language("fi")
    name_fi = "Joku nimi"
    category.name = name_fi
    category.save()
    assert category.slug == slugify(name_fi)

    # Make sure english still have default slug
    activate("en")
    category.set_current_language("en")
    assert category.slug == default_slug

    # Make sure Japanese does not have translations generated
    # Check that slug is not generated to other languages
    with pytest.raises(ObjectDoesNotExist):
        translation = category.get_translation("ja")
        translation.refresh_from_db()  # If the translation object was returned from cache
def test_mass_edit_products(rf, admin_user):
    shop = get_default_shop()
    supplier = get_default_supplier()
    product1 = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price="50")
    product2 = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price="501")

    category = get_default_category()
    shop_product1 = product1.get_shop_instance(shop)
    shop_product2 = product2.get_shop_instance(shop)

    # ensure no categories set
    assert shop_product1.primary_category is None
    assert shop_product2.primary_category is None

    request = apply_request_middleware(rf.post("/", data={"primary_category": category.pk}), user=admin_user)
    request.session["mass_action_ids"] = [shop_product1.pk, shop_product2.pk]

    view = ProductMassEditView.as_view()
    response = view(request=request)
    assert response.status_code == 302
    for product in Product.objects.all():
        assert product.get_shop_instance(shop).primary_category == category
Exemple #46
0
def test_category_copy_visibility(rf, admin_user):
    shop = get_default_shop()
    group = get_default_customer_group()
    category = get_default_category()
    category.status = CategoryStatus.INVISIBLE
    category.visibility = CategoryVisibility.VISIBLE_TO_GROUPS
    category.shops.add(shop)
    category.visibility_groups.add(group)
    category.save()
    product = create_product("test_product", shop=shop)
    shop_product = product.get_shop_instance(shop)
    shop_product.primary_category = category
    shop_product.save()
    view = CategoryCopyVisibilityView.as_view()
    request = apply_request_middleware(rf.post("/"), user=admin_user)
    response = view(request, pk=category.pk)
    shop_product.refresh_from_db()
    assert response.status_code == 200
    assert shop_product.visibility == ShopProductVisibility.NOT_VISIBLE
    assert shop_product.visibility_limit.value == category.visibility.value
    assert shop_product.visibility_groups.count() == category.visibility_groups.count()
    assert set(shop_product.visibility_groups.all()) == set(category.visibility_groups.all())
def test_category_products_effect_with_amount(rf):
    request, shop, group = initialize_test(rf, False)

    basket = get_basket(request)
    category = get_default_category()
    supplier = get_default_supplier()

    single_product_price = "50"
    discount_amount_value = "10"
    quantity = 5

    product = create_product("The product", shop=shop, supplier=supplier, default_price=single_product_price)
    shop_product = product.get_shop_instance(shop)
    shop_product.categories.add(category)

    basket.add_product(supplier=supplier, shop=shop, product=product, quantity=quantity)
    basket.shipping_method = get_shipping_method(shop=shop)
    basket.save()

    rule = CategoryProductsBasketCondition.objects.create(operator=ComparisonOperator.EQUALS, quantity=quantity)
    rule.categories.add(category)

    campaign = BasketCampaign.objects.create(active=True, shop=shop, name="test", public_name="test")
    campaign.conditions.add(rule)

    DiscountFromCategoryProducts.objects.create(
        campaign=campaign, category=category, discount_amount=discount_amount_value)

    assert rule.matches(basket, [])
    basket.uncache()
    final_lines = basket.get_final_lines()

    assert len(final_lines) == 2  # no new lines since the effect touches original lines
    expected_discount_amount = quantity * basket.create_price(discount_amount_value)
    original_price = basket.create_price(single_product_price) * quantity
    line = final_lines[0]
    assert line.discount_amount == expected_discount_amount
    assert basket.total_price == original_price - expected_discount_amount
Exemple #48
0
def test_category_links_plugin_with_customer(rf, show_all_categories):
    """
    Test plugin for categories that is visible for certain group
    """
    shop = get_default_shop()
    group = get_default_customer_group()
    customer = create_random_person()
    customer.groups.add(group)
    customer.save()

    request = rf.get("/")
    request.shop = get_default_shop()
    apply_request_middleware(request)
    request.customer = customer

    category = get_default_category()
    category.status = CategoryStatus.VISIBLE
    category.visibility = CategoryVisibility.VISIBLE_TO_GROUPS
    category.visibility_groups.add(group)
    category.shops.add(shop)
    category.save()

    vars = {"request": request}
    context = get_jinja_context(**vars)
    plugin = CategoryLinksPlugin({
        "categories": [category.pk],
        "show_all_categories": show_all_categories
    })
    assert category.is_visible(customer)
    assert category in plugin.get_context_data(context)["categories"]

    customer_without_groups = create_random_person()
    customer_without_groups.groups.clear()

    assert not category.is_visible(customer_without_groups)
    request.customer = customer_without_groups
    context = get_jinja_context(**vars)
    assert category not in plugin.get_context_data(context)["categories"]
Exemple #49
0
def test_products_form_remove_with_parent():
    shop = get_default_shop()
    category = get_default_category()
    category.shops.add(shop)

    product = create_product("test_product", shop=shop, mode=ProductMode.SIMPLE_VARIATION_PARENT)
    shop_product = product.get_shop_instance(shop)
    shop_product.primary_category = category
    shop_product.save()
    shop_product.categories.add(category)

    child_product = create_product("child_product", shop=shop)
    child_product.link_to_parent(product)
    child_shop_product = child_product.get_shop_instance(shop)
    child_shop_product.primary_category = category
    child_shop_product.save()
    child_shop_product.categories.add(category)


    shop_product.refresh_from_db()
    assert (shop_product.primary_category == category)
    assert (shop_product.categories.count() == 1)
    assert (shop_product.categories.first() == category)

    assert (category.shop_products.count() == 2)

    data = {
        "remove_products": ["%s" % shop_product.id]
    }
    form = CategoryProductForm(shop=shop, category=category, data=data)
    form.full_clean()
    form.save()

    category.refresh_from_db()
    assert (category.shop_products.count() == 0)
    shop_product.refresh_from_db()
    assert (shop_product.primary_category is None)
    assert (shop_product.categories.count() == 0)
Exemple #50
0
def test_products_for_category():
    from shuup.front.template_helpers import general

    supplier = get_default_supplier()
    shop = get_default_shop()

    category = get_default_category()
    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)]
    category.shops.add(shop)

    for child in children:
        child.link_to_parent(products[0])

    context = get_jinja_context()

    for product in products:
        product.get_shop_instance(shop).categories.add(category)

    category_products = list(general.get_products_for_categories(context, [category]))
    assert len(category_products) == 2
    assert products[0] in category_products
    assert products[1] in category_products
def test_manufacturer_filter_get_fields(rf):
    cache.clear()

    shop = factories.get_default_shop()

    request = apply_request_middleware(rf.get("/"))
    assert ManufacturerProductListFilter().get_fields(request, None) is None

    manufacturer = Manufacturer.objects.create(name="Random Brand Inc")
    assert ManufacturerProductListFilter().get_fields(request, None) is None

    category = factories.get_default_category()
    product = factories.create_product("sku", shop=shop)
    shop_product = product.get_shop_instance(shop=shop)
    shop_product.primary_category = category
    shop_product.save()

    assert ManufacturerProductListFilter().get_fields(request,
                                                      category) is None

    # Now once we link manufacturer to product we should get
    # form field for manufacturer
    product.manufacturer = manufacturer
    product.save()
    form_field = ManufacturerProductListFilter().get_fields(request,
                                                            category)[0][1]
    assert form_field is not None
    assert form_field.label == "Manufacturers"

    with override_settings(
            SHUUP_FRONT_OVERRIDE_SORTS_AND_FILTERS_LABELS_LOGIC={
                "manufacturers": "Brands"
            }):
        form_field = ManufacturerProductListFilter().get_fields(
            request, category)[0][1]
        assert form_field is not None
        assert form_field.label == "Brands"
Exemple #52
0
def test_category_detail(client):
    shop = factories.get_default_shop()

    # Activate show supplier info for front
    assert ThemeSettings.objects.count() == 1
    theme_settings = ThemeSettings.objects.first()
    theme_settings.update_settings({"show_supplier_info": True})

    # Activate supplier filters to prove they don't effect results
    # without actually filtering something. There is separate tests
    # to do the more closer tests.
    set_configuration(
        shop=shop,
        data={
            "filter_products_by_supplier": True,
            "filter_products_by_supplier_ordering": 1,
        }
    )

    category = factories.get_default_category()

    product_data = [
        ("laptop", 1500),
        ("keyboard", 150),
        ("mouse", 150)
    ]
    products = []
    for sku, price_value in product_data:
        products.append(factories.create_product(sku, shop=shop, default_price=price_value))

    supplier_data = [
        ("Johnny Inc", 0.5),
        ("Mike Inc", 0.9),
        ("Simon Inc", 0.8),
    ]
    for name, percentage_from_original_price in supplier_data:
        supplier = Supplier.objects.create(name=name)

        for product in products:
            shop_product = product.get_shop_instance(shop)
            shop_product.suppliers.add(supplier)
            shop_product.primary_category = category
            shop_product.save()

            supplier_price = (
                percentage_from_original_price * [price for sku, price in product_data if product.sku == sku][0])
            SupplierPrice.objects.create(supplier=supplier, shop=shop, product=product, amount_value=supplier_price)

    strategy = "shuup.testing.supplier_pricing.supplier_strategy:CheapestSupplierPriceSupplierStrategy"
    with override_settings(SHUUP_PRICING_MODULE="supplier_pricing", SHUUP_SHOP_PRODUCT_SUPPLIERS_STRATEGY=strategy):
        with override_current_theme_class(ClassicGrayTheme, shop):  # Ensure settings is refreshed from DB
            soup = _get_category_detail_soup(client, category)

            # Johnny Inc has the best prices for everything
            laptop = [product for product in products if product.sku == "laptop"][0]
            laptop_product_box = soup.find("div", {"id": "product-%s" % laptop.pk})
            _assert_supplier_info(laptop_product_box, "Johnny Inc")
            _assert_product_price(laptop_product_box, 750)

            keyboard = [product for product in products if product.sku == "keyboard"][0]
            keyboard_product_box = soup.find("div", {"id": "product-%s" % keyboard.pk})
            _assert_supplier_info(keyboard_product_box, "Johnny Inc")
            _assert_product_price(keyboard_product_box, 75)

            mouse = [product for product in products if product.sku == "mouse"][0]
            mouse_product_box = soup.find("div", {"id": "product-%s" % mouse.pk})
            _assert_supplier_info(mouse_product_box, "Johnny Inc")
            _assert_product_price(mouse_product_box, 75)

            # Ok competition has done it job and the other suppliers
            # has to start adjust their prices.

            # Let's say Mike has the cheapest laptop
            mike_supplier = Supplier.objects.filter(name="Mike Inc")
            SupplierPrice.objects.filter(supplier=mike_supplier, shop=shop, product=laptop).update(amount_value=333)

            soup = _get_category_detail_soup(client, category)
            laptop_product_box = soup.find("div", {"id": "product-%s" % laptop.pk})
            _assert_supplier_info(laptop_product_box, "Mike Inc")
            _assert_product_price(laptop_product_box, 333)

            # Just to make sure Simon takes over the mouse biz
            simon_supplier = Supplier.objects.filter(name="Simon Inc")
            SupplierPrice.objects.filter(supplier=simon_supplier, shop=shop, product=mouse).update(amount_value=1)

            soup = _get_category_detail_soup(client, category)
            mouse_product_box = soup.find("div", {"id": "product-%s" % mouse.pk})
            _assert_supplier_info(mouse_product_box, "Simon Inc")
            _assert_product_price(mouse_product_box, 1)
def test_many_price_info_cache_bump(rf):
    initial_price = 10
    shop = factories.get_default_shop()
    tax = factories.get_default_tax()
    tax_class = factories.get_default_tax_class()
    product = factories.create_product(
        "product", shop=shop, supplier=factories.get_default_supplier(), default_price=initial_price
    )
    child1 = factories.create_product("child1", shop=shop, supplier=factories.get_default_supplier(), default_price=5)
    child2 = factories.create_product("child2", shop=shop, supplier=factories.get_default_supplier(), default_price=9)
    child1.link_to_parent(product, variables={"color": "red"})
    child2.link_to_parent(product, variables={"color": "blue"})

    request = apply_request_middleware(rf.get("/"))
    child1_pi = child1.get_price_info(request)
    child2_pi = child1.get_price_info(request)

    def assert_cache_products():
        cache_many_price_info(request, product, 1, [child1_pi, child2_pi])
        assert get_many_cached_price_info(request, product, 1)[0].price == child1_pi.price
        assert get_many_cached_price_info(request, product, 1)[1].price == child2_pi.price

    def assert_nothing_is_cached():
        # nothing is cached
        assert get_many_cached_price_info(request, product, 1) is None

    # cache the item
    assert_nothing_is_cached()
    assert_cache_products()

    # cache bumped - the cache should be dropped - then, cache again
    tax.save()
    assert_nothing_is_cached()
    assert_cache_products()

    # cache bumped - the cache should be dropped - then, cache again
    tax_class.save()
    assert_nothing_is_cached()
    assert_cache_products()

    # cache bumped - the cache should be dropped - then, cache again
    product.save()
    assert_nothing_is_cached()
    assert_cache_products()

    shop_product = product.get_shop_instance(shop)

    # cache bumped - the cache should be dropped - then, cache again
    shop_product.save()
    assert_nothing_is_cached()
    assert_cache_products()

    category = factories.get_default_category()

    # cache bumped - the cache should be dropped - then, cache again
    shop_product.categories.add(category)
    assert_nothing_is_cached()
    assert_cache_products()

    # cache bumped - the cache should be dropped - then, cache again
    supplier = shop_product.suppliers.first()
    supplier.enabled = False
    supplier.save()
    assert_nothing_is_cached()
    assert_cache_products()
Exemple #54
0
def test_category_detail_multiselect_supplier_filters(client):
    shop = factories.get_default_shop()

    # Activate show supplier info for front
    assert ThemeSettings.objects.count() == 1
    theme_settings = ThemeSettings.objects.first()
    theme_settings.update_settings({"show_supplier_info": True})

    category = factories.get_default_category()

    # Important! Activate supplier filter.
    set_configuration(shop=shop,
                      data={
                          "filter_products_by_supplier": True,
                          "filter_products_by_supplier_ordering": 1,
                          "filter_products_by_supplier_multiselect_enabled":
                          True
                      })

    supplier_data = [
        ("Johnny Inc", 0.5),
        ("Mike Inc", 0.9),
        ("Simon Inc", 0.8),
    ]

    for name, percentage_from_original_price in supplier_data:
        supplier = Supplier.objects.create(name=name)
        supplier.shops.add(shop)
        sku = name
        price_value = 10
        product = factories.create_product(sku,
                                           shop=shop,
                                           default_price=price_value)
        shop_product = product.get_shop_instance(shop)
        shop_product.suppliers.add(supplier)
        shop_product.primary_category = category
        shop_product.categories.add(category)
        shop_product.save()

        supplier_price = (percentage_from_original_price * price_value)
        SupplierPrice.objects.create(supplier=supplier,
                                     shop=shop,
                                     product=product,
                                     amount_value=supplier_price)

    strategy = "shuup.testing.supplier_pricing.supplier_strategy:CheapestSupplierPriceSupplierStrategy"
    with override_settings(SHUUP_PRICING_MODULE="supplier_pricing",
                           SHUUP_SHOP_PRODUCT_SUPPLIERS_STRATEGY=strategy):
        with override_current_theme_class(
                ClassicGrayTheme,
                shop):  # Ensure settings is refreshed from DB
            johnny_supplier = Supplier.objects.filter(
                name="Johnny Inc").first()
            mike_supplier = Supplier.objects.filter(name="Mike Inc").first()
            simon_supplier = Supplier.objects.filter(name="Simon Inc").first()

            soup = _get_category_detail_soup_multiselect(
                client, category, [johnny_supplier.pk])
            assert len(soup.findAll("div", {"class": "single-product"})) == 1

            soup = _get_category_detail_soup_multiselect(
                client, category, [johnny_supplier.pk, mike_supplier.pk])
            assert len(soup.findAll("div", {"class": "single-product"})) == 2

            soup = _get_category_detail_soup_multiselect(
                client, category,
                [johnny_supplier.pk, mike_supplier.pk, simon_supplier.pk])
            assert len(soup.findAll("div", {"class": "single-product"})) == 3
Exemple #55
0
def test_product_price_range_filter(reindex_catalog):
    shop = factories.get_default_shop()
    product = factories.get_default_product()
    category = factories.get_default_category()
    shop_product = product.get_shop_instance(shop)
    shop_product.default_price_value = 10
    shop_product.save()
    shop_product.categories.add(category)
    reindex_catalog()

    client = SmartClient()
    config = {
        "filter_products_by_price": True,
        "filter_products_by_price_range_min": 5,
        "filter_products_by_price_range_max": 15,
        "filter_products_by_price_range_size": 5,
    }
    set_configuration(category=category, data=config)
    url = reverse("shuup:category",
                  kwargs={
                      "pk": category.pk,
                      "slug": category.slug
                  })
    response, soup = client.response_and_soup(url)
    assert response.status_code == 200
    assert soup.find(id="product-%d" % product.id)
    price_range_select = soup.find(id="id_price_range")
    # as the configuration is not set to override shop default configuration
    # this field shouldn't be there..
    assert price_range_select is None

    # make the category configuration override the shop's default config
    config.update({"override_default_configuration": True})
    set_configuration(category=category, data=config)
    url = reverse("shuup:category",
                  kwargs={
                      "pk": category.pk,
                      "slug": category.slug
                  })
    response, soup = client.response_and_soup(url)
    assert response.status_code == 200
    assert soup.find(id="product-%d" % product.id)
    price_range_select = soup.find(id="id_price_range")
    price_ranges = price_range_select.find_all("option")
    assert len(price_ranges) == 4

    # filter products with prices above $15
    filtered_url = "{}?price_range={}".format(url,
                                              price_ranges[-1].attrs["value"])
    response, soup = client.response_and_soup(filtered_url)
    assert response.status_code == 200
    assert not soup.find(id="product-%d" % product.id)

    # explicitly disable the override
    config.update({"override_default_configuration": False})
    set_configuration(category=category, data=config)
    url = reverse("shuup:category",
                  kwargs={
                      "pk": category.pk,
                      "slug": category.slug
                  })
    response, soup = client.response_and_soup(url)
    assert response.status_code == 200
    assert soup.find(id="product-%d" % product.id)
    price_range_select = soup.find(id="id_price_range")
    assert price_range_select is None
Exemple #56
0
def test_categories_shopproducts_manytomany():
    shop_product = get_default_shop_product()
    category = get_default_category()
    category.shop_products.set([shop_product])
    assert category.shop_products.first() == shop_product
    assert shop_product.categories.first() == category
def test_category_detail_filters(client):
    shop = factories.get_default_shop()

    # Activate show supplier info for front
    assert ThemeSettings.objects.count() == 1
    theme_settings = ThemeSettings.objects.first()
    theme_settings.update_settings({"show_supplier_info": True})

    category = factories.get_default_category()

    # Important! Activate supplier filter.
    set_configuration(
        shop=shop,
        data={
            "filter_products_by_supplier": True,
            "filter_products_by_supplier_ordering": 1,
        },
    )

    product_data = [("laptop", 1500), ("keyboard", 150), ("mouse", 150)]
    products = []
    for sku, price_value in product_data:
        products.append(factories.create_product(sku, shop=shop, default_price=price_value))

    supplier_data = [
        ("Johnny Inc", 0.5),
        ("Mike Inc", 0.9),
        ("Simon Inc", 0.8),
    ]
    for name, percentage_from_original_price in supplier_data:
        supplier = Supplier.objects.create(name=name)
        supplier.shops.add(shop)

        for product in products:
            shop_product = product.get_shop_instance(shop)
            shop_product.suppliers.add(supplier)
            shop_product.primary_category = category
            shop_product.categories.add(category)
            shop_product.save()

            supplier_price = (
                percentage_from_original_price * [price for sku, price in product_data if product.sku == sku][0]
            )
            SupplierPrice.objects.create(supplier=supplier, shop=shop, product=product, amount_value=supplier_price)

    strategy = "shuup.testing.supplier_pricing.supplier_strategy:CheapestSupplierPriceSupplierStrategy"
    with override_settings(SHUUP_PRICING_MODULE="supplier_pricing", SHUUP_SHOP_PRODUCT_SUPPLIERS_STRATEGY=strategy):
        with override_current_theme_class(ClassicGrayTheme, shop):  # Ensure settings is refreshed from DB

            laptop = [product for product in products if product.sku == "laptop"][0]
            keyboard = [product for product in products if product.sku == "keyboard"][0]
            mouse = [product for product in products if product.sku == "mouse"][0]
            # Let's get products for Johnny
            supplier_johnny = Supplier.objects.filter(name="Johnny Inc").first()
            soup = _get_category_detail_soup(client, category, supplier_johnny.pk)

            laptop_product_box = soup.find("div", {"id": "product-%s" % laptop.pk})
            _assert_supplier_info(laptop_product_box, "Johnny Inc")
            _assert_product_price(laptop_product_box, 750)

            # Now here when the category view is filtered based on supplier
            # the product urls should lead to supplier product url so we
            # can show details and prices for correct supplier.
            _assert_product_url(laptop_product_box, supplier_johnny, laptop)

            # Let's test rest of the products and suppliers
            keyboard_product_box = soup.find("div", {"id": "product-%s" % keyboard.pk})
            _assert_supplier_info(keyboard_product_box, "Johnny Inc")
            _assert_product_price(keyboard_product_box, 75)
            _assert_product_url(keyboard_product_box, supplier_johnny, keyboard)

            mike_supplier = Supplier.objects.filter(name="Mike Inc").first()
            soup = _get_category_detail_soup(client, category, mike_supplier.pk)
            keyboard_product_box = soup.find("div", {"id": "product-%s" % keyboard.pk})
            _assert_supplier_info(keyboard_product_box, "Mike Inc")
            _assert_product_price(keyboard_product_box, 135)
            _assert_product_url(keyboard_product_box, mike_supplier, keyboard)

            simon_supplier = Supplier.objects.filter(name="Simon Inc").first()
            soup = _get_category_detail_soup(client, category, simon_supplier.pk)
            mouse_product_box = soup.find("div", {"id": "product-%s" % mouse.pk})
            _assert_supplier_info(mouse_product_box, "Simon Inc")
            _assert_product_price(mouse_product_box, 120)
            _assert_product_url(mouse_product_box, simon_supplier, mouse)
Exemple #58
0
def test_catalog_campaign_sync():
    shop = factories.get_default_shop()
    supplier = factories.get_default_supplier()
    default_price = 100
    product1 = factories.create_product("test1",
                                        shop=shop,
                                        supplier=supplier,
                                        default_price=default_price)
    product2 = factories.create_product("test2",
                                        shop=shop,
                                        supplier=supplier,
                                        default_price=default_price)
    product3 = factories.create_product("test3",
                                        shop=shop,
                                        supplier=supplier,
                                        default_price=default_price)
    category = factories.get_default_category()
    shop_product = product1.get_shop_instance(shop)
    shop_product.primary_category = category
    shop_product.save()
    shop_product.categories.add(category)

    contact1 = factories.create_random_person()

    contact2 = factories.create_random_person()
    contact_group = factories.get_default_customer_group()
    contact2.groups.add(contact_group)

    happy_hour1_weekdays = "0,1"  # Mon, Tue
    happy_hour1_start = datetime.time(21)
    happy_hour1_end = datetime.time(3)
    happy_hour1_condition = HourCondition.objects.create(
        days=happy_hour1_weekdays,
        hour_start=happy_hour1_start,
        hour_end=happy_hour1_end)

    happy_hour2_weekdays = "2,6"  # Wed, Sun
    happy_hour2_start = datetime.time(14)
    happy_hour2_end = datetime.time(16)
    happy_hour2_condition = HourCondition.objects.create(
        days=happy_hour2_weekdays,
        hour_start=happy_hour2_start,
        hour_end=happy_hour2_end)

    discount_amount_value = 50
    discount_percentage = decimal.Decimal("0.35")
    _create_catalog_campaign_for_products(shop, [product1],
                                          discount_amount_value,
                                          happy_hour1_condition)
    _create_catalog_campaign_for_products(shop, [product2, product3],
                                          discount_amount_value)
    _create_catalog_campaign_for_category(shop, category, discount_percentage)
    _create_catalog_campaign_for_contact(shop, product1, contact1,
                                         discount_amount_value)
    _create_catalog_campaign_for_contact_group(shop,
                                               [product1, product2, product3],
                                               contact_group,
                                               discount_percentage,
                                               happy_hour2_condition)

    call_command("import_catalog_campaigns", *[], **{})

    # From first campaign we should get 1 discount with happy hour
    # From second campaign we should get 2 discounts
    # From third campaign we should get 1 discount
    # From fourth campaign we should get also 1 discount
    # From last campaign we should get 3 discounts with happy hour
    assert Discount.objects.count() == 8

    # There should be 2 happy hours in total
    assert HappyHour.objects.count() == 2

    # From first happy hour there should be 4 ranges
    # Mon 21-23, Tue 0-3, Tue 21-23, Wed 0-3
    # From second happy hour there should be 2 ranges
    # Wed 14-16 and Sun 14-16
    assert TimeRange.objects.count() == 6

    # Let's go through all our 8 discounts to make sure all is good
    first_discount = Discount.objects.filter(
        product=product1,
        category__isnull=True,
        contact__isnull=True,
        contact_group__isnull=True).first()
    assert first_discount.happy_hours.count() == 1
    assert first_discount.discount_amount_value == discount_amount_value

    second_discount = Discount.objects.filter(
        product=product2,
        category__isnull=True,
        contact__isnull=True,
        contact_group__isnull=True).first()
    assert second_discount.happy_hours.count() == 0
    assert second_discount.discount_amount_value == discount_amount_value

    third_discount = Discount.objects.filter(
        product=product3,
        category__isnull=True,
        contact__isnull=True,
        contact_group__isnull=True).first()
    assert third_discount.happy_hours.count() == 0
    assert third_discount.discount_amount_value == discount_amount_value

    category_discount = Discount.objects.filter(
        product__isnull=True,
        category=category,
        contact__isnull=True,
        contact_group__isnull=True).first()
    assert category_discount.happy_hours.count() == 0
    assert category_discount.discount_percentage == discount_percentage

    contact_discount = Discount.objects.filter(
        product=product1,
        category__isnull=True,
        contact=contact1,
        contact_group__isnull=True).first()
    assert contact_discount.discount_amount_value == discount_amount_value

    product1_contact_group_discount = Discount.objects.filter(
        product=product1,
        category__isnull=True,
        contact__isnull=True,
        contact_group=contact_group).first()
    assert product1_contact_group_discount.happy_hours.count() == 1
    assert product1_contact_group_discount.discount_percentage == discount_percentage

    product2_contact_group_discount = Discount.objects.filter(
        product=product2,
        category__isnull=True,
        contact__isnull=True,
        contact_group=contact_group).first()
    assert product2_contact_group_discount.happy_hours.count() == 1
    assert product2_contact_group_discount.discount_percentage == discount_percentage

    product3_contact_group_discount = Discount.objects.filter(
        product=product3,
        category__isnull=True,
        contact__isnull=True,
        contact_group=contact_group).first()
    assert product3_contact_group_discount.happy_hours.count() == 1
    assert product3_contact_group_discount.discount_percentage == discount_percentage
Exemple #59
0
def _add_catalog_campaign(shop):
    campaign = CatalogCampaign.objects.create(shop=shop, name="test", public_name="test", active=True)
    category_filter = CategoryFilter.objects.create()
    category_filter.categories.add(get_default_category())
    category_filter.save()
    ProductDiscountAmount.objects.create(campaign=campaign, discount_amount=5)
def test_product_from_category_plugin(rf):
    shop = get_default_shop()
    category1 = get_default_category()
    category2 = CategoryFactory(status=CategoryStatus.VISIBLE)

    category1.shops.add(shop)
    category2.shops.add(shop)

    p1 = create_product("p1", shop, get_default_supplier(), "10")
    p2 = create_product("p2", shop, get_default_supplier(), "20")
    p3 = create_product("p3", shop, get_default_supplier(), "30")

    sp1 = p1.get_shop_instance(shop)
    sp2 = p2.get_shop_instance(shop)
    sp3 = p3.get_shop_instance(shop)

    sp1.categories.add(category1)
    sp2.categories.add(category1)
    sp3.categories.add(category2)

    plugin = ProductsFromCategoryPlugin({
        "category": category1.pk,
        "cache_timeout": 120
    })
    plugin_context = plugin.get_context_data(get_context(rf, is_ajax=False))
    context_products = plugin_context["products"]

    assert len(context_products) == 0

    plugin_context = plugin.get_context_data(get_context(rf))
    context_data_url = plugin_context["data_url"]
    context_products = plugin_context["products"]
    assert p1 in context_products
    assert p2 in context_products
    assert p3 not in context_products

    check_expected_product_count(context_data_url, 2)
    check_expected_product_count(context_data_url, 2)  # one for checking it is cached

    # test the plugin form
    with override_current_theme_class(None):
        theme = get_current_theme(get_default_shop())
        cell = LayoutCell(theme, ProductsFromCategoryPlugin.identifier, sizes={"md": 8})
        lcfg = LayoutCellFormGroup(layout_cell=cell, theme=theme, request=apply_request_middleware(rf.get("/")))
        assert not lcfg.is_valid()

        lcfg = LayoutCellFormGroup(
            data={
                "general-cell_width": "8",
                "general-cell_align": "pull-right",
                "plugin-count": 4,
                "plugin-category": category2.pk,
                "plugin-cache_timeout": 3600
            },
            layout_cell=cell,
            theme=theme,
            request=apply_request_middleware(rf.get("/"))
        )
        assert lcfg.is_valid()
        lcfg.save()
        assert cell.config["category"] == str(category2.pk)