Steps to reproduce the bug: - Add an Image Gallery (IG) snippet on the page. - Click on "Remove all" to remove all the images of the IG snippet. - Add 2 new images in the IG. - Click on the first image of the IG to load its data. - Click on the trash button to remove the snippet. - Bug => The snippet is not removed (an image is removed instead). When a snippet is removed, the `removeSnippet` function is called. The problem is that the `call_for_each_child_snippet` will never resolve. Two mechanisms are of interest to understand why: the first one is the `updateCurrentSnippetEditorOverlay` function. Its goal is to destroy a snippet each time its target is not in the DOM anymore. The second mechanism is specific to the IG snippet: when an image of this snippet is destroyed, the `slideshow` function goes through the remaining images to update parameters. To do it, the function uses the `_replaceContent` function that empties the content of the carousel and then fills it with new data. When a snippet is removed, a `SnippetEditor` is created for each element of it. In the case of the IG, a `SnippetEditor` is created for each image of the the snippet. Because the first image already has a `SnippetEditor` (because it has been clicked), the callback of `call_for_each_child_snippet` is called to remove this image from the IG snippet. The second mechanism explained before will then be called. Meanwhile, a `SnippetEditor` will be created for the second image. However, because the `_replaceContent` function emptied the content of the carousel, the `updateCurrentSnippetEditorOverlay` function will destroy the `SnippetEditor` of the second image as its target is not considered present in the DOM anymore. Unfortunately, the `call_for_each_child_snippet` still needed this `SnippetEditor` and will never entirely resolve. To solve this problem, the `removeSnippet` function is executed inside a mutex. Because the mutex is also used by `updateCurrentSnippetEditorOverlay`, we are sure that this function will not destroy the snippetEditor while the `removeSnippet` is still running. task-3147271 closes odoo/odoo#116352 X-original-commit: 1c99ab2bc04f65999098f70d062d24ed1ac4a9c3 Signed-off-by: Arthur Detroux (ard) <ard@odoo.com> Signed-off-by: loco-odoo <loco@odoo.com>
74 lines
3.3 KiB
Python
74 lines
3.3 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from lxml import html
|
|
from werkzeug.urls import url_encode
|
|
|
|
from odoo.tests import HttpCase, tagged
|
|
from odoo.addons.website.tools import MockRequest
|
|
from odoo.tests.common import HOST
|
|
from odoo.tools import config
|
|
|
|
|
|
@tagged('post_install', '-at_install', 'website_snippets')
|
|
class TestSnippets(HttpCase):
|
|
|
|
def test_01_empty_parents_autoremove(self):
|
|
self.start_tour(self.env['website'].get_client_action_url('/'), 'snippet_empty_parent_autoremove', login='admin')
|
|
|
|
def test_02_default_shape_gets_palette_colors(self):
|
|
self.start_tour('/@/?enable_editor=1', "default_shape_gets_palette_colors", login='admin')
|
|
|
|
def test_03_snippets_all_drag_and_drop(self):
|
|
with MockRequest(self.env, website=self.env['website'].browse(1)):
|
|
snippets_template = self.env['ir.ui.view'].render_public_asset('website.snippets')
|
|
html_template = html.fromstring(snippets_template)
|
|
data_snippet_els = html_template.xpath("//*[@class='o_panel' and not(contains(@class, 'd-none'))]//*[@data-snippet]")
|
|
blacklist = [
|
|
's_facebook_page', # avoid call to external services (facebook.com)
|
|
's_map', # avoid call to maps.google.com
|
|
]
|
|
snippets_names = ','.join(set(el.attrib['data-snippet'] for el in data_snippet_els if el.attrib['data-snippet'] not in blacklist))
|
|
snippets_names_encoded = url_encode({'snippets_names': snippets_names})
|
|
path = url_encode({
|
|
'path': '/?' + snippets_names_encoded
|
|
})
|
|
self.start_tour("/web#action=website.website_preview&%s" % path, "snippets_all_drag_and_drop", login='admin', timeout=300)
|
|
|
|
def test_04_countdown_preview(self):
|
|
self.start_tour(self.env['website'].get_client_action_url('/'), 'snippet_countdown', login='admin')
|
|
|
|
def test_05_social_media(self):
|
|
self.start_tour(self.env['website'].get_client_action_url('/'), 'snippet_social_media', login="admin")
|
|
self.assertEqual(
|
|
self.env['website'].browse(1).social_instagram,
|
|
'https://instagram.com/odoo.official/',
|
|
'Social media should have been updated'
|
|
)
|
|
|
|
def test_06_snippet_popup_add_remove(self):
|
|
self.start_tour(self.env['website'].get_client_action_url('/'), 'snippet_popup_add_remove', login='admin')
|
|
|
|
def test_07_image_gallery(self):
|
|
self.start_tour(self.env['website'].get_client_action_url('/'), 'snippet_image_gallery', login='admin')
|
|
|
|
def test_08_table_of_content(self):
|
|
self.start_tour(self.env['website'].get_client_action_url('/'), 'snippet_table_of_content', login='admin')
|
|
|
|
def test_09_snippet_image_gallery(self):
|
|
IrAttachment = self.env['ir.attachment']
|
|
base = "http://%s:%s" % (HOST, config['http_port'])
|
|
IrAttachment.create({
|
|
'public': True,
|
|
'name': 's_default_image.jpg',
|
|
'type': 'url',
|
|
'url': base + '/web/image/website.s_banner_default_image.jpg',
|
|
})
|
|
IrAttachment.create({
|
|
'public': True,
|
|
'name': 's_default_image2.jpg',
|
|
'type': 'url',
|
|
'url': base + '/web/image/website.s_banner_default_image.jpg',
|
|
})
|
|
self.start_tour("/", "snippet_image_gallery_remove", login='admin')
|