mirror of
https://github.com/bringout/oca-ocb-test.git
synced 2026-04-20 16:02:07 +02:00
Initial commit: Test packages
This commit is contained in:
commit
080accd21c
338 changed files with 32413 additions and 0 deletions
|
|
@ -0,0 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import test_controller_args
|
||||
from . import test_custom_snippet
|
||||
from . import test_error
|
||||
from . import test_fuzzy
|
||||
from . import test_image_upload_progress
|
||||
from . import test_is_multilang
|
||||
from . import test_media
|
||||
from . import test_menu
|
||||
from . import test_multi_company
|
||||
from . import test_page_manager
|
||||
from . import test_page
|
||||
from . import test_performance
|
||||
from . import test_redirect
|
||||
from . import test_reset_views
|
||||
from . import test_session
|
||||
from . import test_settings
|
||||
from . import test_systray
|
||||
from . import test_views_during_module_operation
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
import odoo.tests
|
||||
from odoo.tools import mute_logger
|
||||
|
||||
|
||||
@odoo.tests.common.tagged('post_install', '-at_install')
|
||||
class TestWebsiteControllerArgs(odoo.tests.HttpCase):
|
||||
|
||||
@mute_logger('odoo.http')
|
||||
def test_crawl_args(self):
|
||||
req = self.url_open('/ignore_args/converter/valueA/?b=valueB&c=valueC')
|
||||
self.assertEqual(req.status_code, 200)
|
||||
self.assertEqual(req.json(), {'a': 'valueA', 'b': 'valueB', 'kw': {'c': 'valueC'}})
|
||||
|
||||
req = self.url_open('/ignore_args/converter/valueA/nokw?b=valueB&c=valueC')
|
||||
self.assertEqual(req.status_code, 200)
|
||||
self.assertEqual(req.json(), {'a': 'valueA', 'b': 'valueB'})
|
||||
|
||||
req = self.url_open('/ignore_args/converteronly/valueA/?b=valueB&c=valueC')
|
||||
self.assertEqual(req.status_code, 200)
|
||||
self.assertEqual(req.json(), {'a': 'valueA', 'kw': None})
|
||||
|
||||
req = self.url_open('/ignore_args/none?a=valueA&b=valueB')
|
||||
self.assertEqual(req.status_code, 200)
|
||||
self.assertEqual(req.json(), {'a': None, 'kw': None})
|
||||
|
||||
req = self.url_open('/ignore_args/a?a=valueA&b=valueB')
|
||||
self.assertEqual(req.status_code, 200)
|
||||
self.assertEqual(req.json(), {'a': 'valueA', 'kw': None})
|
||||
|
||||
req = self.url_open('/ignore_args/kw?a=valueA&b=valueB')
|
||||
self.assertEqual(req.status_code, 200)
|
||||
self.assertEqual(req.json(), {'a': 'valueA', 'kw': {'b': 'valueB'}})
|
||||
|
||||
req = self.url_open('/test_website/country/whatever-999999')
|
||||
self.assertEqual(req.status_code, 404,
|
||||
"Model converter record does not exist, return a 404.")
|
||||
|
||||
|
||||
@odoo.tests.common.tagged('post_install', '-at_install')
|
||||
class TestWebsiteControllers(odoo.tests.TransactionCase):
|
||||
|
||||
def test_01_sitemap(self):
|
||||
website = self.env['website'].browse(1)
|
||||
locs = website.with_user(website.user_id)._enumerate_pages(query_string='test_website_sitemap')
|
||||
self.assertEqual(len(list(locs)), 1, "The same URL should only be shown once")
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import odoo.tests
|
||||
from odoo.tools import mute_logger
|
||||
|
||||
|
||||
@odoo.tests.common.tagged('post_install', '-at_install')
|
||||
class TestCustomSnippet(odoo.tests.HttpCase):
|
||||
|
||||
@mute_logger('odoo.addons.http_routing.models.ir_http', 'odoo.http')
|
||||
def test_01_run_tour(self):
|
||||
self.start_tour(self.env['website'].get_client_action_url('/'), 'test_custom_snippet', login="admin")
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import odoo.tests
|
||||
from odoo.tools import mute_logger
|
||||
|
||||
|
||||
@odoo.tests.common.tagged('post_install', '-at_install')
|
||||
class TestWebsiteError(odoo.tests.HttpCase):
|
||||
|
||||
@mute_logger('odoo.addons.http_routing.models.ir_http', 'odoo.http')
|
||||
def test_01_run_test(self):
|
||||
self.start_tour("/test_error_view", 'test_error_website')
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import logging
|
||||
import psycopg2
|
||||
|
||||
from odoo.addons.website.controllers.main import Website
|
||||
from odoo.addons.website.tools import MockRequest
|
||||
import odoo.tests
|
||||
from odoo.tests.common import TransactionCase
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@odoo.tests.tagged('-at_install', 'post_install')
|
||||
class TestAutoComplete(TransactionCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.website = cls.env['website'].browse(1)
|
||||
cls.WebsiteController = Website()
|
||||
|
||||
def _autocomplete(self, term, expected_count, expected_fuzzy_term, search_type="test", options=None):
|
||||
""" Calls the autocomplete for a given term and performs general checks """
|
||||
with MockRequest(self.env, website=self.website):
|
||||
suggestions = self.WebsiteController.autocomplete(
|
||||
search_type=search_type, term=term, max_nb_chars=50, options=options or {},
|
||||
)
|
||||
self.assertEqual(expected_count, suggestions['results_count'], "Wrong number of suggestions")
|
||||
self.assertEqual(expected_fuzzy_term, suggestions.get('fuzzy_search', 'Not found'), "Wrong fuzzy match")
|
||||
|
||||
def _autocomplete_page(self, term, expected_count, expected_fuzzy_term):
|
||||
self._autocomplete(term, expected_count, expected_fuzzy_term, search_type="pages", options={
|
||||
'displayDescription': False, 'displayDetail': False,
|
||||
'displayExtraDetail': False, 'displayExtraLink': False,
|
||||
'displayImage': False, 'allowFuzzy': True
|
||||
})
|
||||
|
||||
def test_01_many_records(self):
|
||||
# REF1000~REF3999
|
||||
data = [{
|
||||
'name': 'REF%s' % count,
|
||||
'is_published': True,
|
||||
} for count in range(1000, 4000)]
|
||||
self.env['test.model'].create(data)
|
||||
# NUM1000~NUM1998
|
||||
data = [{
|
||||
'name': 'NUM%s' % count,
|
||||
'is_published': True,
|
||||
} for count in range(1000, 1999)]
|
||||
self.env['test.model'].create(data)
|
||||
# There are more than 1000 "R*" records
|
||||
# => Find exact match through the fallback
|
||||
self._autocomplete('REF3000', 1, False)
|
||||
# => No exact match => Find fuzzy within first 1000 (distance=3: replace D by F, move 3, add 1)
|
||||
self._autocomplete('RED3000', 1, 'ref3000' if self.env.registry.has_trigram else 'ref1003')
|
||||
# => Find exact match through the fallback
|
||||
self._autocomplete('REF300', 10, False)
|
||||
# => Find exact match through the fallback
|
||||
self._autocomplete('REF1', 1000, False)
|
||||
# => No exact match => Nothing close enough (min distance=5)
|
||||
self._autocomplete('REFX', 0, "Not found")
|
||||
# => Find exact match through the fallback - unfortunate because already in the first 1000 records
|
||||
self._autocomplete('REF1230', 1, False)
|
||||
# => Find exact match through the fallback
|
||||
self._autocomplete('REF2230', 1, False)
|
||||
|
||||
# There are less than 1000 "N*" records
|
||||
# => Fuzzy within N* (distance=1: add 1)
|
||||
self._autocomplete('NUM000', 1, "num1000")
|
||||
# => Exact match (distance=0 shortcut logic)
|
||||
self._autocomplete('NUM100', 10, False)
|
||||
# => Exact match (distance=0 shortcut logic)
|
||||
self._autocomplete('NUM199', 9, False)
|
||||
# => Exact match (distance=0 shortcut logic)
|
||||
self._autocomplete('NUM1998', 1, False)
|
||||
# => Fuzzy within N* (distance=1: replace 1 by 9)
|
||||
self._autocomplete('NUM1999', 1, 'num1199')
|
||||
# => Fuzzy within N* (distance=1: add 1)
|
||||
self._autocomplete('NUM200', 1, 'num1200')
|
||||
|
||||
# There are no "X*" records
|
||||
self._autocomplete('XEF1000', 0, "Not found")
|
||||
|
||||
def test_02_pages_search(self):
|
||||
if not self.env.registry.has_trigram:
|
||||
try:
|
||||
self.env.cr.execute("CREATE EXTENSION IF NOT EXISTS pg_trgm")
|
||||
self.env.registry.has_trigram = True
|
||||
except psycopg2.Error:
|
||||
_logger.warning("pg_trgm extension can't be installed, which is required to run this test")
|
||||
return
|
||||
|
||||
with MockRequest(self.env, website=self.env['website'].browse(1)):
|
||||
# This should not crash. This ensures that when searching on `name`
|
||||
# field of `website.page` model, it works properly when `pg_trgm` is
|
||||
# activated.
|
||||
# Indeed, `name` is a field of `website.page` record but only at the
|
||||
# ORM level, not in SQL, due to how `inherits` works.
|
||||
self.env['website'].browse(1)._search_with_fuzzy(
|
||||
'pages', 'test', limit=5, order='name asc, website_id desc, id', options={
|
||||
'displayDescription': False, 'displayDetail': False,
|
||||
'displayExtraDetail': False, 'displayExtraLink': False,
|
||||
'displayImage': False, 'allowFuzzy': True
|
||||
}
|
||||
)
|
||||
|
||||
test_page = self.env.ref('test_website.test_page')
|
||||
test_page.name = 'testTotallyUnique'
|
||||
|
||||
# Editor and Designer see pages in result
|
||||
self._autocomplete_page('testTotallyUnique', 1, False)
|
||||
|
||||
test_page.visibility = 'connected'
|
||||
self._autocomplete_page('testTotallyUnique', 1, False)
|
||||
test_page.visibility = False
|
||||
|
||||
test_page.groups_id = self.env.ref('base.group_public')
|
||||
self._autocomplete_page('testTotallyUnique', 1, False)
|
||||
test_page.groups_id = False
|
||||
|
||||
# Public user don't see restricted page
|
||||
saved_env = self.env
|
||||
self.website.env = self.env = self.env(user=self.website.user_id)
|
||||
self._autocomplete_page('testTotallyUnique', 0, "Not found")
|
||||
|
||||
test_page.website_indexed = True
|
||||
self._autocomplete_page('testTotallyUnique', 1, False)
|
||||
|
||||
test_page.groups_id = self.env.ref('base.group_system')
|
||||
self._autocomplete_page('testTotallyUnique', 0, "Not found")
|
||||
|
||||
test_page.groups_id = self.env.ref('base.group_public')
|
||||
self._autocomplete_page('testTotallyUnique', 1, False)
|
||||
test_page.groups_id = False
|
||||
|
||||
test_page.visibility = 'password'
|
||||
self._autocomplete_page('testTotallyUnique', 0, "Not found")
|
||||
|
||||
test_page.visibility = 'connected'
|
||||
self._autocomplete_page('testTotallyUnique', 0, "Not found")
|
||||
|
||||
# restore website env for next tests
|
||||
self.website.env = self.env = saved_env
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.addons.web_editor.controllers.main import Web_Editor
|
||||
from odoo.addons.web_unsplash.controllers.main import Web_Unsplash
|
||||
|
||||
import odoo.tests
|
||||
|
||||
from odoo import http
|
||||
|
||||
|
||||
@odoo.tests.common.tagged('post_install', '-at_install')
|
||||
class TestImageUploadProgress(odoo.tests.HttpCase):
|
||||
|
||||
def test_01_image_upload_progress(self):
|
||||
self.start_tour(self.env['website'].get_client_action_url('/test_image_progress'), 'test_image_upload_progress', login="admin")
|
||||
|
||||
def test_02_image_upload_progress_unsplash(self):
|
||||
BASE_URL = self.base_url()
|
||||
|
||||
@http.route('/web_editor/media_library_search', type='json', auth="user", website=True)
|
||||
def media_library_search(self, **params):
|
||||
return {"results": 0, "media": []}
|
||||
# because not preprocessed by ControllerType metaclass
|
||||
media_library_search.original_endpoint.routing_type = 'json'
|
||||
# disable undraw, no third party should be called in tests
|
||||
self.patch(Web_Editor, 'media_library_search', media_library_search)
|
||||
|
||||
@http.route("/web_unsplash/fetch_images", type='json', auth="user")
|
||||
def fetch_unsplash_images(self, **post):
|
||||
return {
|
||||
'total': 1434,
|
||||
'total_pages': 48,
|
||||
'results': [{
|
||||
'id': 'HQqIOc8oYro',
|
||||
'alt_description': 'brown fox sitting on green grass field during daytime',
|
||||
'urls': {
|
||||
# 'regular': 'https://images.unsplash.com/photo-1462953491269-9aff00919695?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwzMDUwOHwwfDF8c2VhcmNofDF8fGZveHxlbnwwfHx8fDE2MzEwMzIzNDE&ixlib=rb-1.2.1&q=80&w=1080',
|
||||
'regular': BASE_URL + '/website/static/src/img/phone.png',
|
||||
},
|
||||
'links': {
|
||||
# 'download_location': 'https://api.unsplash.com/photos/HQqIOc8oYro/download?ixid=MnwzMDUwOHwwfDF8c2VhcmNofDF8fGZveHxlbnwwfHx8fDE2MzEwMzIzNDE'
|
||||
'download_location': BASE_URL + '/website/static/src/img/phone.png',
|
||||
},
|
||||
'user': {
|
||||
'name': 'Mitchell Admin',
|
||||
'links': {
|
||||
'html': BASE_URL,
|
||||
},
|
||||
},
|
||||
}]
|
||||
}
|
||||
# because not preprocessed by ControllerType metaclass
|
||||
fetch_unsplash_images.original_endpoint.routing_type = 'json'
|
||||
# disable undraw, no third party should be called in tests
|
||||
self.patch(Web_Unsplash, 'fetch_unsplash_images', fetch_unsplash_images)
|
||||
|
||||
self.start_tour(self.env['website'].get_client_action_url('/test_image_progress'), 'test_image_upload_progress_unsplash', login="admin")
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from urllib.parse import urlparse
|
||||
import odoo.tests
|
||||
import lxml
|
||||
|
||||
|
||||
@odoo.tests.common.tagged('post_install', '-at_install')
|
||||
class TestIsMultiLang(odoo.tests.HttpCase):
|
||||
|
||||
def test_01_is_multilang_url(self):
|
||||
website = self.env['website'].search([], limit=1)
|
||||
fr = self.env.ref('base.lang_fr').sudo()
|
||||
en = self.env.ref('base.lang_en').sudo()
|
||||
|
||||
fr.active = True
|
||||
fr_prefix = "/" + fr.iso_code
|
||||
|
||||
website.default_lang_id = en
|
||||
website.language_ids = en + fr
|
||||
|
||||
for data in [None, {'post': True}]: # GET / POST
|
||||
body = lxml.html.fromstring(self.url_open('/fr/multi_url', data=data).content)
|
||||
|
||||
self.assertEqual(fr_prefix + '/get', body.find('./a[@id="get"]').get('href'))
|
||||
self.assertEqual(fr_prefix + '/post', body.find('./form[@id="post"]').get('action'))
|
||||
self.assertEqual(fr_prefix + '/get_post', body.find('./a[@id="get_post"]').get('href'))
|
||||
self.assertEqual('/get_post_nomultilang', body.find('./a[@id="get_post_nomultilang"]').get('href'))
|
||||
|
||||
def test_02_url_lang_code_underscore(self):
|
||||
website = self.env['website'].browse(1)
|
||||
it = self.env.ref('base.lang_it').sudo()
|
||||
en = self.env.ref('base.lang_en').sudo()
|
||||
be = self.env.ref('base.lang_fr_BE').sudo()
|
||||
country1 = self.env['res.country'].create({'name': "My Super Country"})
|
||||
|
||||
it.active = True
|
||||
be.active = True
|
||||
website.domain = self.base_url() # for _is_canonical_url
|
||||
website.default_lang_id = en
|
||||
website.language_ids = en + it + be
|
||||
country1.update_field_translations('name', {
|
||||
it.code: country1.name + ' Italia',
|
||||
be.code: country1.name + ' Belgium'
|
||||
})
|
||||
|
||||
r = self.url_open(f'/test_lang_url/{country1.id}')
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(urlparse(r.url).path, f'/test_lang_url/my-super-country-{country1.id}')
|
||||
|
||||
r = self.url_open(f'/{it.url_code}/test_lang_url/{country1.id}')
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(urlparse(r.url).path, f'/{it.url_code}/test_lang_url/my-super-country-italia-{country1.id}')
|
||||
|
||||
body = lxml.html.fromstring(r.content)
|
||||
# Note: this test is indirectly testing the `ref=canonical` tag is correctly set,
|
||||
# as it is required in order for `rel=alternate` tags to be inserted in the DOM
|
||||
it_href = body.find('./head/link[@rel="alternate"][@hreflang="it"]').get('href')
|
||||
fr_href = body.find('./head/link[@rel="alternate"][@hreflang="fr"]').get('href')
|
||||
en_href = body.find('./head/link[@rel="alternate"][@hreflang="en"]').get('href')
|
||||
|
||||
self.assertEqual(urlparse(it_href).path, f'/{it.url_code}/test_lang_url/my-super-country-italia-{country1.id}')
|
||||
self.assertEqual(urlparse(fr_href).path, f'/{be.url_code}/test_lang_url/my-super-country-belgium-{country1.id}')
|
||||
self.assertEqual(urlparse(en_href).path, f'/test_lang_url/my-super-country-{country1.id}')
|
||||
|
||||
def test_03_head_alternate_href(self):
|
||||
website = self.env['website'].search([], limit=1)
|
||||
be = self.env.ref('base.lang_fr_BE').sudo()
|
||||
en = self.env.ref('base.lang_en').sudo()
|
||||
|
||||
be.active = True
|
||||
be_prefix = "/" + be.iso_code
|
||||
|
||||
website.default_lang_id = en
|
||||
website.language_ids = en + be
|
||||
|
||||
# alternate href should be use the current url.
|
||||
self.url_open(be_prefix)
|
||||
self.url_open(be_prefix + '/contactus')
|
||||
r = self.url_open(be_prefix)
|
||||
self.assertRegex(r.text, r'<link rel="alternate" hreflang="en" href="http://[^"]+/"/>')
|
||||
r = self.url_open(be_prefix + '/contactus')
|
||||
self.assertRegex(r.text, r'<link rel="alternate" hreflang="en" href="http://[^"]+/contactus"/>')
|
||||
|
||||
def test_04_multilang_false(self):
|
||||
website = self.env['website'].search([], limit=1)
|
||||
fr = self.env.ref('base.lang_fr').sudo()
|
||||
en = self.env.ref('base.lang_en').sudo()
|
||||
fr.active = True
|
||||
|
||||
website.default_lang_id = en
|
||||
website.language_ids = en + fr
|
||||
self.opener.cookies['frontend_lang'] = fr.iso_code
|
||||
|
||||
res = self.url_open('/get_post_nomultilang', allow_redirects=False)
|
||||
res.raise_for_status()
|
||||
|
||||
self.assertEqual(res.status_code, 200, "Should not be redirected")
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import base64
|
||||
|
||||
import odoo.tests
|
||||
from odoo.tools import mute_logger
|
||||
|
||||
|
||||
@odoo.tests.common.tagged('post_install', '-at_install')
|
||||
class TestMedia(odoo.tests.HttpCase):
|
||||
|
||||
@mute_logger('odoo.addons.http_routing.models.ir_http', 'odoo.http')
|
||||
def test_01_replace_media(self):
|
||||
SVG = base64.b64encode(b'<svg xmlns="http://www.w3.org/2000/svg"></svg>')
|
||||
self.env['ir.attachment'].create({
|
||||
'name': 'sample.svg',
|
||||
'public': True,
|
||||
'mimetype': 'image/svg+xml',
|
||||
'datas': SVG,
|
||||
})
|
||||
self.start_tour("/", 'test_replace_media', login="admin")
|
||||
|
||||
def test_02_image_link(self):
|
||||
self.start_tour("/", 'test_image_link', login="admin")
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from lxml import html
|
||||
|
||||
from odoo.addons.website.tools import MockRequest
|
||||
from odoo.tests import tagged, HttpCase
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestWebsiteMenu(HttpCase):
|
||||
|
||||
def test_menu_active_element(self):
|
||||
records = self.env['test.model'].create([{
|
||||
'name': "Record 1",
|
||||
'is_published': True,
|
||||
}, {
|
||||
'name': "Record 2",
|
||||
'is_published': True,
|
||||
}])
|
||||
|
||||
controller_url = '/test_website/model_item/'
|
||||
website = self.env['website'].browse(1)
|
||||
|
||||
self.env['website.menu'].create([{
|
||||
'name': records[0].name,
|
||||
'url': f"{controller_url}{records[0].id}",
|
||||
'parent_id': website.menu_id.id,
|
||||
'website_id': website.id,
|
||||
'sequence': 10,
|
||||
}, {
|
||||
'name': records[1].name,
|
||||
'url': f"{controller_url}{records[1].id}",
|
||||
'parent_id': website.menu_id.id,
|
||||
'website_id': website.id,
|
||||
'sequence': 20,
|
||||
}])
|
||||
for record in records:
|
||||
record_url = f"{controller_url}{record.id}"
|
||||
with MockRequest(self.env, website=website, url_root='', path=record_url):
|
||||
tree = html.fromstring(self.env['ir.qweb']._render('test_website.model_item', {
|
||||
'record': record,
|
||||
'main_object': record,
|
||||
}))
|
||||
menu_link_el = tree.xpath(".//*[@id='top_menu']//a[@href='%s' and contains(@class, 'active')]" % record_url)
|
||||
self.assertEqual(len(menu_link_el), 1, "The menu link related to the current record should be active")
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.tests.common import HttpCase, tagged
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestMultiCompany(HttpCase):
|
||||
|
||||
def test_company_in_context(self):
|
||||
""" Test website company is set in context """
|
||||
website = self.env.ref('website.default_website')
|
||||
company = self.env['res.company'].create({'name': "Adaa"})
|
||||
website.company_id = company
|
||||
response = self.url_open('/multi_company_website')
|
||||
self.assertEqual(response.json()[0], company.id)
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.tests import HttpCase, tagged
|
||||
from odoo.tests.common import HOST
|
||||
from odoo.tools import config, mute_logger
|
||||
|
||||
|
||||
@tagged('-at_install', 'post_install')
|
||||
class WithContext(HttpCase):
|
||||
def test_01_homepage_url(self):
|
||||
# Setup
|
||||
website = self.env['website'].browse([1])
|
||||
website.write({
|
||||
'name': 'Test Website',
|
||||
'domain': f'http://{HOST}:{config["http_port"]}',
|
||||
'homepage_url': '/unexisting',
|
||||
})
|
||||
home_url = '/'
|
||||
contactus_url = '/contactus'
|
||||
contactus_url_full = website.domain + contactus_url
|
||||
contactus_content = b'content="Contact Us | Test Website"'
|
||||
self.env['website.menu'].search([
|
||||
('website_id', '=', website.id),
|
||||
('url', '=', contactus_url),
|
||||
]).sequence = 1
|
||||
|
||||
# 404 shouldn't be served but fallback on first menu
|
||||
# -------------------------------------------
|
||||
# / page exists | first menu | homepage_url
|
||||
# -------------------------------------------
|
||||
# yes | /contactus | /unexisting
|
||||
# -------------------------------------------
|
||||
r = self.url_open(website.homepage_url)
|
||||
self.assertEqual(r.status_code, 404, "The website homepage_url should be a 404")
|
||||
r = self.url_open(home_url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(r.history[0].status_code, 303)
|
||||
self.assertURLEqual(r.url, contactus_url_full)
|
||||
self.assertIn(contactus_content, r.content)
|
||||
|
||||
# same with 403
|
||||
# -------------------------------------------
|
||||
# / page exists | first menu | homepage_url
|
||||
# -------------------------------------------
|
||||
# yes | /contactus | /test_website/200/name-1
|
||||
# -------------------------------------------
|
||||
rec_unpublished = self.env['test.model'].create({
|
||||
'name': 'name',
|
||||
'is_published': False,
|
||||
})
|
||||
website.homepage_url = f"/test_website/200/name-{rec_unpublished.id}"
|
||||
with mute_logger('odoo.http'): # mute 403 warning
|
||||
r = self.url_open(website.homepage_url)
|
||||
self.assertEqual(r.status_code, 404, "The website homepage_url should be a 404")
|
||||
r = self.url_open(home_url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(r.history[0].status_code, 303)
|
||||
self.assertURLEqual(r.url, contactus_url_full)
|
||||
self.assertIn(contactus_content, r.content)
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import odoo.tests
|
||||
|
||||
|
||||
@odoo.tests.common.tagged('post_install', '-at_install')
|
||||
class TestWebsitePageManager(odoo.tests.HttpCase):
|
||||
def test_page_manager_test_model(self):
|
||||
if self.env['website'].search_count([]) == 1:
|
||||
website2 = self.env['website'].create({
|
||||
'name': 'My Website 2',
|
||||
'domain': '',
|
||||
'sequence': 20,
|
||||
})
|
||||
else:
|
||||
website2 = self.env['website'].search([], order='id desc', limit=1)
|
||||
self.env['test.model.multi.website'].create({'name': 'Test Model Multi Website 2', 'website_id': website2.id})
|
||||
self.assertTrue(
|
||||
len(set([t.website_id.id for t in self.env['test.model.multi.website'].search([])])) >= 3,
|
||||
"There should at least be one record without website_id and one for 2 different websites",
|
||||
)
|
||||
self.assertNotIn('website_id', self.env['test.model']._fields)
|
||||
self.start_tour('/web#action=test_website.action_test_model_multi_website', 'test_website_page_manager', login="admin")
|
||||
# This second test is about ensuring that you can switch from a list
|
||||
# view which has no `website_pages_list` js_class to its kanban view
|
||||
self.start_tour('/web#action=test_website.action_test_model_multi_website_js_class_bug', 'test_website_page_manager_js_class_bug', login="admin")
|
||||
self.start_tour('/web#action=test_website.action_test_model', 'test_website_page_manager_no_website_id', login="admin")
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.addons.website.tests.test_performance import UtilPerf
|
||||
|
||||
|
||||
class TestPerformance(UtilPerf):
|
||||
def test_10_perf_sql_website_controller_minimalist(self):
|
||||
url = '/empty_controller_test'
|
||||
select_tables_perf = {
|
||||
'base_registry_signaling': 1,
|
||||
}
|
||||
self._check_url_hot_query(url, 1, select_tables_perf)
|
||||
self.assertEqual(self._get_url_hot_query(url, cache=False), 1)
|
||||
|
|
@ -0,0 +1,320 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
import odoo
|
||||
from odoo.tests import HttpCase, tagged
|
||||
from odoo.tools import mute_logger
|
||||
from odoo.addons.http_routing.models.ir_http import slug
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
|
||||
@tagged('-at_install', 'post_install')
|
||||
class TestRedirect(HttpCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestRedirect, self).setUp()
|
||||
|
||||
self.user_portal = self.env['res.users'].with_context({'no_reset_password': True}).create({
|
||||
'name': 'Test Website Portal User',
|
||||
'login': 'portal_user',
|
||||
'password': 'portal_user',
|
||||
'email': 'portal_user@mail.com',
|
||||
'groups_id': [(6, 0, [self.env.ref('base.group_portal').id])]
|
||||
})
|
||||
|
||||
def test_01_redirect_308_model_converter(self):
|
||||
|
||||
self.env['website.rewrite'].create({
|
||||
'name': 'Test Website Redirect',
|
||||
'redirect_type': '308',
|
||||
'url_from': '/test_website/country/<model("res.country"):country>',
|
||||
'url_to': '/redirected/country/<model("res.country"):country>',
|
||||
})
|
||||
country_ad = self.env.ref('base.ad')
|
||||
|
||||
""" Ensure 308 redirect with model converter works fine, including:
|
||||
- Correct & working redirect as public user
|
||||
- Correct & working redirect as logged in user
|
||||
- Correct replace of url_for() URLs in DOM
|
||||
"""
|
||||
url = '/test_website/country/' + slug(country_ad)
|
||||
redirect_url = url.replace('test_website', 'redirected')
|
||||
|
||||
# [Public User] Open the original url and check redirect OK
|
||||
r = self.url_open(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertTrue(r.url.endswith(redirect_url), "Ensure URL got redirected")
|
||||
self.assertTrue(country_ad.name in r.text, "Ensure the controller returned the expected value")
|
||||
self.assertTrue(redirect_url in r.text, "Ensure the url_for has replaced the href URL in the DOM")
|
||||
|
||||
# [Logged In User] Open the original url and check redirect OK
|
||||
self.authenticate("portal_user", "portal_user")
|
||||
r = self.url_open(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertTrue(r.url.endswith(redirect_url), "Ensure URL got redirected (2)")
|
||||
self.assertTrue('Logged In' in r.text, "Ensure logged in")
|
||||
self.assertTrue(country_ad.name in r.text, "Ensure the controller returned the expected value (2)")
|
||||
self.assertTrue(redirect_url in r.text, "Ensure the url_for has replaced the href URL in the DOM")
|
||||
|
||||
def test_redirect_308_by_method_url_rewrite(self):
|
||||
self.env['website.rewrite'].create({
|
||||
'name': 'Test Website Redirect',
|
||||
'redirect_type': '308',
|
||||
'url_from': url_from,
|
||||
'url_to': f'{url_from}_new',
|
||||
} for url_from in ('/get', '/post', '/get_post'))
|
||||
|
||||
self.env.ref('test_website.test_view').arch = '''
|
||||
<t>
|
||||
<a href="/get"></a><a href="/post"></a><a href="/get_post"></a>
|
||||
</t>
|
||||
'''
|
||||
|
||||
# [Public User] Open the /test_view url and ensure urls are rewritten
|
||||
r = self.url_open('/test_view')
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(
|
||||
r.content.strip(),
|
||||
b'<a href="/get_new"></a><a href="/post_new"></a><a href="/get_post_new"></a>'
|
||||
)
|
||||
|
||||
@mute_logger('odoo.http') # mute 403 warning
|
||||
def test_02_redirect_308_RequestUID(self):
|
||||
self.env['website.rewrite'].create({
|
||||
'name': 'Test Website Redirect',
|
||||
'redirect_type': '308',
|
||||
'url_from': '/test_website/200/<model("test.model"):rec>',
|
||||
'url_to': '/test_website/308/<model("test.model"):rec>',
|
||||
})
|
||||
|
||||
rec_published = self.env['test.model'].create({'name': 'name', 'website_published': True})
|
||||
rec_unpublished = self.env['test.model'].create({'name': 'name', 'website_published': False})
|
||||
|
||||
WebsiteHttp = odoo.addons.website.models.ir_http.Http
|
||||
|
||||
def _get_error_html(env, code, value):
|
||||
return str(code).split('_')[-1], f"CUSTOM {code}"
|
||||
|
||||
with patch.object(WebsiteHttp, '_get_error_html', _get_error_html):
|
||||
# Patch will avoid to display real 404 page and regenerate assets each time and unlink old one.
|
||||
# And it allow to be sur that exception id handled by handle_exception and return a "managed error" page.
|
||||
|
||||
# published
|
||||
resp = self.url_open(f"/test_website/200/name-{rec_published.id}", allow_redirects=False)
|
||||
self.assertEqual(resp.status_code, 308)
|
||||
self.assertURLEqual(resp.headers.get('Location'), f"/test_website/308/name-{rec_published.id}")
|
||||
|
||||
resp = self.url_open(f"/test_website/308/name-{rec_published.id}", allow_redirects=False)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
resp = self.url_open(f"/test_website/200/xx-{rec_published.id}", allow_redirects=False)
|
||||
self.assertEqual(resp.status_code, 308)
|
||||
self.assertURLEqual(resp.headers.get('Location'), f"/test_website/308/xx-{rec_published.id}")
|
||||
|
||||
resp = self.url_open(f"/test_website/308/xx-{rec_published.id}", allow_redirects=False)
|
||||
self.assertEqual(resp.status_code, 301)
|
||||
self.assertURLEqual(resp.headers.get('Location'), f"/test_website/308/name-{rec_published.id}")
|
||||
|
||||
resp = self.url_open(f"/test_website/200/xx-{rec_published.id}", allow_redirects=True)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.assertURLEqual(resp.url, f"/test_website/308/name-{rec_published.id}")
|
||||
|
||||
# unexisting
|
||||
resp = self.url_open("/test_website/200/name-100", allow_redirects=False)
|
||||
self.assertEqual(resp.status_code, 308)
|
||||
self.assertURLEqual(resp.headers.get('Location'), "/test_website/308/name-100")
|
||||
|
||||
resp = self.url_open("/test_website/308/name-100", allow_redirects=False)
|
||||
self.assertEqual(resp.status_code, 404)
|
||||
self.assertEqual(resp.text, "CUSTOM 404")
|
||||
|
||||
resp = self.url_open("/test_website/200/xx-100", allow_redirects=False)
|
||||
self.assertEqual(resp.status_code, 308)
|
||||
self.assertURLEqual(resp.headers.get('Location'), "/test_website/308/xx-100")
|
||||
|
||||
resp = self.url_open("/test_website/308/xx-100", allow_redirects=False)
|
||||
self.assertEqual(resp.status_code, 404)
|
||||
self.assertEqual(resp.text, "CUSTOM 404")
|
||||
|
||||
# unpublish
|
||||
resp = self.url_open(f"/test_website/200/name-{rec_unpublished.id}", allow_redirects=False)
|
||||
self.assertEqual(resp.status_code, 308)
|
||||
self.assertURLEqual(resp.headers.get('Location'), f"/test_website/308/name-{rec_unpublished.id}")
|
||||
|
||||
resp = self.url_open(f"/test_website/308/name-{rec_unpublished.id}", allow_redirects=False)
|
||||
self.assertEqual(resp.status_code, 404)
|
||||
self.assertEqual(resp.text, "CUSTOM 404")
|
||||
|
||||
resp = self.url_open(f"/test_website/200/xx-{rec_unpublished.id}", allow_redirects=False)
|
||||
self.assertEqual(resp.status_code, 308)
|
||||
self.assertURLEqual(resp.headers.get('Location'), f"/test_website/308/xx-{rec_unpublished.id}")
|
||||
|
||||
resp = self.url_open(f"/test_website/308/xx-{rec_unpublished.id}", allow_redirects=False)
|
||||
self.assertEqual(resp.status_code, 404)
|
||||
self.assertEqual(resp.text, "CUSTOM 404")
|
||||
|
||||
# with seo_name as slug
|
||||
rec_published.seo_name = "seo_name"
|
||||
rec_unpublished.seo_name = "seo_name"
|
||||
|
||||
resp = self.url_open(f"/test_website/200/seo-name-{rec_published.id}", allow_redirects=False)
|
||||
self.assertEqual(resp.status_code, 308)
|
||||
self.assertURLEqual(resp.headers.get('Location'), f"/test_website/308/seo-name-{rec_published.id}")
|
||||
|
||||
resp = self.url_open(f"/test_website/308/seo-name-{rec_published.id}", allow_redirects=False)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
resp = self.url_open(f"/test_website/200/xx-{rec_unpublished.id}", allow_redirects=False)
|
||||
self.assertEqual(resp.status_code, 308)
|
||||
self.assertURLEqual(resp.headers.get('Location'), f"/test_website/308/xx-{rec_unpublished.id}")
|
||||
|
||||
resp = self.url_open(f"/test_website/308/xx-{rec_unpublished.id}", allow_redirects=False)
|
||||
self.assertEqual(resp.status_code, 404)
|
||||
self.assertEqual(resp.text, "CUSTOM 404")
|
||||
|
||||
resp = self.url_open("/test_website/200/xx-100", allow_redirects=False)
|
||||
self.assertEqual(resp.status_code, 308)
|
||||
self.assertURLEqual(resp.headers.get('Location'), "/test_website/308/xx-100")
|
||||
|
||||
resp = self.url_open("/test_website/308/xx-100", allow_redirects=False)
|
||||
self.assertEqual(resp.status_code, 404)
|
||||
self.assertEqual(resp.text, "CUSTOM 404")
|
||||
|
||||
def test_03_redirect_308_qs(self):
|
||||
self.env['website.rewrite'].create({
|
||||
'name': 'Test QS Redirect',
|
||||
'redirect_type': '308',
|
||||
'url_from': '/empty_controller_test',
|
||||
'url_to': '/empty_controller_test_redirected',
|
||||
})
|
||||
r = self.url_open('/test_website/test_redirect_view_qs?a=a')
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertIn(
|
||||
'href="/empty_controller_test_redirected?a=a"', r.text,
|
||||
"Redirection should have been applied, and query string should not have been duplicated.",
|
||||
)
|
||||
|
||||
@mute_logger('odoo.http') # mute 403 warning
|
||||
def test_04_redirect_301_route_unpublished_record(self):
|
||||
# 1. Accessing published record: Normal case, expecting 200
|
||||
rec1 = self.env['test.model'].create({
|
||||
'name': '301 test record',
|
||||
'is_published': True,
|
||||
})
|
||||
url_rec1 = '/test_website/200/' + slug(rec1)
|
||||
r = self.url_open(url_rec1)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
# 2. Accessing unpublished record: expecting 404 for public users
|
||||
rec1.is_published = False
|
||||
r = self.url_open(url_rec1)
|
||||
self.assertEqual(r.status_code, 404)
|
||||
|
||||
# 3. Accessing unpublished record with redirect to a 404: expecting 404
|
||||
redirect = self.env['website.rewrite'].create({
|
||||
'name': 'Test 301 Redirect route unpublished record',
|
||||
'redirect_type': '301',
|
||||
'url_from': url_rec1,
|
||||
'url_to': '/404',
|
||||
})
|
||||
r = self.url_open(url_rec1)
|
||||
self.assertEqual(r.status_code, 404)
|
||||
|
||||
# 4. Accessing unpublished record with redirect to another published
|
||||
# record: expecting redirect to that record
|
||||
rec2 = rec1.copy({'is_published': True})
|
||||
url_rec2 = '/test_website/200/' + slug(rec2)
|
||||
redirect.url_to = url_rec2
|
||||
r = self.url_open(url_rec1)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertTrue(
|
||||
r.url.endswith(url_rec2),
|
||||
"Unpublished record should redirect to published record set in redirect")
|
||||
|
||||
@mute_logger('odoo.http')
|
||||
def test_05_redirect_404_notfound_record(self):
|
||||
# 1. Accessing unexisting record: raise 404
|
||||
url_rec1 = '/test_website/200/unexisting-100000'
|
||||
r = self.url_open(url_rec1)
|
||||
self.assertEqual(r.status_code, 404)
|
||||
|
||||
# 2. Accessing unpublished record with redirect to a 404: expecting 404
|
||||
redirect = self.env['website.rewrite'].create({
|
||||
'name': 'Test 301 Redirect route unexisting record',
|
||||
'redirect_type': '301',
|
||||
'url_from': url_rec1,
|
||||
'url_to': '/get',
|
||||
})
|
||||
r = self.url_open(url_rec1, allow_redirects=False)
|
||||
self.assertEqual(r.status_code, 301)
|
||||
self.assertURLEqual(r.headers.get('Location'), redirect.url_to)
|
||||
|
||||
r = self.url_open(url_rec1, allow_redirects=True)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertURLEqual(r.url, redirect.url_to)
|
||||
|
||||
def test_redirect_308_multiple_url_endpoint(self):
|
||||
self.env['website.rewrite'].create({
|
||||
'name': 'Test Multi URL 308',
|
||||
'redirect_type': '308',
|
||||
'url_from': '/test_countries_308',
|
||||
'url_to': '/test_countries_308_redirected',
|
||||
})
|
||||
rec1 = self.env['test.model'].create({
|
||||
'name': '301 test record',
|
||||
'is_published': True,
|
||||
})
|
||||
url_rec1 = f"/test_countries_308/{slug(rec1)}"
|
||||
|
||||
resp = self.url_open("/test_countries_308", allow_redirects=False)
|
||||
self.assertEqual(resp.status_code, 308)
|
||||
self.assertURLEqual(resp.headers.get('Location'), "/test_countries_308_redirected")
|
||||
|
||||
resp = self.url_open(url_rec1)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.assertTrue(resp.url.endswith(url_rec1))
|
||||
|
||||
def test_redirect_with_qs(self):
|
||||
self.env['website.rewrite'].create({
|
||||
'name': 'Test 301 Redirect with qs',
|
||||
'redirect_type': '301',
|
||||
'url_from': '/foo?bar=1',
|
||||
'url_to': '/new-page-01',
|
||||
})
|
||||
self.env['website.rewrite'].create({
|
||||
'name': 'Test 301 Redirect with qs',
|
||||
'redirect_type': '301',
|
||||
'url_from': '/foo?bar=2',
|
||||
'url_to': '/new-page-10?qux=2',
|
||||
})
|
||||
self.env['website.rewrite'].create({
|
||||
'name': 'Test 301 Redirect without qs',
|
||||
'redirect_type': '301',
|
||||
'url_from': '/foo',
|
||||
'url_to': '/new-page-11',
|
||||
})
|
||||
|
||||
# should match qs first
|
||||
resp = self.url_open("/foo?bar=1", allow_redirects=False)
|
||||
self.assertEqual(resp.status_code, 301)
|
||||
self.assertURLEqual(resp.headers.get('Location'), "/new-page-01?bar=1")
|
||||
|
||||
# should match qs first
|
||||
resp = self.url_open("/foo?bar=2", allow_redirects=False)
|
||||
self.assertEqual(resp.status_code, 301)
|
||||
self.assertURLEqual(resp.headers.get('Location'), "/new-page-10?qux=2&bar=2")
|
||||
|
||||
# should match no qs
|
||||
resp = self.url_open("/foo?bar=3", allow_redirects=False)
|
||||
self.assertEqual(resp.status_code, 301)
|
||||
self.assertURLEqual(resp.headers.get('Location'), "/new-page-11?bar=3")
|
||||
|
||||
resp = self.url_open("/foo", allow_redirects=False)
|
||||
self.assertEqual(resp.status_code, 301)
|
||||
self.assertURLEqual(resp.headers.get('Location'), "/new-page-11")
|
||||
|
||||
# we dont support wrong get order
|
||||
# purpose is to support simple case like content.asp?id=xx
|
||||
resp = self.url_open("/foo?oups=1&bar=2", allow_redirects=False)
|
||||
self.assertEqual(resp.status_code, 301)
|
||||
self.assertURLEqual(resp.headers.get('Location'), "/new-page-11?oups=1&bar=2")
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
import re
|
||||
|
||||
import odoo.tests
|
||||
from odoo.tools import mute_logger
|
||||
|
||||
|
||||
def break_view(view, fr='<p>placeholder</p>', to='<p t-field="no_record.exist"/>'):
|
||||
view.arch = view.arch.replace(fr, to)
|
||||
|
||||
|
||||
@odoo.tests.common.tagged('post_install', '-at_install')
|
||||
class TestWebsiteResetViews(odoo.tests.HttpCase):
|
||||
|
||||
def fix_it(self, page, mode='soft'):
|
||||
self.authenticate("admin", "admin")
|
||||
resp = self.url_open(page)
|
||||
self.assertEqual(resp.status_code, 500, "Waiting 500")
|
||||
self.assertTrue('<button data-mode="soft" class="reset_templates_button' in resp.text)
|
||||
data = {'view_id': self.find_template(resp), 'redirect': page, 'mode': mode}
|
||||
resp = self.url_open('/website/reset_template', data)
|
||||
self.assertEqual(resp.status_code, 200, "Waiting 200")
|
||||
|
||||
def find_template(self, response):
|
||||
find = re.search(r'<input.*type="hidden".*name="view_id".*value="([0-9]+)?"', response.text)
|
||||
return find and find.group(1)
|
||||
|
||||
def setUp(self):
|
||||
super(TestWebsiteResetViews, self).setUp()
|
||||
self.Website = self.env['website']
|
||||
self.View = self.env['ir.ui.view']
|
||||
self.test_view = self.Website.viewref('test_website.test_view')
|
||||
|
||||
@mute_logger('odoo.http')
|
||||
def test_01_reset_specific_page_view(self):
|
||||
self.test_page_view = self.Website.viewref('test_website.test_page_view')
|
||||
total_views = self.View.search_count([('type', '=', 'qweb')])
|
||||
# Trigger COW then break the QWEB XML on it
|
||||
break_view(self.test_page_view.with_context(website_id=1))
|
||||
self.assertEqual(total_views + 1, self.View.search_count([('type', '=', 'qweb')]), "Missing COW view")
|
||||
self.fix_it('/test_page_view')
|
||||
|
||||
@mute_logger('odoo.http')
|
||||
def test_02_reset_specific_view_controller(self):
|
||||
total_views = self.View.search_count([('type', '=', 'qweb')])
|
||||
# Trigger COW then break the QWEB XML on it
|
||||
# `t-att-data="no_record.exist"` will test the case where exception.html contains branding
|
||||
break_view(self.test_view.with_context(website_id=1), to='<p t-att-data="no_record.exist" />')
|
||||
self.assertEqual(total_views + 1, self.View.search_count([('type', '=', 'qweb')]), "Missing COW view")
|
||||
self.fix_it('/test_view')
|
||||
|
||||
@mute_logger('odoo.http')
|
||||
def test_03_reset_specific_view_controller_t_called(self):
|
||||
self.test_view_to_be_t_called = self.Website.viewref('test_website.test_view_to_be_t_called')
|
||||
|
||||
total_views = self.View.search_count([('type', '=', 'qweb')])
|
||||
# Trigger COW then break the QWEB XML on it
|
||||
break_view(self.test_view_to_be_t_called.with_context(website_id=1))
|
||||
break_view(self.test_view, to='<t t-call="test_website.test_view_to_be_t_called"/>')
|
||||
self.assertEqual(total_views + 1, self.View.search_count([('type', '=', 'qweb')]), "Missing COW view")
|
||||
self.fix_it('/test_view')
|
||||
|
||||
@mute_logger('odoo.http')
|
||||
def test_04_reset_specific_view_controller_inherit(self):
|
||||
self.test_view_child_broken = self.Website.viewref('test_website.test_view_child_broken')
|
||||
|
||||
# Activate and break the inherited view
|
||||
self.test_view_child_broken.active = True
|
||||
break_view(self.test_view_child_broken.with_context(website_id=1, load_all_views=True))
|
||||
|
||||
self.fix_it('/test_view')
|
||||
|
||||
# This test work in real life, but not in test mode since we cannot rollback savepoint.
|
||||
# @mute_logger('odoo.http', 'odoo.addons.website.models.ir_ui_view')
|
||||
# def test_05_reset_specific_view_controller_broken_request(self):
|
||||
# total_views = self.View.search_count([('type', '=', 'qweb')])
|
||||
# # Trigger COW then break the QWEB XML on it
|
||||
# break_view(self.test_view.with_context(website_id=1), to='<t t-esc="request.env[\'website\'].browse(\'a\').name" />')
|
||||
# self.assertEqual(total_views + 1, self.View.search_count([('type', '=', 'qweb')]), "Missing COW view (1)")
|
||||
# self.fix_it('/test_view')
|
||||
|
||||
# also mute ir.ui.view as `_get_view_id()` will raise "Could not find view object with xml_id 'no_record.exist'""
|
||||
@mute_logger('odoo.http', 'odoo.addons.website.models.ir_ui_view')
|
||||
def test_06_reset_specific_view_controller_inexisting_template(self):
|
||||
total_views = self.View.search_count([('type', '=', 'qweb')])
|
||||
# Trigger COW then break the QWEB XML on it
|
||||
break_view(self.test_view.with_context(website_id=1), to='<t t-call="no_record.exist"/>')
|
||||
self.assertEqual(total_views + 1, self.View.search_count([('type', '=', 'qweb')]), "Missing COW view (2)")
|
||||
self.fix_it('/test_view')
|
||||
|
||||
@mute_logger('odoo.http')
|
||||
def test_07_reset_page_view_complete_flow(self):
|
||||
self.start_tour(self.env['website'].get_client_action_url('/test_page_view'), 'test_reset_page_view_complete_flow_part1', login="admin")
|
||||
self.fix_it('/test_page_view')
|
||||
self.start_tour(self.env['website'].get_client_action_url('/test_page_view'), 'test_reset_page_view_complete_flow_part2', login="admin")
|
||||
self.fix_it('/test_page_view')
|
||||
|
||||
@mute_logger('odoo.http')
|
||||
def test_08_reset_specific_page_view_hard_mode(self):
|
||||
self.test_page_view = self.Website.viewref('test_website.test_page_view')
|
||||
total_views = self.View.search_count([('type', '=', 'qweb')])
|
||||
# Trigger COW then break the QWEB XML on it
|
||||
break_view(self.test_page_view.with_context(website_id=1))
|
||||
# Break it again to have a previous arch different than file arch
|
||||
break_view(self.test_page_view.with_context(website_id=1))
|
||||
self.assertEqual(total_views + 1, self.View.search_count([('type', '=', 'qweb')]), "Missing COW view")
|
||||
with self.assertRaises(AssertionError):
|
||||
# soft reset should not be able to reset the view as previous
|
||||
# version is also broken
|
||||
self.fix_it('/test_page_view')
|
||||
self.fix_it('/test_page_view', 'hard')
|
||||
# hard reset should set arch_updated to false
|
||||
self.assertFalse(self.test_page_view.arch_updated)
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from lxml import html
|
||||
|
||||
import odoo.tests
|
||||
|
||||
from odoo.addons.base.tests.common import HttpCaseWithUserDemo
|
||||
|
||||
|
||||
@odoo.tests.common.tagged('post_install', '-at_install')
|
||||
class TestWebsiteSession(HttpCaseWithUserDemo):
|
||||
|
||||
def test_01_run_test(self):
|
||||
self.start_tour('/', 'test_json_auth')
|
||||
|
||||
def test_branding_cache(self):
|
||||
def has_branding(html_text):
|
||||
el = html.fromstring(html_text)
|
||||
return el.xpath('//*[@data-oe-model="test.model"]')
|
||||
|
||||
self.user_demo.groups_id += self.env.ref('website.group_website_restricted_editor')
|
||||
self.user_demo.groups_id -= self.env.ref('website.group_website_designer')
|
||||
|
||||
# Create session for demo user.
|
||||
public_session = self.authenticate(None, None)
|
||||
demo_session = self.authenticate('demo', 'demo')
|
||||
record = self.env['test.model'].search([], limit=1)
|
||||
result = self.url_open(f'/test_website/model_item/{record.id}')
|
||||
self.assertTrue(has_branding(result.text), "Should have branding for user demo")
|
||||
|
||||
# Public user.
|
||||
self.opener.cookies['session_id'] = public_session.sid
|
||||
result = self.url_open(f'/test_website/model_item/{record.id}')
|
||||
self.assertFalse(has_branding(result.text), "Should have no branding for public user")
|
||||
|
||||
# Back to demo user.
|
||||
self.opener.cookies['session_id'] = demo_session.sid
|
||||
result = self.url_open(f'/test_website/model_item/{record.id}')
|
||||
self.assertTrue(has_branding(result.text), "Should have branding for user demo")
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import odoo
|
||||
import odoo.tests
|
||||
|
||||
@odoo.tests.tagged('-at_install', 'post_install')
|
||||
class TestWebsiteSettings(odoo.tests.HttpCase):
|
||||
def test_01_multi_website_settings(self):
|
||||
# If not enabled (like in demo data), landing on res.config will try
|
||||
# to disable module_sale_quotation_builder and raise an issue
|
||||
group_order_template = self.env.ref('sale_management.group_sale_order_template', raise_if_not_found=False)
|
||||
if group_order_template:
|
||||
self.env.ref('base.group_user').write({"implied_ids": [(4, group_order_template.id)]})
|
||||
self.env['website'].create({'name': "Website Test Settings", 'specific_user_account': True})
|
||||
self.start_tour("/web", 'website_settings_m2o_dirty', login="admin")
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.tests.common import HOST, new_test_user, tagged
|
||||
from odoo.tools import config, mute_logger
|
||||
|
||||
from odoo.addons.base.tests.common import HttpCase
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestSystray(HttpCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.group_restricted_editor = cls.env.ref('website.group_website_restricted_editor')
|
||||
cls.group_tester = cls.env.ref('test_website.group_test_website_tester')
|
||||
# Do not rely on HttpCaseWithUserDemo to avoid having different user
|
||||
# definitions with and without demo data.
|
||||
cls.user_test = new_test_user(cls.env, login='testtest', website_id=False)
|
||||
other_website = cls.env['website'].create({
|
||||
'name': 'Other',
|
||||
})
|
||||
cls.env['ir.ui.view'].create({
|
||||
'name': "Patch to recognize other website",
|
||||
'website_id': other_website.id,
|
||||
'type': 'qweb',
|
||||
'inherit_id': cls.env.ref('test_website.test_model_page_layout').id,
|
||||
'arch': """
|
||||
<xpath expr="//span" position="after">
|
||||
<div>Other</div>
|
||||
</xpath>
|
||||
"""
|
||||
})
|
||||
|
||||
|
||||
@mute_logger('odoo.addons.http_routing.models.ir_http', 'odoo.http')
|
||||
def test_01_admin(self):
|
||||
self.start_tour(self.env['website'].get_client_action_url('/test_model/1'), 'test_systray_admin', login="admin")
|
||||
|
||||
@mute_logger('odoo.addons.http_routing.models.ir_http', 'odoo.http')
|
||||
def test_02_reditor_tester(self):
|
||||
self.user_test.groups_id |= self.group_restricted_editor
|
||||
self.user_test.groups_id |= self.group_tester
|
||||
self.start_tour(self.env['website'].get_client_action_url('/test_model/1'), 'test_systray_reditor_tester', login="testtest")
|
||||
|
||||
@mute_logger('odoo.addons.http_routing.models.ir_http', 'odoo.http')
|
||||
def test_03_reditor_not_tester(self):
|
||||
self.user_test.groups_id |= self.group_restricted_editor
|
||||
self.user_test.groups_id = self.user_test.groups_id.filtered(lambda group: group != self.group_tester)
|
||||
self.assertNotIn(self.group_tester.id, self.user_test.groups_id.ids, "User should not be a group_tester")
|
||||
self.start_tour(self.env['website'].get_client_action_url('/test_model/1'), 'test_systray_reditor_not_tester', login="testtest")
|
||||
|
||||
@mute_logger('odoo.addons.http_routing.models.ir_http', 'odoo.http')
|
||||
def test_04_not_reditor_tester(self):
|
||||
self.user_test.groups_id = self.user_test.groups_id.filtered(lambda group: group != self.group_restricted_editor)
|
||||
self.user_test.groups_id |= self.group_tester
|
||||
self.assertNotIn(self.group_restricted_editor.id, self.user_test.groups_id.ids, "User should not be a group_restricted_editor")
|
||||
self.start_tour(self.env['website'].get_client_action_url('/test_model/1'), 'test_systray_not_reditor_tester', login="testtest")
|
||||
|
||||
@mute_logger('odoo.addons.http_routing.models.ir_http', 'odoo.http')
|
||||
def test_05_not_reditor_not_tester(self):
|
||||
self.user_test.groups_id = self.user_test.groups_id.filtered(lambda group: group not in [self.group_restricted_editor, self.group_tester])
|
||||
self.assertNotIn(self.group_restricted_editor.id, self.user_test.groups_id.ids, "User should not be a group_restricted_editor")
|
||||
self.assertNotIn(self.group_tester.id, self.user_test.groups_id.ids, "User should not be a group_tester")
|
||||
self.start_tour(self.env['website'].get_client_action_url('/test_model/1'), 'test_systray_not_reditor_not_tester', login="testtest")
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.addons.website.tools import MockRequest
|
||||
from odoo.tests import standalone
|
||||
|
||||
|
||||
@standalone('cow_views', 'website_standalone')
|
||||
def test_01_cow_views_unlink_on_module_update(env):
|
||||
""" Ensure COW views are correctly removed during module update.
|
||||
Not removing the view could lead to traceback:
|
||||
- Having a view A
|
||||
- Having a view B that inherits from a view C
|
||||
- View B t-call view A
|
||||
- COW view B
|
||||
- Delete view A and B from module datas and update it
|
||||
- Rendering view C will crash since it will render child view B that
|
||||
t-call unexisting view A
|
||||
"""
|
||||
|
||||
View = env['ir.ui.view']
|
||||
Imd = env['ir.model.data']
|
||||
|
||||
update_module_base_view = env.ref('test_website.update_module_base_view')
|
||||
update_module_view_to_be_t_called = View.create({
|
||||
'name': 'View to be t-called',
|
||||
'type': 'qweb',
|
||||
'arch': '<div>I will be t-called</div>',
|
||||
'key': 'test_website.update_module_view_to_be_t_called',
|
||||
})
|
||||
update_module_child_view = View.create({
|
||||
'name': 'Child View',
|
||||
'mode': 'extension',
|
||||
'inherit_id': update_module_base_view.id,
|
||||
'arch': '''
|
||||
<div position="inside">
|
||||
<t t-call="test_website.update_module_view_to_be_t_called"/>
|
||||
</div>
|
||||
''',
|
||||
'key': 'test_website.update_module_child_view',
|
||||
})
|
||||
|
||||
# Create IMD so when updating the module the views will be removed (not found in file)
|
||||
Imd.create({
|
||||
'module': 'test_website',
|
||||
'name': 'update_module_view_to_be_t_called',
|
||||
'model': 'ir.ui.view',
|
||||
'res_id': update_module_view_to_be_t_called.id,
|
||||
})
|
||||
Imd.create({
|
||||
'module': 'test_website',
|
||||
'name': 'update_module_child_view',
|
||||
'model': 'ir.ui.view',
|
||||
'res_id': update_module_child_view.id,
|
||||
})
|
||||
|
||||
# Trigger COW on child view
|
||||
update_module_child_view.with_context(website_id=1).write({'name': 'Child View (W1)'})
|
||||
|
||||
# Ensure views are correctly setup
|
||||
msg = "View '%s' does not exist!"
|
||||
assert View.search_count([
|
||||
('type', '=', 'qweb'),
|
||||
('key', '=', update_module_child_view.key)
|
||||
]) == 2, msg % update_module_child_view.key
|
||||
assert bool(env.ref(update_module_view_to_be_t_called.key)),\
|
||||
msg % update_module_view_to_be_t_called.key
|
||||
assert bool(env.ref(update_module_base_view.key)), msg % update_module_base_view.key
|
||||
|
||||
# Upgrade the module
|
||||
test_website_module = env['ir.module.module'].search([('name', '=', 'test_website')])
|
||||
test_website_module.button_immediate_upgrade()
|
||||
env.reset() # clear the set of environments
|
||||
env = env() # get an environment that refers to the new registry
|
||||
|
||||
# Ensure generic views got removed
|
||||
view = env.ref('test_website.update_module_view_to_be_t_called', raise_if_not_found=False)
|
||||
assert not view, "Generic view did not get removed!"
|
||||
|
||||
# Ensure specific COW views got removed
|
||||
assert not env['ir.ui.view'].search_count([
|
||||
('type', '=', 'qweb'),
|
||||
('key', '=', 'test_website.update_module_child_view'),
|
||||
]), "Specific COW views did not get removed!"
|
||||
|
||||
|
||||
@standalone('theme_views', 'website_standalone')
|
||||
def test_02_copy_ids_views_unlink_on_module_update(env):
|
||||
""" Ensure copy_ids views are correctly removed during module update.
|
||||
- Having an ir.ui.view A in the codebase, eg `website.layout`
|
||||
- Having a theme.ir.ui.view B in a theme, inheriting ir.ui.view A
|
||||
- Removing the theme.ir.ui.view B from the XML file and then updating the
|
||||
theme for a particular website should:
|
||||
1. Remove the theme.ir.ui.view record, which is the record pointed by the
|
||||
ir.model.data
|
||||
-> This is done through the regular Odoo behavior related to the
|
||||
ir.model.data and XML file check on upgrade.
|
||||
2. Remove the theme.ir.ui.view's copy_ids (sort of the COW views)
|
||||
-> Not working for now
|
||||
3. (not impact other website using this theme, see below)
|
||||
-> This is done through odoo/odoo@96ef4885a79 but did not come with
|
||||
tests
|
||||
|
||||
Point 2. was not working, this test aims to ensure it will now.
|
||||
Note: This can't be done through a `ondelete=cascade` as this would
|
||||
impact other websites when modifying a specific website. This would
|
||||
be against the multi-website rule:
|
||||
"What is done on a website should not alter other websites."
|
||||
|
||||
Regarding the flow described above, if a theme module was updated
|
||||
through the command line (or via the UI, but this is not possible in
|
||||
standard as theme modules are hidden from the Apps), it should
|
||||
update every website using this theme.
|
||||
"""
|
||||
View = env['ir.ui.view']
|
||||
ThemeView = env['theme.ir.ui.view']
|
||||
Imd = env['ir.model.data']
|
||||
|
||||
website_1 = env['website'].browse(1)
|
||||
website_2 = env['website'].browse(2)
|
||||
theme_default = env.ref('base.module_theme_default')
|
||||
|
||||
# Install theme_default on website 1 and website 2
|
||||
(website_1 + website_2).theme_id = theme_default
|
||||
env['ir.module.module'].with_context(load_all_views=True)._theme_load(website_1)
|
||||
env['ir.module.module'].with_context(load_all_views=True)._theme_load(website_2)
|
||||
|
||||
key = 'theme_default.theme_child_view'
|
||||
domain = [
|
||||
('type', '=', 'qweb'),
|
||||
('key', '=', key),
|
||||
]
|
||||
|
||||
def _simulate_xml_view():
|
||||
# Simulate a theme.ir.ui.view inside theme_default XML files
|
||||
base_view = env.ref('test_website.update_module_base_view')
|
||||
theme_child_view = ThemeView.create({
|
||||
'name': 'Theme Child View',
|
||||
'mode': 'extension',
|
||||
'inherit_id': f'{base_view._name},{base_view.id}',
|
||||
'arch': '''
|
||||
<div position="inside">
|
||||
<p>, and I am inherited by a theme.ir.ui.view</p>
|
||||
</div>
|
||||
''',
|
||||
'key': key,
|
||||
})
|
||||
# Create IMD so when updating the module the views will be removed (not found in file)
|
||||
Imd.create({
|
||||
'module': 'theme_default',
|
||||
'name': 'theme_child_view',
|
||||
'model': 'theme.ir.ui.view',
|
||||
'res_id': theme_child_view.id,
|
||||
})
|
||||
# Simulate the theme.ir.ui.view being installed on website 1 and 2
|
||||
View.create([
|
||||
theme_child_view._convert_to_base_model(website_1),
|
||||
theme_child_view._convert_to_base_model(website_2),
|
||||
])
|
||||
|
||||
# Ensure views are correctly setup: the theme.ir.ui.view should have been
|
||||
# copied to an ir.ui.view for website 1
|
||||
view_website_1, view_website_2 = View.search(domain + [
|
||||
('theme_template_id', '=', theme_child_view.id),
|
||||
('website_id', 'in', (website_1 + website_2).ids),
|
||||
])
|
||||
assert (
|
||||
set((view_website_1 + view_website_2)).issubset(theme_child_view.copy_ids)
|
||||
and view_website_1.website_id == website_1
|
||||
and view_website_2.website_id == website_2
|
||||
), "Theme View should have been copied to the website."
|
||||
|
||||
return view_website_1, view_website_2, theme_child_view
|
||||
|
||||
##########################################
|
||||
# CASE 1: generic update (-u, migration) #
|
||||
##########################################
|
||||
|
||||
view_website_1, view_website_2, theme_child_view = _simulate_xml_view()
|
||||
|
||||
# Upgrade the module
|
||||
theme_default.button_immediate_upgrade()
|
||||
env.reset() # clear the set of environments
|
||||
env = env() # get an environment that refers to the new registry
|
||||
|
||||
# Ensure the theme.ir.ui.view got removed (since there is an IMD but not
|
||||
# present in XML files)
|
||||
view = env.ref('theme_default.theme_child_view', False)
|
||||
assert not view, "Theme view should have been removed during module update."
|
||||
assert not theme_child_view.exists(),\
|
||||
"Theme view should have been removed during module update. (2)"
|
||||
|
||||
# Ensure copy_ids view got removed (and is not a leftover orphan)
|
||||
assert not View.search(domain), "copy_ids views did not get removed!"
|
||||
assert not (view_website_1.exists() or view_website_2.exists()),\
|
||||
"copy_ids views did not get removed! (2)"
|
||||
|
||||
#####################################################
|
||||
# CASE 2: specific update (website theme selection) #
|
||||
#####################################################
|
||||
|
||||
view_website_1, view_website_2, theme_child_view = _simulate_xml_view()
|
||||
|
||||
# Upgrade the module
|
||||
with MockRequest(env, website=website_1):
|
||||
theme_default.button_immediate_upgrade()
|
||||
env.reset() # clear the set of environments
|
||||
env = env() # get an environment that refers to the new registry
|
||||
|
||||
# Ensure the theme.ir.ui.view got removed (since there is an IMD but not
|
||||
# present in XML files)
|
||||
view = env.ref('theme_default.theme_child_view', False)
|
||||
assert not view, "Theme view should have been removed during module update."
|
||||
assert not theme_child_view.exists(),\
|
||||
"Theme view should have been removed during module update. (2)"
|
||||
|
||||
# Ensure only website_1 copy_ids got removed, website_2 should be untouched
|
||||
assert not view_website_1.exists() and view_website_2.exists(),\
|
||||
"Only website_1 copy should be removed (2)"
|
||||
Loading…
Add table
Add a link
Reference in a new issue