175 lines
7.6 KiB
Python
175 lines
7.6 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
import json
|
|
|
|
from werkzeug.urls import url_encode
|
|
|
|
from unittest.mock import patch, Mock
|
|
from odoo import tests
|
|
from odoo.tools.misc import mute_logger, submap
|
|
from odoo.addons.website.controllers.main import Website
|
|
|
|
|
|
@tests.tagged('post_install', '-at_install')
|
|
class TestControllers(tests.HttpCase):
|
|
|
|
@mute_logger('odoo.addons.http_routing.models.ir_http', 'odoo.http')
|
|
def test_last_created_pages_autocompletion(self):
|
|
self.authenticate("admin", "admin")
|
|
Page = self.env['website.page']
|
|
last_5_url_edited = []
|
|
base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
|
suggested_links_url = base_url + '/website/get_suggested_links'
|
|
|
|
old_pages = Page
|
|
for i in range(0, 10):
|
|
new_page = Page.create({
|
|
'name': 'Generic',
|
|
'type': 'qweb',
|
|
'arch': '''
|
|
<div>content</div>
|
|
''',
|
|
'key': "test.generic_view-%d" % i,
|
|
'url': "/generic-%d" % i,
|
|
'is_published': True,
|
|
})
|
|
if i % 2 == 0:
|
|
old_pages += new_page
|
|
else:
|
|
last_5_url_edited.append(new_page.url)
|
|
|
|
self.opener.post(url=suggested_links_url, json={'params': {'needle': '/'}})
|
|
# mark as old
|
|
old_pages._write({'write_date': '2020-01-01'})
|
|
|
|
res = self.opener.post(url=suggested_links_url, json={'params': {'needle': '/'}})
|
|
resp = json.loads(res.content)
|
|
assert 'result' in resp
|
|
suggested_links = resp['result']
|
|
last_modified_history = next(o for o in suggested_links['others'] if o["title"] == "Last modified pages")
|
|
last_modified_values = map(lambda o: o['value'], last_modified_history['values'])
|
|
|
|
matching_pages = set(map(lambda o: o['value'], suggested_links['matching_pages']))
|
|
self.assertEqual(set(last_modified_values), set(last_5_url_edited) - matching_pages)
|
|
|
|
def test_02_client_action_iframe_url(self):
|
|
urls = [
|
|
'/', # Homepage URL (special case)
|
|
'/contactus', # Regular website.page URL
|
|
'/website/info', # Controller (!!also testing multi slashes URL!!)
|
|
'/contactus?name=testing', # Query string URL
|
|
]
|
|
for url in urls:
|
|
resp = self.url_open(f'/@{url}')
|
|
self.assertURLEqual(resp.url, url, "Public user should have landed in the frontend")
|
|
self.authenticate("admin", "admin")
|
|
for url in urls:
|
|
resp = self.url_open(f'/@{url}')
|
|
backend_params = url_encode(dict(path=url))
|
|
self.assertURLEqual(
|
|
resp.url, f'/odoo/action-website.website_preview?{backend_params}',
|
|
"Internal user should have landed in the backend")
|
|
|
|
def test_03_website_image(self):
|
|
attachment = self.env['ir.attachment'].create({
|
|
'name': 'one_pixel.png',
|
|
'datas': 'iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAYAAADgzO9IAAAAJElEQVQI'
|
|
'mWP4/b/qPzbM8Pt/1X8GBgaEAJTNgFcHXqOQMV4dAMmObXXo1/BqAAAA'
|
|
'AElFTkSuQmCC',
|
|
'public': True,
|
|
})
|
|
|
|
res = self.url_open(f'/website/image/ir.attachment/{attachment.id}_unique/raw?download=1')
|
|
res.raise_for_status()
|
|
|
|
headers = {
|
|
'Content-Length': '93',
|
|
'Content-Type': 'image/png',
|
|
'Content-Disposition': 'attachment; filename=one_pixel.png',
|
|
'Cache-Control': 'public, max-age=31536000, immutable',
|
|
}
|
|
self.assertEqual(submap(res.headers, headers.keys()), headers)
|
|
self.assertEqual(res.content, attachment.raw)
|
|
|
|
def test_04_website_partner_avatar(self):
|
|
partner = self.env['res.partner'].create({'name': "Jack O'Neill"})
|
|
|
|
with self.subTest(published=False):
|
|
partner.website_published = False
|
|
res = self.url_open(f'/website/image/res.partner/{partner.id}/avatar_128?download=1')
|
|
self.assertEqual(res.status_code, 404, "Public user should't access avatar of unpublished partners")
|
|
|
|
with self.subTest(published=True):
|
|
partner.website_published = True
|
|
res = self.url_open(f'/website/image/res.partner/{partner.id}/avatar_128?download=1')
|
|
self.assertEqual(res.status_code, 200, "Public user should access avatar of published partners")
|
|
|
|
with self.subTest(published=True):
|
|
partner.website_published = True
|
|
self.patch(self.env.registry[partner._name].avatar_128, 'groups', 'base.group_system')
|
|
res = self.url_open(f'/website/image/res.partner/{partner.id}/avatar_128?download=1')
|
|
self.assertEqual(
|
|
res.status_code,
|
|
404,
|
|
"Public user shouldn't access record fields with a `groups` even if published"
|
|
)
|
|
|
|
@patch('requests.get')
|
|
def test_05_seo_suggest_language_regex(self, mock_get):
|
|
"""
|
|
Test the seo_suggest method to verify it properly handles different
|
|
language inputs, sends correct parameters ('hl' for host language and
|
|
'gl' for geolocation) to the Google API, and returns the expected
|
|
suggestions. The test checks a variety of cases including:
|
|
- Regional language codes (e.g., 'en_US', 'fr_FR')
|
|
- Basic language codes (e.g., 'es', 'sr')
|
|
- Language codes with script modifier (e.g., 'sr_RS@latin',
|
|
'zh_CN@pinyin')
|
|
- Empty string input to handle default case
|
|
"""
|
|
|
|
# Mocking the response from Google API to simulate what would be
|
|
# returned by the seo_suggest method.
|
|
mock_response = Mock()
|
|
mock_response.content = '''<?xml version="1.0"?>
|
|
<toplevel>
|
|
<CompleteSuggestion>
|
|
<suggestion data="test suggestion"/>
|
|
</CompleteSuggestion>
|
|
</toplevel>'''
|
|
mock_get.return_value = mock_response
|
|
|
|
# Test cases with different language inputs and expected hl and gl
|
|
# values.
|
|
test_cases = [
|
|
('en_US', ['en', 'US']), # US English
|
|
('fr_FR', ['fr', 'FR']), # French in France
|
|
('es', ['es', '']), # Spanish without country code
|
|
('sr_RS@latin', ['sr', 'RS']), # Serbian with script in Serbia
|
|
('zh_CN@pinyin', ['zh', 'CN']), # Chinese with pinyin script in China
|
|
('sr@latin', ['sr', '']), # Serbian with script but no country
|
|
('', ['en', 'US']) # Default case (empty lang. input)
|
|
]
|
|
|
|
for lang_input, expected_output in test_cases:
|
|
# subTest creates an isolated context for each test case, allowing
|
|
# failures to be reported separately.
|
|
with self.subTest(lang=lang_input):
|
|
result = Website.seo_suggest(self, keywords="test", lang=lang_input)
|
|
|
|
# Extract the parameters that were passed in the mock
|
|
# requests.get call.
|
|
called_params = mock_get.call_args[1]['params']
|
|
|
|
# Verify that the 'hl' parameter (host language) matches the
|
|
# expected output
|
|
self.assertEqual(called_params['hl'], expected_output[0])
|
|
|
|
# Verify that the 'gl' parameter (geolocation) matches the
|
|
# expected output
|
|
self.assertEqual(called_params['gl'], expected_output[1])
|
|
|
|
# Verify that the returned result contains the expected
|
|
# suggestion "test suggestion"
|
|
self.assertIn('test suggestion', result)
|