From 539fb375e18fc49ad2d53cbb612604dd8aeb6e73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Theys?= Date: Mon, 18 Feb 2019 09:30:42 +0000 Subject: [PATCH 1/2] [MOV] website_sale: rename zoom test to image test --- addons/website_sale/tests/__init__.py | 2 +- .../{test_website_sale_zoom.py => test_website_sale_image.py} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename addons/website_sale/tests/{test_website_sale_zoom.py => test_website_sale_image.py} (100%) diff --git a/addons/website_sale/tests/__init__.py b/addons/website_sale/tests/__init__.py index 89fc4f46d6f..483dd984704 100644 --- a/addons/website_sale/tests/__init__.py +++ b/addons/website_sale/tests/__init__.py @@ -2,4 +2,4 @@ from . import test_customize from . import test_sale_process from . import test_website_sale_pricelist from . import test_website_sale_product_attribute_value_config -from . import test_website_sale_zoom +from . import test_website_sale_image diff --git a/addons/website_sale/tests/test_website_sale_zoom.py b/addons/website_sale/tests/test_website_sale_image.py similarity index 100% rename from addons/website_sale/tests/test_website_sale_zoom.py rename to addons/website_sale/tests/test_website_sale_image.py From 13026546463788852b79d23fde488308023f20a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Theys?= Date: Mon, 18 Feb 2019 09:43:06 +0000 Subject: [PATCH 2/2] [IMP] base,web,website_sale: add tests for product image and mixin This commit will add tests for the PR #30656 which added the following commit: 7daa85dd12ff522be510bc2f8db1d968cae7d891 The tests in the current commit achieve 100% code coverage for: - the `image.mixin` - the overridden `image.mixin` methods in `product.product` - the `_get_images` methods of ecommerce - the size logic in method `content_image` (route `/web/image`) PR: #31181 --- addons/web/tests/test_image.py | 25 +++ .../tests/test_website_sale_image.py | 143 +++++++++++++++--- odoo/addons/base/models/image_mixin.py | 4 + 3 files changed, 151 insertions(+), 21 deletions(-) diff --git a/addons/web/tests/test_image.py b/addons/web/tests/test_image.py index 96e09444c05..e17073d0fb7 100644 --- a/addons/web/tests/test_image.py +++ b/addons/web/tests/test_image.py @@ -10,10 +10,35 @@ from odoo.tests.common import HttpCase class TestImage(HttpCase): def test_01_content_image_resize_placeholder(self): + """The goal of this test is to make sure the placeholder image is + resized appropriately depending on the given URL parameters.""" + + # CASE: resize placeholder to given size response = self.url_open('/web/image/0/200x150') image = Image.open(io.BytesIO(response.content)) self.assertEqual(image.size, (200, 150)) + # CASE: resize placeholder to small response = self.url_open('/web/image/fake/0/image_small') image = Image.open(io.BytesIO(response.content)) self.assertEqual(image.size, (64, 64)) + + # CASE: resize placeholder to medium + response = self.url_open('/web/image/fake/0/image_medium') + image = Image.open(io.BytesIO(response.content)) + self.assertEqual(image.size, (128, 128)) + + # CASE: resize placeholder to large + response = self.url_open('/web/image/fake/0/image_large') + image = Image.open(io.BytesIO(response.content)) + self.assertEqual(image.size, (256, 256)) + + # CASE: resize placeholder to big (but placeholder image is too small) + response = self.url_open('/web/image/fake/0/image') + image = Image.open(io.BytesIO(response.content)) + self.assertEqual(image.size, (256, 256)) + + # CASE: no size found, use placeholder original size + response = self.url_open('/web/image/fake/0/image_no_size') + image = Image.open(io.BytesIO(response.content)) + self.assertEqual(image.size, (256, 256)) diff --git a/addons/website_sale/tests/test_website_sale_image.py b/addons/website_sale/tests/test_website_sale_image.py index d12a905af6d..bf47ecbfb73 100644 --- a/addons/website_sale/tests/test_website_sale_image.py +++ b/addons/website_sale/tests/test_website_sale_image.py @@ -9,7 +9,7 @@ import odoo.tests @odoo.tests.common.tagged('post_install', '-at_install') -class TestWebsiteSaleZoom(odoo.tests.HttpCase): +class TestWebsiteSaleImage(odoo.tests.HttpCase): # registry_test_mode = False # uncomment to save the product to test in browser @@ -65,34 +65,42 @@ class TestWebsiteSaleZoom(odoo.tests.HttpCase): f.seek(0) green_image = base64.b64encode(f.read()) - # create the first product and the template. - # Set image to blue (will be set on the template because it was empty) - product_red = self.env['product.product'].create({ - 'name': 'A Colorful Image', - 'image': blue_image, - 'attribute_value_ids': [(6, 0, attr_values.filtered(lambda l: l.name == name_red).ids)], - }) + # Template Extra Image 1 + f = io.BytesIO() + Image.new('RGB', (124, 147)).save(f, 'GIF') + f.seek(0) + image_gif = base64.b64encode(f.read()) - # now set the red image on the first variant - # that works because template image is not empty anymore - product_red.image = red_image + # Template Extra Image 2 + image_svg = base64.b64encode(b'') + + # 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, 'JPEG') + f.seek(0) + image_jpeg = 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': image_gif}), (0, 0, {'name': 'image 4', 'image': image_svg})], + }) # set the color attribute and values on the template self.env['product.template.attribute.line'].create([{ 'attribute_id': product_attribute.id, - 'product_tmpl_id': product_red.product_tmpl_id.id, + 'product_tmpl_id': template.id, 'value_ids': [(6, 0, attr_values.ids)] }]) - # create the green variant - self.env['product.product'].create({ - 'image': green_image, - 'product_tmpl_id': product_red.product_tmpl_id.id, - 'attribute_value_ids': [(6, 0, attr_values.filtered(lambda l: l.name == name_green).ids)], - }) - # set a different price on the variants to differentiate them - product_template_attribute_values = self.env['product.template.attribute.value'].search([('product_tmpl_id', '=', product_red.product_tmpl_id.id)]) + 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: @@ -100,6 +108,99 @@ class TestWebsiteSaleZoom(odoo.tests.HttpCase): else: val.price_extra = 20 + # Create 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 = self.env['product.product'].create({ + 'product_tmpl_id': template.id, + 'image': blue_image, + 'attribute_value_ids': [(6, 0, attr_values.filtered(lambda l: l.name == name_red).ids)], + 'product_variant_image_ids': [(0, 0, {'name': 'image 2', 'image': image_bmp})], + }) + + self.assertEqual(template.image_original, blue_image) + + # create the green variant + product_green = self.env['product.product'].create({ + 'image': green_image, + 'product_tmpl_id': template.id, + 'attribute_value_ids': [(6, 0, attr_values.filtered(lambda l: l.name == name_green).ids)], + 'product_variant_image_ids': [(0, 0, {'name': 'image 3', 'image': image_jpeg})], + }) + + # 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 = red_image + + # Verify image_original size > 1024 can be zoomed + self.assertTrue(template.can_image_be_zoomed) + self.assertFalse(template.product_template_image_ids[0].can_image_be_zoomed) + self.assertFalse(template.product_template_image_ids[1].can_image_be_zoomed) + self.assertFalse(product_red.can_image_be_zoomed) + self.assertFalse(product_red.product_variant_image_ids[0].can_image_be_zoomed) + self.assertTrue(product_green.can_image_be_zoomed) + self.assertTrue(product_green.product_variant_image_ids[0].can_image_be_zoomed) + + # Verify original size: keep original + image = Image.open(io.BytesIO(base64.b64decode(template.image_original))) + self.assertEqual(image.size, (1920, 1080)) + image = Image.open(io.BytesIO(base64.b64decode(product_red.image_original))) + self.assertEqual(image.size, (800, 500)) + image = Image.open(io.BytesIO(base64.b64decode(product_green.image_original))) + self.assertEqual(image.size, (1920, 1080)) + + # Verify big size: keep aspect ratio + image = Image.open(io.BytesIO(base64.b64decode(template.image))) + self.assertEqual(image.size, (1024, 576)) + image = Image.open(io.BytesIO(base64.b64decode(product_red.image))) + self.assertEqual(image.size, (800, 500)) + image = Image.open(io.BytesIO(base64.b64decode(product_green.image))) + self.assertEqual(image.size, (1024, 576)) + + # Verify large size + image = Image.open(io.BytesIO(base64.b64decode(template.image_large))) + self.assertEqual(image.size, (256, 256)) + image = Image.open(io.BytesIO(base64.b64decode(product_red.image_large))) + self.assertEqual(image.size, (256, 256)) + image = Image.open(io.BytesIO(base64.b64decode(product_green.image_large))) + self.assertEqual(image.size, (256, 256)) + + # Verify medium size + image = Image.open(io.BytesIO(base64.b64decode(template.image_medium))) + self.assertEqual(image.size, (128, 128)) + image = Image.open(io.BytesIO(base64.b64decode(product_red.image_medium))) + self.assertEqual(image.size, (128, 128)) + image = Image.open(io.BytesIO(base64.b64decode(product_green.image_medium))) + self.assertEqual(image.size, (128, 128)) + + # Verify small size + image = Image.open(io.BytesIO(base64.b64decode(template.image_small))) + self.assertEqual(image.size, (64, 64)) + image = Image.open(io.BytesIO(base64.b64decode(product_red.image_small))) + self.assertEqual(image.size, (64, 64)) + image = Image.open(io.BytesIO(base64.b64decode(product_green.image_small))) + self.assertEqual(image.size, (64, 64)) + # self.env.cr.commit() # uncomment to save the product to test in browser - self.phantom_js("/", "odoo.__DEBUG__.services['web_tour.tour'].run('shop_zoom')", "odoo.__DEBUG__.services['web_tour.tour'].tours.shop_zoom.ready", login="admin") + self.browser_js("/", "odoo.__DEBUG__.services['web_tour.tour'].run('shop_zoom')", "odoo.__DEBUG__.services['web_tour.tour'].tours.shop_zoom.ready", login="admin") + + # CASE: unlink move image to fallback if fallback image empty + template.image = False + product_red.unlink() + self.assertEqual(template.image_original, red_image) + + # CASE: unlink does nothing special if fallback image already set + product_green.copy().unlink() + self.assertEqual(template.image_original, red_image) + + # CASE: display variant image first if set + self.assertEqual(product_green._get_images()[0].image_original, green_image) + + # CASE: display variant fallback after variant o2m, correct fallback + # write on the raw field, otherwise it will write on the fallback here + product_green.image_raw_original = False + images = product_green._get_images() + self.assertEqual(images[0].image_original, image_jpeg) + self.assertEqual(images[1].image_original, red_image) + self.assertEqual(images[2].image_original, image_gif) + self.assertEqual(images[3].image_original, image_svg) diff --git a/odoo/addons/base/models/image_mixin.py b/odoo/addons/base/models/image_mixin.py index dc65f31d1ae..1e55cea38ed 100644 --- a/odoo/addons/base/models/image_mixin.py +++ b/odoo/addons/base/models/image_mixin.py @@ -73,6 +73,10 @@ class ImageMixin(models.AbstractModel): def _set_image(self): for record in self: record.image_original = record.image + # We want the image field to be recomputed to have a correct size. + # Without this `invalidate_cache`, the image field will keep holding the + # image_original instead of the big-sized image. + self.invalidate_cache() @api.multi def _compute_image_large(self):