summaryrefslogtreecommitdiff
path: root/addons/website_sale/tests/test_website_sale_image.py
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/website_sale/tests/test_website_sale_image.py
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/website_sale/tests/test_website_sale_image.py')
-rw-r--r--addons/website_sale/tests/test_website_sale_image.py320
1 files changed, 320 insertions, 0 deletions
diff --git a/addons/website_sale/tests/test_website_sale_image.py b/addons/website_sale/tests/test_website_sale_image.py
new file mode 100644
index 00000000..b50f7ae6
--- /dev/null
+++ b/addons/website_sale/tests/test_website_sale_image.py
@@ -0,0 +1,320 @@
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+import base64
+import io
+
+from PIL import Image
+
+import odoo.tests
+
+
+@odoo.tests.common.tagged('post_install', '-at_install')
+class TestWebsiteSaleImage(odoo.tests.HttpCase):
+
+ # registry_test_mode = False # uncomment to save the product to test in browser
+
+ def test_01_admin_shop_zoom_tour(self):
+ color_red = '#CD5C5C'
+ name_red = 'Indian Red'
+
+ color_green = '#228B22'
+ name_green = 'Forest Green'
+
+ color_blue = '#4169E1'
+ name_blue = 'Royal Blue'
+
+ # create the color attribute
+ product_attribute = self.env['product.attribute'].create({
+ 'name': 'Beautiful Color',
+ 'display_type': 'color',
+ })
+
+ # create the color attribute values
+ attr_values = self.env['product.attribute.value'].create([{
+ 'name': name_red,
+ 'attribute_id': product_attribute.id,
+ 'html_color': color_red,
+ 'sequence': 1,
+ }, {
+ 'name': name_green,
+ 'attribute_id': product_attribute.id,
+ 'html_color': color_green,
+ 'sequence': 2,
+ }, {
+ 'name': name_blue,
+ 'attribute_id': product_attribute.id,
+ 'html_color': color_blue,
+ 'sequence': 3,
+ }])
+
+ # first image (blue) for the template
+ f = io.BytesIO()
+ Image.new('RGB', (1920, 1080), color_blue).save(f, 'JPEG')
+ f.seek(0)
+ blue_image = base64.b64encode(f.read())
+
+ # second image (red) for the variant 1, small image (no zoom)
+ f = io.BytesIO()
+ Image.new('RGB', (800, 500), color_red).save(f, 'JPEG')
+ f.seek(0)
+ red_image = base64.b64encode(f.read())
+
+ # second image (green) for the variant 2, big image (zoom)
+ f = io.BytesIO()
+ Image.new('RGB', (1920, 1080), color_green).save(f, 'JPEG')
+ f.seek(0)
+ green_image = base64.b64encode(f.read())
+
+ # Template Extra Image 1
+ f = io.BytesIO()
+ Image.new('RGB', (124, 147)).save(f, 'GIF')
+ f.seek(0)
+ image_gif = base64.b64encode(f.read())
+
+ # Template Extra Image 2
+ image_svg = base64.b64encode(b'<svg></svg>')
+
+ # Red Variant Extra Image 1
+ f = io.BytesIO()
+ Image.new('RGB', (767, 247)).save(f, 'BMP')
+ f.seek(0)
+ image_bmp = base64.b64encode(f.read())
+
+ # Green Variant Extra Image 1
+ f = io.BytesIO()
+ Image.new('RGB', (2147, 3251)).save(f, 'PNG')
+ f.seek(0)
+ image_png = base64.b64encode(f.read())
+
+ # create the template, without creating the variants
+ template = self.env['product.template'].with_context(create_product_product=True).create({
+ 'name': 'A Colorful Image',
+ 'product_template_image_ids': [(0, 0, {'name': 'image 1', 'image_1920': image_gif}), (0, 0, {'name': 'image 4', 'image_1920': image_svg})],
+ })
+
+ # set the color attribute and values on the template
+ line = self.env['product.template.attribute.line'].create([{
+ 'attribute_id': product_attribute.id,
+ 'product_tmpl_id': template.id,
+ 'value_ids': [(6, 0, attr_values.ids)]
+ }])
+ value_red = line.product_template_value_ids[0]
+ value_green = line.product_template_value_ids[1]
+
+ # set a different price on the variants to differentiate them
+ product_template_attribute_values = self.env['product.template.attribute.value'].search([('product_tmpl_id', '=', template.id)])
+
+ for val in product_template_attribute_values:
+ if val.name == name_red:
+ val.price_extra = 10
+ else:
+ val.price_extra = 20
+
+ # Get RED variant, and set image to blue (will be set on the template
+ # because the template image is empty and there is only one variant)
+ product_red = template._get_variant_for_combination(value_red)
+ product_red.write({
+ 'image_1920': blue_image,
+ 'product_variant_image_ids': [(0, 0, {'name': 'image 2', 'image_1920': image_bmp})],
+ })
+
+ self.assertEqual(template.image_1920, blue_image)
+
+ # Get the green variant
+ product_green = template._get_variant_for_combination(value_green)
+ product_green.write({
+ 'image_1920': green_image,
+ 'product_variant_image_ids': [(0, 0, {'name': 'image 3', 'image_1920': image_png})],
+ })
+
+ # now set the red image on the first variant, that works because
+ # template image is not empty anymore and we have a second variant
+ product_red.image_1920 = red_image
+
+ # Verify image_1920 size > 1024 can be zoomed
+ self.assertTrue(template.can_image_1024_be_zoomed)
+ self.assertFalse(template.product_template_image_ids[0].can_image_1024_be_zoomed)
+ self.assertFalse(template.product_template_image_ids[1].can_image_1024_be_zoomed)
+ self.assertFalse(product_red.can_image_1024_be_zoomed)
+ self.assertFalse(product_red.product_variant_image_ids[0].can_image_1024_be_zoomed)
+ self.assertTrue(product_green.can_image_1024_be_zoomed)
+ self.assertTrue(product_green.product_variant_image_ids[0].can_image_1024_be_zoomed)
+
+ # jpeg encoding is changing the color a bit
+ jpeg_blue = (65, 105, 227)
+ jpeg_red = (205, 93, 92)
+ jpeg_green = (34, 139, 34)
+
+ # Verify original size: keep original
+ image = Image.open(io.BytesIO(base64.b64decode(template.image_1920)))
+ self.assertEqual(image.size, (1920, 1080))
+ self.assertEqual(image.getpixel((image.size[0] / 2, image.size[1] / 2)), jpeg_blue, "blue")
+ image = Image.open(io.BytesIO(base64.b64decode(product_red.image_1920)))
+ self.assertEqual(image.size, (800, 500))
+ self.assertEqual(image.getpixel((image.size[0] / 2, image.size[1] / 2)), jpeg_red, "red")
+ image = Image.open(io.BytesIO(base64.b64decode(product_green.image_1920)))
+ self.assertEqual(image.size, (1920, 1080))
+ self.assertEqual(image.getpixel((image.size[0] / 2, image.size[1] / 2)), jpeg_green, "green")
+
+ # Verify 1024 size: keep aspect ratio
+ image = Image.open(io.BytesIO(base64.b64decode(template.image_1024)))
+ self.assertEqual(image.size, (1024, 576))
+ self.assertEqual(image.getpixel((image.size[0] / 2, image.size[1] / 2)), jpeg_blue, "blue")
+ image = Image.open(io.BytesIO(base64.b64decode(product_red.image_1024)))
+ self.assertEqual(image.size, (800, 500))
+ self.assertEqual(image.getpixel((image.size[0] / 2, image.size[1] / 2)), jpeg_red, "red")
+ image = Image.open(io.BytesIO(base64.b64decode(product_green.image_1024)))
+ self.assertEqual(image.size, (1024, 576))
+ self.assertEqual(image.getpixel((image.size[0] / 2, image.size[1] / 2)), jpeg_green, "green")
+
+ # Verify 512 size: keep aspect ratio
+ image = Image.open(io.BytesIO(base64.b64decode(template.image_512)))
+ self.assertEqual(image.size, (512, 288))
+ self.assertEqual(image.getpixel((image.size[0] / 2, image.size[1] / 2)), jpeg_blue, "blue")
+ image = Image.open(io.BytesIO(base64.b64decode(product_red.image_512)))
+ self.assertEqual(image.size, (512, 320))
+ self.assertEqual(image.getpixel((image.size[0] / 2, image.size[1] / 2)), jpeg_red, "red")
+ image = Image.open(io.BytesIO(base64.b64decode(product_green.image_512)))
+ self.assertEqual(image.size, (512, 288))
+ self.assertEqual(image.getpixel((image.size[0] / 2, image.size[1] / 2)), jpeg_green, "green")
+
+ # Verify 256 size: keep aspect ratio
+ image = Image.open(io.BytesIO(base64.b64decode(template.image_256)))
+ self.assertEqual(image.size, (256, 144))
+ self.assertEqual(image.getpixel((image.size[0] / 2, image.size[1] / 2)), jpeg_blue, "blue")
+ image = Image.open(io.BytesIO(base64.b64decode(product_red.image_256)))
+ self.assertEqual(image.size, (256, 160))
+ self.assertEqual(image.getpixel((image.size[0] / 2, image.size[1] / 2)), jpeg_red, "red")
+ image = Image.open(io.BytesIO(base64.b64decode(product_green.image_256)))
+ self.assertEqual(image.size, (256, 144))
+ self.assertEqual(image.getpixel((image.size[0] / 2, image.size[1] / 2)), jpeg_green, "green")
+
+ # Verify 128 size: keep aspect ratio
+ image = Image.open(io.BytesIO(base64.b64decode(template.image_128)))
+ self.assertEqual(image.size, (128, 72))
+ self.assertEqual(image.getpixel((image.size[0] / 2, image.size[1] / 2)), jpeg_blue, "blue")
+ image = Image.open(io.BytesIO(base64.b64decode(product_red.image_128)))
+ self.assertEqual(image.size, (128, 80))
+ self.assertEqual(image.getpixel((image.size[0] / 2, image.size[1] / 2)), jpeg_red, "red")
+ image = Image.open(io.BytesIO(base64.b64decode(product_green.image_128)))
+ self.assertEqual(image.size, (128, 72))
+ self.assertEqual(image.getpixel((image.size[0] / 2, image.size[1] / 2)), jpeg_green, "green")
+
+ # self.env.cr.commit() # uncomment to save the product to test in browser
+
+ self.start_tour("/", 'shop_zoom', login="admin")
+
+ # CASE: unlink move image to fallback if fallback image empty
+ template.image_1920 = False
+ product_red.unlink()
+ self.assertEqual(template.image_1920, red_image)
+
+ # CASE: unlink does nothing special if fallback image already set
+ self.env['product.product'].create({
+ 'product_tmpl_id': template.id,
+ 'image_1920': green_image,
+ }).unlink()
+ self.assertEqual(template.image_1920, red_image)
+
+ # CASE: display variant image first if set
+ self.assertEqual(product_green._get_images()[0].image_1920, green_image)
+
+ # CASE: display variant fallback after variant o2m, correct fallback
+ # write on the variant field, otherwise it will write on the fallback
+ product_green.image_variant_1920 = False
+ images = product_green._get_images()
+ # images on fields are resized to max 1920
+ image = Image.open(io.BytesIO(base64.b64decode(images[0].image_1920)))
+ self.assertEqual(image.size, (1268, 1920))
+ self.assertEqual(images[1].image_1920, red_image)
+ self.assertEqual(images[2].image_1920, image_gif)
+ self.assertEqual(images[3].image_1920, image_svg)
+
+ # CASE: When uploading a product variant image
+ # we don't want the default_product_tmpl_id from the context to be applied if we have a product_variant_id set
+ # we want the default_product_tmpl_id from the context to be applied if we don't have a product_variant_id set
+
+ additionnal_context = {'default_product_tmpl_id': template.id}
+
+ product = self.env['product.product'].create({
+ 'product_tmpl_id': template.id,
+ })
+
+ product_image = self.env['product.image'].with_context(**additionnal_context).create([{
+ 'name': 'Template image',
+ 'image_1920': red_image,
+ }, {
+ 'name': 'Variant image',
+ 'image_1920': blue_image,
+ 'product_variant_id': product.id,
+ }])
+
+ template_image = product_image.filtered(lambda i: i.name == 'Template image')
+ variant_image = product_image.filtered(lambda i: i.name == 'Variant image')
+
+ self.assertEqual(template_image.product_tmpl_id.id, template.id)
+ self.assertFalse(template_image.product_variant_id.id)
+ self.assertFalse(variant_image.product_tmpl_id.id)
+ self.assertEqual(variant_image.product_variant_id.id, product.id)
+
+ def test_02_image_holder(self):
+ f = io.BytesIO()
+ Image.new('RGB', (800, 500), '#FF0000').save(f, 'JPEG')
+ f.seek(0)
+ image = base64.b64encode(f.read())
+
+ # create the color attribute
+ product_attribute = self.env['product.attribute'].create({
+ 'name': 'Beautiful Color',
+ 'display_type': 'color',
+ })
+
+ # create the color attribute values
+ attr_values = self.env['product.attribute.value'].create([{
+ 'name': 'Red',
+ 'attribute_id': product_attribute.id,
+ 'sequence': 1,
+ }, {
+ 'name': 'Green',
+ 'attribute_id': product_attribute.id,
+ 'sequence': 2,
+ }, {
+ 'name': 'Blue',
+ 'attribute_id': product_attribute.id,
+ 'sequence': 3,
+ }])
+
+ # create the template, without creating the variants
+ template = self.env['product.template'].with_context(create_product_product=True).create({
+ 'name': 'Test subject',
+ })
+
+ # when there are no variants, the image must be obtained from the template
+ self.assertEqual(template, template._get_image_holder())
+
+ # set the color attribute and values on the template
+ line = self.env['product.template.attribute.line'].create([{
+ 'attribute_id': product_attribute.id,
+ 'product_tmpl_id': template.id,
+ 'value_ids': [(6, 0, attr_values.ids)]
+ }])
+ value_red = line.product_template_value_ids[0]
+ product_red = template._get_variant_for_combination(value_red)
+ product_red.image_variant_1920 = image
+
+ value_green = line.product_template_value_ids[1]
+ product_green = template._get_variant_for_combination(value_green)
+ product_green.image_variant_1920 = image
+
+ # when there are no template image but there are variants, the image must be obtained from the first variant
+ self.assertEqual(product_red, template._get_image_holder())
+
+ product_red.toggle_active()
+
+ # but when some variants are not available, the image must be obtained from the first available variant
+ self.assertEqual(product_green, template._get_image_holder())
+
+ template.image_1920 = image
+
+ # when there is a template image, the image must be obtained from the template
+ self.assertEqual(template, template._get_image_holder())