def test_product_create(browser, admin_user, live_server, settings): activate("en") shop = get_default_shop() get_default_product_type() get_default_sales_unit() get_default_tax_class() configuration.set(None, "shuup_product_tour_complete", True) object_created.connect(_add_custom_product_created_message, sender=Product, dispatch_uid="object_created_signal_test") initialize_admin_browser_test(browser, live_server, settings) url = reverse("shuup_admin:shop_product.new") browser.visit("%s%s" % (live_server, url)) sku = "testsku" name = "Some product name" price_value = 10 short_description = "short but gold" browser.fill("base-sku", sku) browser.fill("base-name__en", name) browser.fill("base-short_description__en", short_description) browser.fill("shop%s-default_price_value" % shop.pk, price_value) configuration.set(None, "shuup_category_tour_complete", True) _add_primary_category(browser, shop) _add_additional_category(browser, shop) click_element(browser, "button[form='product_form']") wait_until_appeared(browser, "div[class='message success']") product = Product.objects.filter(sku=sku).first() assert product.log_entries.filter(identifier=OBJECT_CREATED_LOG_IDENTIFIER).count() == 1 object_created.disconnect(sender=Product, dispatch_uid="object_created_signal_test") shop_product = product.get_shop_instance(shop) assert shop_product.categories.count() == 2
def test_product_add_attribute(admin_user): shop = get_default_shop() client = _get_client(admin_user) product = create_product("product1") attribute1 = Attribute.objects.create(identifier="attr1", type=AttributeType.BOOLEAN, visibility_mode=AttributeVisibility.SHOW_ON_PRODUCT_PAGE, name="Attribute 1") attribute2 = Attribute.objects.create(identifier="attr2", type=AttributeType.TRANSLATED_STRING, visibility_mode=AttributeVisibility.SHOW_ON_PRODUCT_PAGE, name="Attribute 2") attribute3 = Attribute.objects.create(identifier="attr3", type=AttributeType.UNTRANSLATED_STRING, visibility_mode=AttributeVisibility.SHOW_ON_PRODUCT_PAGE, name="Attribute 3") get_default_product_type().attributes.add(attribute1) get_default_product_type().attributes.add(attribute2) product_attr1_data = { "attribute": attribute1.pk, "numeric_value": 0, } response = client.post("/api/shuup/product/%d/add_attribute/" % product.pk, content_type="application/json", data=json.dumps(product_attr1_data)) assert response.status_code == status.HTTP_201_CREATED assert ProductAttribute.objects.filter(product=product).count() == 1 pa = ProductAttribute.objects.first() assert pa.attribute.pk == attribute1.pk assert pa.numeric_value == product_attr1_data["numeric_value"] product_attr2_data = { "attribute": attribute2.pk, "translations": { "en": {"translated_string_value": "come on"}, "pt-br": {"translated_string_value": "vamos lá"} } } response = client.post("/api/shuup/product/%d/add_attribute/" % product.pk, content_type="application/json", data=json.dumps(product_attr2_data)) assert response.status_code == status.HTTP_201_CREATED assert ProductAttribute.objects.filter(product=product).count() == 2 pa = ProductAttribute.objects.last() assert pa.attribute.pk == attribute2.pk assert pa.translated_string_value == product_attr2_data["translations"]["en"]["translated_string_value"] # try to add an attribute which does not belong to the product type product_attr3_data = { "attribute": attribute3.pk, "untraslated_string": "lalala" } response = client.post("/api/shuup/product/%d/add_attribute/" % product.pk, content_type="application/json", data=json.dumps(product_attr3_data)) assert response.status_code == status.HTTP_400_BAD_REQUEST assert ProductAttribute.objects.filter(product=product).count() == 2
def test_quick_add(browser, admin_user, live_server, settings): shop = get_default_shop() get_default_product_type() get_default_sales_unit() get_default_tax_class() initialize_admin_browser_test(browser, live_server, settings) url = reverse("shuup_admin:shop_product.new") browser.visit("%s%s" % (live_server, url)) sku = "testsku" name = "Some product name" price_value = 10 short_description = "short but gold" browser.fill("base-sku", sku) browser.fill("base-name__en", name) browser.fill("base-short_description__en", short_description) browser.fill("shop%s-default_price_value" % shop.pk, price_value) wait_until_appeared(browser, "#id_shop%d-primary_category ~ .quick-add-btn a.btn" % shop.id) click_element(browser, "#id_shop%d-primary_category ~ .quick-add-btn a.btn" % shop.id) wait_until_appeared(browser, "#create-object-iframe") with browser.get_iframe('create-object-iframe') as iframe: assert Category.objects.count() == 0 wait_until_appeared(iframe, "input[name='base-name__en']") iframe.fill("base-name__en", "Test Category") time.sleep(3) # Let's just wait here to the iFrame to open fully (for Chrome and headless) wait_until_appeared(iframe, "button[form='category_form']") click_element(browser, "button[form='category_form']") wait_until_condition(browser, condition=lambda x: Category.objects.count() == 1, timeout=20) assert Category.objects.first().name == "Test Category" # click to edit the button click_element(browser, "#id_shop%d-primary_category ~ .edit-object-btn a.btn" % shop.id) with browser.get_iframe('create-object-iframe') as iframe: wait_until_appeared(iframe, "input[name='base-name__en']") new_cat_name = "Changed Name" iframe.fill("base-name__en", new_cat_name) time.sleep(3) # Let's just wait here to the iFrame to open fully (for Chrome and headless) wait_until_appeared(iframe, "button[form='category_form']") click_element(iframe, "button[form='category_form']") wait_until_condition(browser, condition=lambda x: Category.objects.first().name == new_cat_name, timeout=20) click_element(browser, "button[form='product_form']") wait_until_appeared(browser, "div[class='message success']")
def test_complex_import(): filename = "complex_import.xlsx" activate("en") shop = get_default_shop() get_default_tax_class() get_default_product_type() get_default_supplier() get_default_sales_unit() path = os.path.join(os.path.dirname(__file__), "data", "product", filename) transformed_data = transform_file(filename.split(".")[1], path) importer = ProductImporter(transformed_data, shop, "en") importer.process_data() assert len(importer.unmatched_fields) == 0 importer.do_import(ImportMode.CREATE_UPDATE) products = importer.new_objects assert len(products) == 6 assert ShopProduct.objects.count() == 6 assert Category.objects.count() == 11 assert Manufacturer.objects.count() == 4 for idx, product in enumerate(Product.objects.all().order_by("sku")): shop_product = product.get_shop_instance(shop) data = PRODUCT_DATA[idx] assert product.sku == data["sku"] assert product.name == data["name"] assert shop_product.default_price_value == Decimal(data["price"]) assert product.description == data["description"] if data.get("categories"): all_cats = set(data["categories"]) all_cats.add(data["category"]) for cat in shop_product.categories.all(): assert cat.name in all_cats assert shop_product.categories.count() == len(all_cats) # also add primary category if data.get("category"): assert shop_product.primary_category.name == data["category"] assert force_text(shop_product.visibility.label) == data["visibility"].lower() assert product.tax_class.name == data["tax_class"] if data.get("manufacturer"): assert product.manufacturer.name == data["manufacturer"]
def test_sample_import_all_match(filename): activate("en") shop = get_default_shop() tax_class = get_default_tax_class() product_type = get_default_product_type() path = os.path.join(os.path.dirname(__file__), "data", "product", filename) transformed_data = transform_file(filename.split(".")[1], path) importer = ProductImporter(transformed_data, shop, "en") importer.process_data() assert len(importer.unmatched_fields) == 0 importer.do_import(ImportMode.CREATE_UPDATE) products = importer.new_objects assert len(products) == 2 for product in products: shop_product = product.get_shop_instance(shop) assert shop_product.pk assert shop_product.pk == product.pk assert shop_product.default_price_value == 150 assert shop_product.default_price == shop.create_price(150) assert product.type == product_type # product type comes from importer defaults if product.pk == 1: assert product.tax_class.pk == 2 # new was created assert product.name == "Product English" assert product.description == "Description English" else: assert product.tax_class.pk == tax_class.pk # old was found as should assert product.name == "Product 2 English" assert product.description == "Description English 2" assert shop_product.primary_category.pk == 1 assert [c.pk for c in shop_product.categories.all()] == [1,2]
def test_sample_ignore_column(): activate("en") shop = get_default_shop() get_default_tax_class() get_default_product_type() get_default_sales_unit() path = os.path.join(os.path.dirname(__file__), "data", "product", "sample_import_ignore.csv") transformed_data = transform_file("csv", path) importer = ProductImporter(transformed_data, shop, "en") importer.process_data() assert len(importer.unmatched_fields) == 0 importer.do_import(ImportMode.CREATE_UPDATE) products = importer.new_objects assert len(products) == 2
def test_sample_import_no_match(stock_managed): filename = "sample_import_nomatch.xlsx" if "shuup.simple_supplier" not in settings.INSTALLED_APPS: pytest.skip("Need shuup.simple_supplier in INSTALLED_APPS") from shuup_tests.simple_supplier.utils import get_simple_supplier activate("en") shop = get_default_shop() tax_class = get_default_tax_class() product_type = get_default_product_type() supplier = get_simple_supplier(stock_managed) sales_unit = get_default_sales_unit() Manufacturer.objects.create(name="manufctr") path = os.path.join(os.path.dirname(__file__), "data", "product", filename) transformed_data = transform_file(filename.split(".")[1], path) importer = ProductImporter(transformed_data, shop, "en") importer.process_data() assert len(importer.unmatched_fields) == 1 assert "gtiin" in importer.unmatched_fields importer.manually_match("gtiin", "shuup.core.models.Product:gtin") importer.do_remap() assert len(importer.unmatched_fields) == 0 importer.do_import(ImportMode.CREATE_UPDATE) products = importer.new_objects assert len(products) == 2 for product in products: assert product.gtin == "1280x720" shop_product = product.get_shop_instance(shop) assert shop_product.pk assert shop_product.default_price_value == 150 assert shop_product.default_price == shop.create_price(150) assert product.type == product_type # product type comes from importer defaults assert product.sales_unit == sales_unit if product.pk == 1: assert product.tax_class.pk == 2 # new was created assert product.name == "Product English" assert product.description == "Description English" else: assert product.tax_class.pk == tax_class.pk # old was found as should assert product.name == "Product 2 English" assert product.description == "Description English 2" assert shop_product.primary_category.pk == 1 assert [c.pk for c in shop_product.categories.all()] == [1,2] # stock was not managed since supplier doesn't like that for msg in importer.other_log_messages: assert "please set Stock Managed on" in msg supplier.stock_managed = True supplier.save() importer.do_import("create,update") assert len(importer.other_log_messages) == 0 for sa in StockAdjustment.objects.all(): assert sa.product.pk assert sa.delta == 20
def test_product_attribute(admin_user): shop = get_default_shop() client = _get_client(admin_user) product1 = create_product("product1") attrib1 = create_random_product_attribute() attrib2 = create_random_product_attribute() product_type = get_default_product_type() product_type.attributes.add(attrib1) product_type.attributes.add(attrib2) product_attr1_data = {"attribute": attrib1.pk, "numeric_value": 0} product_attr2_data = {"attribute": attrib2.pk, "numeric_value": 1} response = client.post("/api/shuup/product/%d/add_attribute/" % product1.pk, content_type="application/json", data=json.dumps(product_attr1_data)) assert response.status_code == status.HTTP_201_CREATED response = client.post("/api/shuup/product/%d/add_attribute/" % product1.pk, content_type="application/json", data=json.dumps(product_attr2_data)) assert response.status_code == status.HTTP_201_CREATED attrs = ProductAttribute.objects.filter(product=product1) # get the first attr response = client.get("/api/shuup/product_attribute/%d/" % attrs[0].id) assert response.status_code == status.HTTP_200_OK data = json.loads(response.content.decode("utf-8")) assert data["product"] == product1.id assert data["attribute"] == attrib1.id assert Decimal(data["numeric_value"]) == Decimal(0) # get the 2nd attr response = client.get("/api/shuup/product_attribute/%d/" % attrs[1].id) assert response.status_code == status.HTTP_200_OK data = json.loads(response.content.decode("utf-8")) assert data["product"] == product1.id assert data["attribute"] == attrib2.id assert Decimal(data["numeric_value"]) == Decimal(1) # update the first attr - numeric_value=1, attr=2 attr_data = {"numeric_value": 1, "attribute": attrib2.pk} response = client.put("/api/shuup/product_attribute/%d/" % attrs[0].id, content_type="application/json", data=json.dumps(attr_data)) assert response.status_code == status.HTTP_200_OK response = client.get("/api/shuup/product_attribute/%d/" % attrs[0].id) assert response.status_code == status.HTTP_200_OK data = json.loads(response.content.decode("utf-8")) assert data["product"] == product1.id assert data["attribute"] == attrib2.id assert Decimal(data["numeric_value"]) == Decimal(1) # deletes the last attr response = client.delete("/api/shuup/product_attribute/%d/" % attrs[1].id) assert response.status_code == status.HTTP_204_NO_CONTENT assert ProductAttribute.objects.count() == 1
def test_product_create(browser, admin_user, live_server, settings): activate("en") shop = get_default_shop() get_default_product_type() get_default_sales_unit() get_default_tax_class() object_created.connect(_add_custom_product_created_message, sender=Product, dispatch_uid="object_created_signal_test") initialize_admin_browser_test(browser, live_server, settings) url = reverse("shuup_admin:shop_product.new") browser.visit("%s%s" % (live_server, url)) wait_until_condition(browser, condition=lambda x: x.is_text_present("New shop product")) sku = "testsku" name = "Some product name" price_value = 10 short_description = "short but gold" move_to_element(browser, "#id_base-sku") browser.fill("base-sku", sku) browser.fill("base-name__en", name) browser.fill("base-short_description__en", short_description) browser.fill("shop%s-default_price_value" % shop.pk, price_value) _add_primary_category(browser, shop) _add_additional_category(browser, shop) move_to_element(browser, "button[form='product_form']") try: click_element(browser, "button[form='product_form']") wait_until_appeared(browser, "div[class='message success']") except selenium.common.exceptions.WebDriverException as e: # TODO: Revise! # Give a product save second chance it seems that the save can # lag a little and the success message doesn't happen fast # enough every single time. click_element(browser, "button[form='product_form']") wait_until_appeared(browser, "div[class='message success']") product = Product.objects.filter(sku=sku).first() assert product.log_entries.filter(identifier=OBJECT_CREATED_LOG_IDENTIFIER).count() == 1 object_created.disconnect(sender=Product, dispatch_uid="object_created_signal_test") shop_product = product.get_shop_instance(shop) assert shop_product.categories.count() == 2
def test_admin(rf, admin_user): activate("en") shop = get_default_shop() shop.staff_members.add(admin_user) get_default_tax_class() get_default_product_type() get_default_sales_unit() client = do_importing("123", "test", "en", shop) # change import language and update product client = do_importing("123", "test-fi", "fi", shop, client=client) # update name in english client = do_importing("123", "test-en", "en", shop, client=client) # cannot update client = do_importing("123", "test", "en", shop, import_mode=ImportMode.CREATE, client=client) # can update do_importing("123", "test", "en", shop, import_mode=ImportMode.UPDATE, client=client)
def import_categoryfile(filename, expected_category_count, map_from=None, map_to=None): activate("en") shop = get_default_shop() get_default_tax_class() get_default_product_type() get_default_supplier() path = os.path.join(os.path.dirname(__file__), "data", "product", filename) transformed_data = transform_file(filename.split(".")[1], path) importer = ProductImporter(transformed_data, shop, "en") importer.process_data() if map_from: assert len(importer.unmatched_fields) == 1 assert map_from in importer.unmatched_fields importer.manually_match(map_from, map_to) importer.do_remap() else: assert len(importer.unmatched_fields) == 0 importer.do_import(ImportMode.CREATE_UPDATE) products = importer.new_objects assert Category.objects.count() == expected_category_count
def test_sample_import_shop_relation(): activate("en") shop = get_default_shop() get_default_tax_class() get_default_product_type() get_default_sales_unit() path = os.path.join(os.path.dirname(__file__), "data", "product", "complex_import.xlsx") transformed_data = transform_file("xlsx", path) importer = ProductImporter(transformed_data, shop, "en") importer.process_data() importer.do_import(ImportMode.CREATE_UPDATE) products = importer.new_objects for product in products: shop_product = product.get_shop_instance(shop) for category in shop_product.categories.all(): assert shop in category.shops.all() if product.manufacturer: assert shop in product.manufacturer.shops.all()
def create_sample_product(name, description, business_segment, image_file, shop): product = Product.objects.create( name=name, description=description, type=get_default_product_type(), tax_class=get_default_tax_class(), sales_unit=SalesUnit.objects.first(), sku=fuzzy.FuzzyText(length=10).fuzz() ) image_file_path = os.path.join(SAMPLE_IMAGES_BASE_DIR, image_file) path = "ProductImages/Samples/%s" % business_segment.capitalize() filer_image = _filer_image_from_file_path(image_file_path, path) media_file = MediaFile.objects.create(file=filer_image) media_file.shops.add(shop) media = ProductMedia.objects.create( product=product, kind=ProductMediaKind.IMAGE, file=filer_image ) media.save() media.shops.add(shop) product.primary_image = media product.save() # create the price and round it to the number of decimals of the currency price = shop.create_price(decimal.Decimal(random.random() * random.randrange(0, 500))).as_rounded() sp = ShopProduct.objects.create( product=product, purchasable=True, visibility=ShopProductVisibility.ALWAYS_VISIBLE, default_price_value=price, shop=shop, shop_primary_image=media ) sp.categories = shop.categories.all() sp.suppliers.add(get_default_supplier()) # configure prices if "shuup.customer_group_pricing" in settings.INSTALLED_APPS: from shuup.customer_group_pricing.models import CgpPrice CgpPrice.objects.create( product=product, price_value=random.randint(15, 340), shop=shop, group=PersonContact.get_default_group() ) return product
def test_import_error(admin_user): shop = get_default_shop() shop.staff_members.add(admin_user) get_default_tax_class() get_default_product_type() get_default_sales_unit() client = SmartClient() client.login(username="******", password="******") import_path = reverse("shuup_admin:importer.import") process_path = reverse("shuup_admin:importer.import_process") client = SmartClient() client.login(username="******", password="******") csv_content = str.encode("sku;name\n123;Teste") data = { "importer": "product_importer", "shop": shop.pk, "language": "en", "file": SimpleUploadedFile("file.csv", csv_content, content_type="text/csv") } response = client.post(import_path, data=data) assert response.status_code == 302 query_string = urlparse(response["location"]).query query = parse_qs(query_string) data = { "importer": "product_importer", "shop": shop.pk, "language": "en", "n": query.get("n"), } data = {} process_submit_path = "%s?%s" % (process_path, query_string) response = client.post(process_submit_path, data=data) assert response.status_code == 302 assert "Failed to import the file." == list(response.wsgi_request._messages)[0].message
def test_admin(rf, admin_user): view = ImportView.as_view() activate("en") shop = get_default_shop() tax_class = get_default_tax_class() product_type = get_default_product_type() client = do_importing("123", "test", "en", shop) # change import language and update product client = do_importing("123", "test-fi", "fi", shop, client=client) # update name in english client = do_importing("123", "test-en", "en", shop, client=client) # cannot update client = do_importing("123", "test", "en", shop, import_mode=ImportMode.CREATE, client=client) # can update do_importing("123", "test", "en", shop, import_mode=ImportMode.UPDATE, client=client)
def test_sample_import_all_match_all_shops(filename): activate("en") shop1 = get_shop(identifier="shop1", domain="shop1", enabled=True) shop2 = get_shop(identifier="shop2", domain="shop2", enabled=True) Product.objects.all().delete() tax_class = get_default_tax_class() product_type = get_default_product_type() sales_unit = get_default_sales_unit() path = os.path.join(os.path.dirname(__file__), "data", "product", filename) transformed_data = transform_file(filename.split(".")[1], path) for shop in [shop1, shop2]: importer = ProductImporter(transformed_data, shop, "en") importer.process_data() assert len(importer.unmatched_fields) == 0 importer.do_import(ImportMode.CREATE_UPDATE) products = importer.new_objects if shop == shop1: # products created assert len(products) == 2 else: # products already exist assert len(products) == 0 assert Product.objects.count() == 2 for product in Product.objects.all(): shop_product = product.get_shop_instance(shop) assert shop_product.pk assert shop_product.default_price_value == 150 assert shop_product.default_price == shop.create_price(150) assert product.type == product_type # product type comes from importer defaults assert product.sales_unit == sales_unit assert shop_product.primary_category.pk == 1 assert [c.pk for c in shop_product.categories.all()] == [1, 2] assert ShopProduct.objects.count() == 4
def _get_product_sample_data(): return { # translations "translations":{ "en": { "name": "Product Name", "description": "Product Description", "slug": "product_sku", "keywords": "keyword1, k3yw0rd2", "status_text": "available soon", "variation_name": "Product RED" }, "pt-br": { "name": "Nome do Produto", "description": "Descrição do Produto", "slug": "product_sku_em_portugues", "keywords": "chave1, chavez2", "status_text": "disponivel logo", "variation_name": "Produto Vermelho" } }, # others "stock_behavior": StockBehavior.STOCKED.value, "shipping_mode": ShippingMode.SHIPPED.value, "sales_unit": get_default_sales_unit().pk, "tax_class": get_default_tax_class().pk, "type": get_default_product_type().pk, "sku": "sku12345", "gtin": "789456132", "barcode": "7896899123456", "accounting_identifier": "cbe6a7d67a8bdae", "profit_center": "prooofit!", "cost_center": "space ghost", "width": 150.0, "height": 230.0, "depth": 450.4, "net_weight": 13.2, "gross_weight": 20.3 }
def test_invalid_files(rf, admin_user): lang = "en" import_mode = ImportMode.CREATE_UPDATE sku = "123" name = "test" import_path = reverse("shuup_admin:importer.import") process_path = reverse("shuup_admin:importer.import_process") tax_class = get_default_tax_class() product_type = get_default_product_type() get_default_sales_unit() activate("en") shop = get_default_shop() shop.staff_members.add(admin_user) client = SmartClient() client.login(username="******", password="******") csv_content = str.encode("sku;name\n%s;%s" % (sku, name)) # show import view data = { "importer": "product_importer", "shop": shop.pk, "language": lang, "file": SimpleUploadedFile("file.csv", csv_content, content_type="text/csv") } response = client.post(import_path, data=data) assert response.status_code == 302 query_string = urlparse(response["location"]).query query = parse_qs(query_string) data = { # data with missing `n` "importer": "product_importer", "shop": shop.pk, "language": lang, } response, soup = client.response_and_soup(process_path, data=data) assert response.status_code == 400 assert "File missing." in str(soup)
def test_remap(rf, admin_user): lang = "en" import_mode = ImportMode.CREATE_UPDATE sku = "123" name = "test" import_path = reverse("shuup_admin:importer.import") process_path = reverse("shuup_admin:importer.import_process") tax_class = get_default_tax_class() product_type = get_default_product_type() activate("en") shop = get_default_shop() client = SmartClient() client.login(username="******", password="******") csv_content = str.encode("sku;name;gtiin\n%s;%s;111" % (sku, name)) # show import view data = { "importer": "product_importer", "shop": shop.pk, "language": lang, "file": SimpleUploadedFile("file.csv", csv_content, content_type="text/csv") } response = client.post(import_path, data=data) assert response.status_code == 302 query_string = urlparse(response["location"]).query query = parse_qs(query_string) data = { # data with missing `n` "importer": "product_importer", "shop": shop.pk, "language": lang, "n": query.get("n") } soup = client.soup(process_path, data=data) assert "The following fields must be manually tied" in str(soup) for row in soup.findAll("tr"): first_td = row.find("td") if not first_td: continue tds = row.findAll("td") if len(tds) < 2: continue second_td = tds[1] if first_td.text.startswith("Connection"): # check that sku was connected badges = second_td.findAll("span", {"class": "badge"}) assert len(badges) == 1 assert badges[0].text == "sku" elif first_td.text.startswith("The following fields"): badges = second_td.findAll("span", {"class": "badge"}) assert len(badges) == 2 badge_texts = [] for b in badges: badge_texts.append(b.text) badge_texts.sort() assert badge_texts[0] == "name" assert badge_texts[1] == "sku" data = {} data["import_mode"] = import_mode.value data["remap[gtiin]"] = "Product:gtin" # map gtin process_submit_path = "%s?%s" % (process_path, query_string) client.soup(process_submit_path, data=data, method="post") assert Product.objects.count() == 1 product = Product.objects.first() assert product.gtin == "111"
def test_sample_import_all_match(filename): activate("en") shop = get_default_shop() tax_class = get_default_tax_class() product_type = get_default_product_type() sales_unit = get_default_sales_unit() path = os.path.join(os.path.dirname(__file__), "data", "product", filename) if filename == bom_file: import codecs bytes = min(32, os.path.getsize(path)) raw = open(path, 'rb').read(bytes) assert raw.startswith(codecs.BOM_UTF8) transformed_data = transform_file(filename.split(".")[1], path) importer = ProductImporter(transformed_data, shop, "en") importer.process_data() if filename == images_file: assert len(importer.unmatched_fields) == 2 _create_random_media_file(shop, "image1.jpeg") _create_random_media_file(shop, "products/images/image2.jpeg") _create_random_media_file(shop, "products/images/image3.jpeg") _create_random_media_file(shop, "image4.jpeg") _create_random_media_file(shop, "products2/images/image5.jpeg") _create_random_media_file(shop, "product1.jpeg") _create_random_media_file(shop, "products/images/product2.jpeg") _create_random_media_file(shop, "products/images2/product2.jpeg") else: assert len(importer.unmatched_fields) == 0 importer.do_import(ImportMode.CREATE_UPDATE) products = importer.new_objects if filename == images_file: assert len(products) == 3 else: assert len(products) == 2 for product in products: shop_product = product.get_shop_instance(shop) assert shop_product.pk assert shop_product.default_price_value == 150 assert shop_product.default_price == shop.create_price(150) assert product.type == product_type # product type comes from importer defaults assert product.sales_unit == sales_unit if product.pk == 1: assert product.tax_class.pk == 2 # new was created assert product.name == "Product English" assert product.description == "Description English" if filename == images_file: assert product.media.count() == 3 elif product.pk == 2: assert product.tax_class.pk == tax_class.pk # old was found as should assert product.name == "Product 2 English" assert product.description == "Description English 2" if filename == images_file: assert product.media.count() == 2 elif product.pk == 3 and filename == images_file: assert product.media.count() == 3 assert shop_product.primary_category.pk == 1 assert [c.pk for c in shop_product.categories.all()] == [1,2]
def test_sample_import_no_match(rf, stock_managed): filename = "sample_import_nomatch.xlsx" if "shuup.simple_supplier" not in settings.INSTALLED_APPS: pytest.skip("Need shuup.simple_supplier in INSTALLED_APPS") from shuup_tests.simple_supplier.utils import get_simple_supplier activate("en") shop = get_default_shop() tax_class = get_default_tax_class() product_type = get_default_product_type() supplier = get_simple_supplier(stock_managed) sales_unit = get_default_sales_unit() Manufacturer.objects.create(name="manufctr") path = os.path.join(os.path.dirname(__file__), "data", "product", filename) transformed_data = transform_file(filename.split(".")[1], path) importer = ProductImporter( transformed_data, ProductImporter.get_importer_context(rf.get("/"), shop=shop, language="en")) importer.process_data() assert len(importer.unmatched_fields) == 1 assert "gtiin" in importer.unmatched_fields importer.manually_match("gtiin", "shuup.core.models.Product:gtin") importer.do_remap() assert len(importer.unmatched_fields) == 0 importer.do_import(ImportMode.CREATE_UPDATE) products = importer.new_objects assert len(products) == 2 for product in products: assert product.gtin == "1280x720" shop_product = product.get_shop_instance(shop) assert shop_product.pk assert shop_product.default_price_value == 150 assert shop_product.default_price == shop.create_price(150) assert product.type == product_type # product type comes from importer defaults assert product.sales_unit == sales_unit if product.pk == 1: assert product.tax_class.pk == 2 # new was created assert product.name == "Product English" assert product.description == "Description English" else: assert product.tax_class.pk == tax_class.pk # old was found as should assert product.name == "Product 2 English" assert product.description == "Description English 2" assert shop_product.primary_category.pk == 1 assert [c.pk for c in shop_product.categories.all()] == [1, 2] # stock was not managed since supplier doesn't like that for msg in importer.other_log_messages: assert "please set `Stock Managed` on" in msg supplier.stock_managed = True supplier.save() importer.do_import("create,update") assert len(importer.other_log_messages) == 0 for sa in StockAdjustment.objects.all(): assert sa.product.pk assert sa.delta == 20
def test_product_type_selector(admin_user): shop = get_default_shop() get_default_product_type() assert get_object_selector_results(ProductType, shop, admin_user, "default")
def test_invalid_file_type(rf, admin_user): lang = "en" import_mode = ImportMode.CREATE_UPDATE sku = "123" name = "test" import_path = reverse("shuup_admin:importer.import") process_path = reverse("shuup_admin:importer.import_process") get_default_tax_class() get_default_product_type() get_default_sales_unit() activate("en") shop = get_default_shop() shop.staff_members.add(admin_user) client = SmartClient() client.login(username="******", password="******") csv_content = str.encode("sku;name\n%s;%s" % (sku, name)) # show import view data = { "importer": "product_importer", "shop": shop.pk, "language": lang, "file": SimpleUploadedFile("file.derp", csv_content, content_type="text/csv") } response = client.post(import_path, data=data) assert response.status_code == 302 query_string = urlparse(response["location"]).query query = parse_qs(query_string) data = { # data with missing `n` "importer": "product_importer", "shop": shop.pk, "language": lang, } response, soup = client.response_and_soup(process_path, data=data) assert response.status_code == 400 for row in soup.findAll("tr"): first_td = row.find("td") if not first_td: continue tds = row.findAll("td") if len(tds) < 2: continue second_td = tds[1] if first_td.text.startswith("Connection"): # check that sku was connected badges = second_td.findAll("span", {"class": "badge"}) assert len(badges) == 1 assert badges[0].text == "sku" elif first_td.text.startswith("The following fields"): badges = second_td.findAll("span", {"class": "badge"}) assert len(badges) == 2 badge_texts = [] for b in badges: badge_texts.append(b.text) badge_texts.sort() assert badge_texts[0] == "name" assert badge_texts[1] == "sku" data = {} data["import_mode"] = import_mode.value process_submit_path = "%s?%s" % (process_path, query_string) response = client.post(process_submit_path, data=data) assert response.status_code == 302
def test_edit_button_no_permission(browser, admin_user, live_server, settings): shop = get_default_shop() manager_group = Group.objects.create(name="Managers") manager = create_random_user("en", is_staff=True) manager.username = "******" manager.set_password("password") manager.save() manager.groups.add(manager_group) shop.staff_members.add(manager) # add permissions for Product admin module manager_permissions = set(["dashboard", "Products", "shop_product.new"]) set_permissions_for_group(manager_group, manager_permissions) get_default_product_type() get_default_sales_unit() get_default_tax_class() initialize_admin_browser_test(browser, live_server, settings, username=manager.username) url = reverse("shuup_admin:shop_product.new") browser.visit("%s%s" % (live_server, url)) sku = "testsku" name = "Some product name" price_value = 10 short_description = "short but gold" browser.fill("base-sku", sku) browser.fill("base-name__en", name) browser.fill("base-short_description__en", short_description) browser.fill("shop%s-default_price_value" % shop.pk, price_value) wait_until_appeared(browser, "#id_shop%d-primary_category ~ .quick-add-btn a.btn" % shop.id) click_element(browser, "#id_shop%d-primary_category ~ .quick-add-btn a.btn" % shop.id) wait_until_appeared(browser, "#create-object-iframe") # no permission to add category with browser.get_iframe('create-object-iframe') as iframe: error = "Can't view this page. You do not have the required permissions: category.new" wait_until_condition(iframe, condition=lambda x: x.is_text_present(error)) # close iframe click_element(browser, "#create-object-overlay a.close-btn") # add permission to add category manager_permissions.add("category.new") manager_permissions.add("category.edit") set_permissions_for_group(manager_group, manager_permissions) # click to add category again click_element(browser, "#id_shop%d-primary_category ~ .quick-add-btn a.btn" % shop.id) wait_until_appeared(browser, "#create-object-iframe") # add the category with browser.get_iframe('create-object-iframe') as iframe: assert Category.objects.count() == 0 wait_until_appeared(iframe, "input[name='base-name__en']") iframe.fill("base-name__en", "Test Category") time.sleep(3) # Let's just wait here to the iFrame to open fully (for Chrome and headless) wait_until_appeared(iframe, "button[form='category_form']") click_element(browser, "button[form='category_form']") wait_until_condition(browser, condition=lambda x: Category.objects.count() == 1, timeout=20) assert Category.objects.first().name == "Test Category" # remove the edit category permissions # add permission to add category manager_permissions.remove("category.edit") set_permissions_for_group(manager_group, manager_permissions) # click to edit the button click_element(browser, "#id_shop%d-primary_category ~ .edit-object-btn a.btn" % shop.id) # no permission to edit category with browser.get_iframe('create-object-iframe') as iframe: error = "Can't view this page. You do not have the required permission(s): category.edit" wait_until_condition(iframe, condition=lambda x: x.is_text_present(error)) # close iframe click_element(browser, "#create-object-overlay a.close-btn") manager_permissions.add("category.edit") set_permissions_for_group(manager_group, manager_permissions) click_element(browser, "#id_shop%d-primary_category ~ .edit-object-btn a.btn" % shop.id) wait_until_appeared(browser, "#create-object-iframe") new_cat_name = "Changed Name" with browser.get_iframe('create-object-iframe') as iframe: wait_until_appeared(iframe, "input[name='base-name__en']") iframe.fill("base-name__en", new_cat_name) time.sleep(3) # Let's just wait here to the iFrame to open fully (for Chrome and headless) wait_until_appeared(iframe, "button[form='category_form']") click_element(browser, "button[form='category_form']") wait_until_condition(browser, condition=lambda x: Category.objects.first().name == new_cat_name, timeout=20)
def test_variatins_import(rf): filename = "product_sample_import_with_variations.xlsx" if "shuup.simple_supplier" not in settings.INSTALLED_APPS: pytest.skip("Need shuup.simple_supplier in INSTALLED_APPS") from shuup_tests.simple_supplier.utils import get_simple_supplier activate("en") shop = get_default_shop() product_type = get_default_product_type() sales_unit = get_default_sales_unit() tax_class = get_default_tax_class() # Create media _create_random_media_file(shop, "shirt1.jpeg") _create_random_media_file(shop, "shirt2.jpeg") _create_random_media_file(shop, "shirt3.jpeg") path = os.path.join(os.path.dirname(__file__), "data", "product", filename) transformed_data = transform_file(filename.split(".")[1], path) importer = ProductImporter( transformed_data, ProductImporter.get_importer_context(rf.get("/"), shop=shop, language="en")) importer.process_data() assert len(importer.unmatched_fields) == 0 importer.do_import(ImportMode.CREATE_UPDATE) products = importer.new_objects assert len(products) == 42 # 2 parents 20 variations each supplier = Supplier.objects.first() assert supplier and supplier.stock_managed and supplier.module_identifier == "simple_supplier" assert ShopProduct.objects.filter(suppliers=supplier).count() == 42 parent1 = Product.objects.filter(sku=1).first() assert parent1.mode == ProductMode.VARIABLE_VARIATION_PARENT assert parent1.variation_children.all().count() == 20 # Check product names child1 = Product.objects.filter(sku=10).first() assert child1.name == "T-Shirt - Pink - S" child2 = Product.objects.filter(sku=11).first() assert child2.name == "T-Shirt - Pink - XS" # Check stock counts assert supplier.get_stock_status(child1.pk).logical_count == Decimal(5) assert supplier.get_stock_status(child2.pk).logical_count == Decimal(10) parent2 = Product.objects.filter(sku=22).first() assert parent1.mode == ProductMode.VARIABLE_VARIATION_PARENT assert parent1.variation_children.all().count() == 20 # Check product names child1 = Product.objects.filter(sku=38).first() assert child1.name == "Custom T-Shirt - Black - XL" child2 = Product.objects.filter(sku=39).first() assert child2.name == "Custom T-Shirt - Black - L" # Check stock counts assert supplier.get_stock_status(child1.pk).logical_count == Decimal(5) assert supplier.get_stock_status(child2.pk).logical_count == Decimal(10) path = os.path.join(os.path.dirname(__file__), "data", "product", "product_sample_import_with_variations_update.xlsx") transformed_data = transform_file(filename.split(".")[1], path) importer = ProductImporter( transformed_data, ProductImporter.get_importer_context(rf.get("/"), shop=shop, language="en")) importer.process_data() assert len(importer.unmatched_fields) == 0 importer.do_import(ImportMode.CREATE_UPDATE) products = importer.new_objects assert len(products) == 0 updated_products = importer.updated_objects assert len(updated_products) == 4 # Check product names child1 = Product.objects.filter(sku=10).first() assert child1.name == "T-Shirt - Pink - S" child2 = Product.objects.filter(sku=11).first() assert child2.name == "Test" # Check stock counts assert supplier.get_stock_status(child1.pk).logical_count == Decimal(5) assert supplier.get_stock_status(child2.pk).logical_count == Decimal( 20) # Did not add 20 but made the logical to 20 parent2 = Product.objects.filter(sku=22).first() assert parent1.mode == ProductMode.VARIABLE_VARIATION_PARENT assert parent1.variation_children.all().count() == 20 # Check product names child1 = Product.objects.filter(sku=38).first() assert child1.name == "Custom T-Shirt - Black - XL" child2 = Product.objects.filter(sku=39).first() assert child2.name == "Custom T-Shirt - Black - L" # Check stock counts assert supplier.get_stock_status(child1.pk).logical_count == Decimal(15) assert supplier.get_stock_status(child2.pk).logical_count == Decimal(10) # Test file with missing variations path = os.path.join( os.path.dirname(__file__), "data", "product", "product_sample_import_with_variations_missing_variables.xlsx") transformed_data = transform_file(filename.split(".")[1], path) importer = ProductImporter( transformed_data, ProductImporter.get_importer_context(rf.get("/"), shop=shop, language="en")) importer.process_data() assert len(importer.unmatched_fields) == 0 importer.do_import(ImportMode.CREATE_UPDATE) products = importer.new_objects assert len(products) == 0 updated_products = importer.updated_objects assert len(updated_products) == 4 for log_message in importer.log_messages: assert "Parent SKU set for the row, but no variation" in log_message[ "messages"][0] # check that both variation products still looks correct parent1 = Product.objects.filter(sku=1).first() assert parent1.mode == ProductMode.VARIABLE_VARIATION_PARENT assert parent1.variation_children.all().count() == 20 parent2 = Product.objects.filter(sku=22).first() assert parent1.mode == ProductMode.VARIABLE_VARIATION_PARENT assert parent1.variation_children.all().count() == 20
def test_quick_add(browser, admin_user, live_server, settings): shop = get_default_shop() get_default_product_type() get_default_sales_unit() get_default_tax_class() initialize_admin_browser_test(browser, live_server, settings) url = reverse("shuup_admin:shop_product.new") browser.visit("%s%s" % (live_server, url)) sku = "testsku" name = "Some product name" price_value = 10 short_description = "short but gold" browser.fill("base-sku", sku) browser.fill("base-name__en", name) browser.fill("base-short_description__en", short_description) browser.fill("shop%s-default_price_value" % shop.pk, price_value) wait_until_appeared( browser, "#id_shop%d-primary_category ~ .quick-add-btn a.btn" % shop.id) click_element( browser, "#id_shop%d-primary_category ~ .quick-add-btn a.btn" % shop.id) wait_until_appeared(browser, "#create-object-iframe") with browser.get_iframe("create-object-iframe") as iframe: assert Category.objects.count() == 0 wait_until_appeared(iframe, "input[name='base-name__en']") iframe.fill("base-name__en", "Test Category") time.sleep( 3 ) # Let's just wait here to the iFrame to open fully (for Chrome and headless) wait_until_appeared(iframe, "button[form='category_form']") click_element(browser, "button[form='category_form']") wait_until_condition(browser, condition=lambda x: Category.objects.count() == 1, timeout=20) assert Category.objects.first().name == "Test Category" # click to edit the button click_element( browser, "#id_shop%d-primary_category ~ .edit-object-btn a.btn" % shop.id) with browser.get_iframe("create-object-iframe") as iframe: wait_until_appeared(iframe, "input[name='base-name__en']") new_cat_name = "Changed Name" iframe.fill("base-name__en", new_cat_name) time.sleep( 3 ) # Let's just wait here to the iFrame to open fully (for Chrome and headless) wait_until_appeared(iframe, "button[form='category_form']") click_element(iframe, "button[form='category_form']") wait_until_condition( browser, condition=lambda x: Category.objects.first().name == new_cat_name, timeout=20) click_element(browser, "button[form='product_form']") wait_until_appeared(browser, "div[class='message success']")
def test_create_product_with_shop_product_and_attributes(admin_user): shop = get_default_shop() client = _get_client(admin_user) supplier = get_default_supplier() cat = Category.objects.create( status=CategoryStatus.VISIBLE, visibility=CategoryVisibility.VISIBLE_TO_ALL, identifier="test_category", name="Test" ) product_type = get_default_product_type() assert Attribute.objects.count() > 0 assert Product.objects.count() == 0 attributes_data = [] expected_values = { "untranslated_string_value": "test value", "numeric_value": 12, "boolean_value": True, "timedelta_value": "200", # seconds "datetime_value": "2017-01-01 01:00:00", "translated_string_value": "translated string value" } for spec in ATTR_SPECS: attr = Attribute.objects.get(identifier=spec["identifier"]) attr_data = { "numeric_value": None, "datetime_value": None, "untranslated_string_value": "", "attribute": attr.pk, # "product": product.pk } if attr.is_stringy: if attr.is_translated: attr_data["translations"] = { "en": { "translated_string_value": expected_values["translated_string_value"] } } else: attr_data["untranslated_string_value"] = expected_values["untranslated_string_value"] elif attr.is_numeric: if attr.type == AttributeType.BOOLEAN: attr_data["numeric_value"] = int(expected_values["boolean_value"]) elif attr.type == AttributeType.TIMEDELTA: attr_data["numeric_value"] = int(expected_values["timedelta_value"]) else: attr_data["numeric_value"] = expected_values["numeric_value"] elif attr.is_temporal: attr_data["datetime_value"] = expected_values["datetime_value"] attributes_data.append(attr_data) data = _get_product_sample_data() data["shop_products"] = _get_sample_shop_product_data(shop, cat, supplier) data["attributes"] = attributes_data response = client.post("/api/shuup/product/", content_type="application/json", data=json.dumps(data)) assert response.status_code == status.HTTP_201_CREATED # check all for lang in ("en", "pt-br"): activate(lang) product = Product.objects.first() _check_product_basic_data(product, data, lang) assert Product.objects.count() == 1 assert ShopProduct.objects.count() == 1 product = Product.objects.first() shop_product = ShopProduct.objects.first() assert product.get_shop_instance(shop) == shop_product assert supplier in shop_product.suppliers.all() assert cat in shop_product.categories.all() assert shop_product.primary_category == cat # validate attribute values for spec in ATTR_SPECS: attribute = Attribute.objects.get(identifier=spec["identifier"]) attr = ProductAttribute.objects.get(product=product, attribute=attribute) if attribute.is_stringy: if attribute.is_translated: attr.set_current_language("en") assert attr.value == expected_values["translated_string_value"] else: assert attr.value == expected_values["untranslated_string_value"] elif attribute.is_numeric: if attribute.type == AttributeType.BOOLEAN: assert attr.value == expected_values["boolean_value"] elif attribute.type == AttributeType.TIMEDELTA: assert attr.value == datetime.timedelta(seconds=int(expected_values["timedelta_value"])) else: assert attr.value == expected_values["numeric_value"] elif attribute.is_temporal: dt_value = expected_values["datetime_value"] parsed_dt = dt.strptime(dt_value, "%Y-%m-%d %H:%M:%S") assert attr.value.year == parsed_dt.year assert attr.value.month == parsed_dt.month assert attr.value.day == parsed_dt.day
def test_create_product_with_shop_product_and_attributes(admin_user): shop = get_default_shop() client = _get_client(admin_user) supplier = get_default_supplier() cat = Category.objects.create(status=CategoryStatus.VISIBLE, visibility=CategoryVisibility.VISIBLE_TO_ALL, identifier="test_category", name="Test") product_type = get_default_product_type() assert Attribute.objects.count() > 0 assert Product.objects.count() == 0 attributes_data = [] expected_values = { "untranslated_string_value": "test value", "numeric_value": 12, "boolean_value": True, "timedelta_value": "200", # seconds "datetime_value": "2017-01-01 01:00:00", "translated_string_value": "translated string value" } for spec in ATTR_SPECS: attr = Attribute.objects.get(identifier=spec["identifier"]) attr_data = { "numeric_value": None, "datetime_value": None, "untranslated_string_value": "", "attribute": attr.pk, # "product": product.pk } if attr.is_stringy: if attr.is_translated: attr_data["translations"] = { "en": { "translated_string_value": expected_values["translated_string_value"] } } else: attr_data["untranslated_string_value"] = expected_values[ "untranslated_string_value"] elif attr.is_numeric: if attr.type == AttributeType.BOOLEAN: attr_data["numeric_value"] = int( expected_values["boolean_value"]) elif attr.type == AttributeType.TIMEDELTA: attr_data["numeric_value"] = int( expected_values["timedelta_value"]) else: attr_data["numeric_value"] = expected_values["numeric_value"] elif attr.is_temporal: attr_data["datetime_value"] = expected_values["datetime_value"] attributes_data.append(attr_data) data = _get_product_sample_data() data["shop_products"] = _get_sample_shop_product_data(shop, cat, supplier) data["attributes"] = attributes_data response = client.post("/api/shuup/product/", content_type="application/json", data=json.dumps(data)) assert response.status_code == status.HTTP_201_CREATED # check all for lang in ("en", "pt-br"): activate(lang) product = Product.objects.first() _check_product_basic_data(product, data, lang) assert Product.objects.count() == 1 assert ShopProduct.objects.count() == 1 product = Product.objects.first() shop_product = ShopProduct.objects.first() assert product.get_shop_instance(shop) == shop_product assert supplier in shop_product.suppliers.all() assert cat in shop_product.categories.all() assert shop_product.primary_category == cat # validate attribute values for spec in ATTR_SPECS: attribute = Attribute.objects.get(identifier=spec["identifier"]) attr = ProductAttribute.objects.get(product=product, attribute=attribute) if attribute.is_stringy: if attribute.is_translated: attr.set_current_language("en") assert attr.value == expected_values["translated_string_value"] else: assert attr.value == expected_values[ "untranslated_string_value"] elif attribute.is_numeric: if attribute.type == AttributeType.BOOLEAN: assert attr.value == expected_values["boolean_value"] elif attribute.type == AttributeType.TIMEDELTA: assert attr.value == datetime.timedelta( seconds=int(expected_values["timedelta_value"])) else: assert attr.value == expected_values["numeric_value"] elif attribute.is_temporal: dt_value = expected_values["datetime_value"] parsed_dt = dt.strptime(dt_value, "%Y-%m-%d %H:%M:%S") assert attr.value.year == parsed_dt.year assert attr.value.month == parsed_dt.month assert attr.value.day == parsed_dt.day
def test_edit_button_no_permission(browser, admin_user, live_server, settings): shop = get_default_shop() manager_group = Group.objects.create(name="Managers") manager = create_random_user("en", is_staff=True) manager.username = "******" manager.set_password("password") manager.save() manager.groups.add(manager_group) shop.staff_members.add(manager) # add permissions for Product admin module manager_permissions = set(["dashboard", "Products", "shop_product.new"]) set_permissions_for_group(manager_group, manager_permissions) get_default_product_type() get_default_sales_unit() get_default_tax_class() initialize_admin_browser_test(browser, live_server, settings, username=manager.username) url = reverse("shuup_admin:shop_product.new") browser.visit("%s%s" % (live_server, url)) sku = "testsku" name = "Some product name" price_value = 10 short_description = "short but gold" browser.fill("base-sku", sku) browser.fill("base-name__en", name) browser.fill("base-short_description__en", short_description) browser.fill("shop%s-default_price_value" % shop.pk, price_value) wait_until_appeared(browser, "#id_shop%d-primary_category ~ .quick-add-btn a.btn" % shop.id) click_element(browser, "#id_shop%d-primary_category ~ .quick-add-btn a.btn" % shop.id) wait_until_appeared(browser, "#create-object-iframe") # no permission to add category with browser.get_iframe('create-object-iframe') as iframe: error = "Can't view this page. You do not have the required permissions: category.new" wait_until_condition(iframe, condition=lambda x: x.is_text_present(error)) # close iframe click_element(browser, "#create-object-overlay a.close-btn") # add permission to add category manager_permissions.add("category.new") manager_permissions.add("category.edit") set_permissions_for_group(manager_group, manager_permissions) # click to add category again click_element(browser, "#id_shop%d-primary_category ~ .quick-add-btn a.btn" % shop.id) wait_until_appeared(browser, "#create-object-iframe") # add the category with browser.get_iframe('create-object-iframe') as iframe: assert Category.objects.count() == 0 wait_until_appeared(iframe, "input[name='base-name__en']") iframe.fill("base-name__en", "Test Category") time.sleep(3) # Let's just wait here to the iFrame to open fully (for Chrome and headless) wait_until_appeared(iframe, "button[form='category_form']") click_element(browser, "button[form='category_form']") wait_until_condition(browser, condition=lambda x: Category.objects.count() == 1, timeout=20) assert Category.objects.first().name == "Test Category" # remove the edit category permissions # add permission to add category manager_permissions.remove("category.edit") set_permissions_for_group(manager_group, manager_permissions) # click to edit the button click_element(browser, "#id_shop%d-primary_category ~ .edit-object-btn a.btn" % shop.id) # no permission to edit category with browser.get_iframe('create-object-iframe') as iframe: error = "Can't view this page. You do not have the required permission(s): `category.edit`." wait_until_condition(iframe, condition=lambda x: x.is_text_present(error)) # close iframe click_element(browser, "#create-object-overlay a.close-btn") manager_permissions.add("category.edit") set_permissions_for_group(manager_group, manager_permissions) click_element(browser, "#id_shop%d-primary_category ~ .edit-object-btn a.btn" % shop.id) wait_until_appeared(browser, "#create-object-iframe") new_cat_name = "Changed Name" with browser.get_iframe('create-object-iframe') as iframe: wait_until_appeared(iframe, "input[name='base-name__en']") iframe.fill("base-name__en", new_cat_name) time.sleep(3) # Let's just wait here to the iFrame to open fully (for Chrome and headless) wait_until_appeared(iframe, "button[form='category_form']") click_element(browser, "button[form='category_form']") wait_until_condition(browser, condition=lambda x: Category.objects.first().name == new_cat_name, timeout=20)