forked from Mapan/odoo17e
397 lines
18 KiB
Python
397 lines
18 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from datetime import timedelta
|
|
import base64
|
|
import io
|
|
import os
|
|
import re
|
|
from markupsafe import Markup
|
|
from PIL import Image
|
|
from unittest import skipIf
|
|
from odoo import fields
|
|
from odoo.tests.common import tagged, users
|
|
from odoo.addons.base.tests.common import HttpCaseWithUserDemo
|
|
from odoo.addons.mail.tests.common import MailCommon, mail_new_test_user
|
|
|
|
|
|
class TestKnowledgeUICommon(HttpCaseWithUserDemo, MailCommon):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super(TestKnowledgeUICommon, cls).setUpClass()
|
|
# remove existing articles to ease tour management
|
|
cls.env['knowledge.article'].with_context(active_test=False).search([]).unlink()
|
|
|
|
cls.user_portal = mail_new_test_user(
|
|
cls.env,
|
|
company_id=cls.company_admin.id,
|
|
email='patrick.portal@test.example.com',
|
|
groups='base.group_portal',
|
|
login='portal_test',
|
|
name='Patrick Portal',
|
|
notification_type='email',
|
|
tz='Europe/Brussels',
|
|
)
|
|
|
|
@tagged('post_install', '-at_install', 'knowledge', 'knowledge_tour')
|
|
class TestKnowledgeUI(TestKnowledgeUICommon):
|
|
|
|
def test_knowledge_history(self):
|
|
"""This tour will check that the history works properly."""
|
|
self.start_tour('/web', 'knowledge_history_tour', login='demo', step_delay=100)
|
|
|
|
def test_knowledge_load_template(self):
|
|
"""This tour will check that the user can create a new article by using
|
|
the template gallery."""
|
|
category = self.env['knowledge.article.template.category'].create({
|
|
'name': 'Personal'
|
|
})
|
|
template = self.env['knowledge.article'].create({
|
|
'icon': '📚',
|
|
'article_properties_definition': [{
|
|
'name': '28db68689e91de10',
|
|
'type': 'char',
|
|
'string': 'My Text Field',
|
|
'default': ''
|
|
}],
|
|
'is_template': True,
|
|
'template_name': 'My Template',
|
|
'template_category_id': category.id,
|
|
'template_body': Markup('<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>'),
|
|
})
|
|
|
|
self.start_tour('/web', 'knowledge_load_template', login='admin')
|
|
article = self.env['knowledge.article'].search([('id', '!=', template.id)], limit=1)
|
|
self.assertTrue(bool(article))
|
|
|
|
# Strip collaborative steps ids from the body for content-only
|
|
# comparison
|
|
body = re.sub(r'\s*data-last-history-steps="[^"]*"', '', article.body)
|
|
|
|
self.assertEqual(template.template_body, body)
|
|
self.assertEqual(template.icon, article.icon)
|
|
self.assertEqual(template.article_properties_definition, article.article_properties_definition)
|
|
|
|
def test_knowledge_main_flow(self):
|
|
|
|
# Patching 'now' to allow checking the order of trashed articles, as
|
|
# they are sorted using their deletion date which is based on the
|
|
# 'write_date' field
|
|
self.patch(self.env.cr, 'now', lambda: fields.Datetime.now() - timedelta(days=1))
|
|
article_1 = self.env['knowledge.article'].create({
|
|
'name': 'Article 1',
|
|
'active': False,
|
|
'to_delete': True,
|
|
})
|
|
article_1.flush_recordset()
|
|
|
|
# as the knowledge.article#_resequence method is based on write date
|
|
# force the write_date to be correctly computed
|
|
# otherwise it always returns the same value as we are in a single transaction
|
|
self.patch(self.env.cr, 'now', fields.Datetime.now)
|
|
self.env['knowledge.article'].create({
|
|
'name': 'Article 2',
|
|
'active': False,
|
|
'to_delete': True,
|
|
})
|
|
self.env['knowledge.article'].create({
|
|
'name': 'Article 3',
|
|
'internal_permission': 'write',
|
|
'parent_id': False,
|
|
'is_article_visible_by_everyone': True,
|
|
})
|
|
with self.mock_mail_gateway(), self.mock_mail_app():
|
|
self.start_tour('/web', 'knowledge_main_flow_tour', login='admin', step_delay=100)
|
|
|
|
# check our articles were correctly created
|
|
# with appropriate default values (section / internal_permission)
|
|
private_article = self.env['knowledge.article'].search([('name', '=', 'My Private Article')])
|
|
self.assertTrue(bool(private_article))
|
|
self.assertEqual(private_article.category, 'private')
|
|
self.assertEqual(private_article.internal_permission, 'none')
|
|
|
|
workspace_article = self.env['knowledge.article'].search([('name', '=', 'My Workspace Article')])
|
|
self.assertTrue(bool(workspace_article))
|
|
self.assertEqual(workspace_article.category, 'workspace')
|
|
self.assertEqual(workspace_article.internal_permission, 'write')
|
|
|
|
children_workspace_articles = workspace_article.child_ids.sorted('sequence')
|
|
self.assertEqual(len(children_workspace_articles), 2)
|
|
|
|
child_article_1 = children_workspace_articles.filtered(
|
|
lambda article: article.name == 'Child Article 1')
|
|
child_article_2 = children_workspace_articles.filtered(
|
|
lambda article: article.name == 'Child Article 2')
|
|
|
|
# as we re-ordered children, article 2 should come first
|
|
self.assertEqual(children_workspace_articles[0], child_article_2)
|
|
self.assertEqual(children_workspace_articles[1], child_article_1)
|
|
|
|
# workspace article should have one partner invited on it
|
|
invited_member = workspace_article.article_member_ids.filtered(lambda member: member.partner_id != workspace_article.create_uid.partner_id)
|
|
self.assertEqual(len(invited_member), 1)
|
|
invited_partner = invited_member.partner_id
|
|
self.assertEqual(len(invited_partner), 1)
|
|
self.assertEqual(invited_partner.name, 'micheline@knowledge.com')
|
|
self.assertEqual(invited_partner.email, 'micheline@knowledge.com')
|
|
# check that the partner received an invitation link
|
|
invitation_message = self.env['mail.message'].search([
|
|
('partner_ids', 'in', invited_partner.id)
|
|
])
|
|
self.assertEqual(len(invitation_message), 1)
|
|
self.assertIn(
|
|
workspace_article._get_invite_url(invited_partner),
|
|
self._new_mails.body_html
|
|
)
|
|
|
|
# as we re-ordered our favorites, private article should come first
|
|
article_favorites = self.env['knowledge.article.favorite'].search([])
|
|
self.assertEqual(len(article_favorites), 2)
|
|
self.assertEqual(article_favorites[0].article_id, private_article)
|
|
self.assertEqual(article_favorites[1].article_id, workspace_article)
|
|
|
|
def test_knowledge_main_flow_portal(self):
|
|
""" Same goal as 'test_knowledge_main_flow' but for a portal user.
|
|
Portal users have limited rights, they can only access articles to which they have been
|
|
given specific write access to. """
|
|
|
|
# as the knowledge.article#_resequence method is based on write date
|
|
# force the write_date to be correctly computed
|
|
# otherwise it always returns the same value as we are in a single transaction
|
|
self.patch(self.env.cr, 'now', fields.Datetime.now)
|
|
|
|
# create initial set of data:
|
|
# - one regular internal article
|
|
# - one article to which portal has access to
|
|
self.env['knowledge.article'].create([{
|
|
'name': "Internal Workspace Article",
|
|
'internal_permission': 'write',
|
|
'parent_id': False,
|
|
'is_article_visible_by_everyone': True,
|
|
}, {
|
|
'name': "Workspace Article",
|
|
'body': "<p>Content of Workspace Article</p>",
|
|
'internal_permission': 'write',
|
|
'parent_id': False,
|
|
'is_article_visible_by_everyone': True,
|
|
'article_member_ids': [(0, 0, {
|
|
'partner_id': self.user_portal.partner_id.id,
|
|
'permission': 'write',
|
|
})]
|
|
}])
|
|
|
|
self.start_tour('/knowledge/home', 'knowledge_main_flow_tour_portal', login='portal_test')
|
|
|
|
# check our articles were correctly created
|
|
# with appropriate default values (section / internal_permission)
|
|
private_article = self.env['knowledge.article'].search([('name', '=', "My Private Article")])
|
|
self.assertTrue(bool(private_article))
|
|
self.assertEqual(private_article.category, 'private')
|
|
self.assertEqual(private_article.internal_permission, 'none')
|
|
|
|
workspace_article = self.env['knowledge.article'].search([('name', '=', "Workspace Article")])
|
|
# check that workspace article's content has been properly modified
|
|
self.assertIn("Edited Content of Workspace Article", workspace_article.body,
|
|
"Portal should have been able to modify the article content as he as direct access")
|
|
|
|
children_workspace_articles = workspace_article.child_ids.sorted('sequence')
|
|
self.assertEqual(len(children_workspace_articles), 2,
|
|
"Portal should have been able to create 2 children")
|
|
|
|
# as we re-ordered our favorites, private article should come first
|
|
article_favorites = self.env['knowledge.article.favorite'].search([])
|
|
self.assertEqual(len(article_favorites), 2)
|
|
self.assertEqual(article_favorites[0].article_id, private_article)
|
|
self.assertEqual(article_favorites[1].article_id, workspace_article)
|
|
|
|
def test_knowledge_pick_emoji(self):
|
|
"""This tour will check that the emojis of the form view are properly updated
|
|
when the user picks an emoji from an emoji picker."""
|
|
self.start_tour('/web', 'knowledge_pick_emoji_tour', login='admin', step_delay=100)
|
|
|
|
def test_knowledge_cover_selector(self):
|
|
"""Check the behaviour of the cover selector when unsplash credentials
|
|
are not set.
|
|
"""
|
|
with io.BytesIO() as f:
|
|
Image.new('RGB', (50, 50)).save(f, 'PNG')
|
|
f.seek(0)
|
|
image = base64.b64encode(f.read())
|
|
attachment = self.env['ir.attachment'].create({
|
|
'name': 'odoo_logo.png',
|
|
'datas': image,
|
|
'res_model': 'knowledge.cover',
|
|
'res_id': 0,
|
|
})
|
|
self.env['knowledge.cover'].create({'attachment_id': attachment.id})
|
|
self.start_tour('/web', 'knowledge_cover_selector_tour', login='admin')
|
|
|
|
def test_knowledge_readonly_favorite(self):
|
|
"""Make sure that a user can add readonly articles to its favorites and
|
|
resequence them.
|
|
"""
|
|
articles = self.env['knowledge.article'].create([{
|
|
'name': 'Readonly Article 1',
|
|
'internal_permission': 'read',
|
|
'article_member_ids': [(0, 0, {
|
|
'partner_id': self.env.ref('base.partner_admin').id,
|
|
'permission': 'write',
|
|
})],
|
|
'is_article_visible_by_everyone': True,
|
|
}, {
|
|
'name': 'Readonly Article 2',
|
|
'internal_permission': False,
|
|
'article_member_ids': [(0, 0, {
|
|
'partner_id': self.env.ref('base.partner_admin').id,
|
|
'permission': 'write',
|
|
}), (0, 0, {
|
|
'partner_id': self.partner_demo.id,
|
|
'permission': 'read',
|
|
})],
|
|
'is_article_visible_by_everyone': True,
|
|
}])
|
|
|
|
self.start_tour('/knowledge/article/%s' % articles[0].id, 'knowledge_readonly_favorite_tour', login='demo', step_delay=100)
|
|
|
|
self.assertTrue(articles[0].with_user(self.user_demo.id).is_user_favorite)
|
|
self.assertTrue(articles[1].with_user(self.user_demo.id).is_user_favorite)
|
|
self.assertGreater(
|
|
articles[0].with_user(self.user_demo.id).user_favorite_sequence,
|
|
articles[1].with_user(self.user_demo.id).user_favorite_sequence,
|
|
)
|
|
|
|
def test_knowledge_resequence_children_of_readonly_parent_tour(self):
|
|
"""Make sure that a user can move children articles under a readonly
|
|
parent.
|
|
"""
|
|
parent = self.env['knowledge.article'].create({
|
|
'name': 'Readonly Parent',
|
|
'internal_permission': 'read',
|
|
'article_member_ids': [(0, 0, {
|
|
'partner_id': self.env.ref('base.partner_admin').id,
|
|
'permission': 'write',
|
|
})]
|
|
})
|
|
self.env['knowledge.article'].create([{
|
|
'name': 'Child 1',
|
|
'internal_permission': 'write',
|
|
'sequence': 1,
|
|
'parent_id': parent.id,
|
|
}, {
|
|
'name': 'Child 2',
|
|
'internal_permission': 'write',
|
|
'sequence': 2,
|
|
'parent_id': parent.id,
|
|
}])
|
|
self.start_tour('/knowledge/article/%s' % parent.id, 'knowledge_resequence_children_of_readonly_parent_tour', login='demo')
|
|
|
|
def test_knowledge_properties_tour(self):
|
|
"""Test article properties panel"""
|
|
parent_article = self.env['knowledge.article'].create([{
|
|
'name': 'ParentArticle',
|
|
'sequence': 1,
|
|
'is_article_visible_by_everyone': True,
|
|
}, {
|
|
'name': 'InheritPropertiesArticle',
|
|
'sequence': 2,
|
|
'is_article_visible_by_everyone': True,
|
|
}])[0]
|
|
self.env['knowledge.article'].create({
|
|
'name': 'ChildArticle',
|
|
'parent_id': parent_article.id
|
|
})
|
|
self.start_tour('/web', 'knowledge_properties_tour', login='admin', step_delay=100)
|
|
|
|
def test_knowledge_items_search_favorites_tour(self):
|
|
"""Test search favorites for items view"""
|
|
self.env['knowledge.article'].create([{'name': 'Article 1'}])
|
|
self.start_tour('/web', 'knowledge_items_search_favorites_tour', login='admin')
|
|
|
|
def test_knowledge_search_favorites_tour(self):
|
|
"""Test search favorites with searchModel state"""
|
|
self.env['knowledge.article'].create([{'name': 'Article 1'}])
|
|
self.start_tour('/web', 'knowledge_search_favorites_tour', login='admin')
|
|
|
|
@users('admin')
|
|
def test_knowledge_sidebar(self):
|
|
# This tour checks that the features of the sidebar work as expected
|
|
self.start_tour('/web', 'knowledge_sidebar_tour', login='admin', timeout=100)
|
|
|
|
# Check section create button and artice icon button
|
|
workspace_article = self.env['knowledge.article'].search([('name', '=', 'Workspace Article')])
|
|
self.assertTrue(bool(workspace_article))
|
|
self.assertEqual(workspace_article.category, 'workspace')
|
|
self.assertFalse(workspace_article.parent_id)
|
|
self.assertEqual(workspace_article.icon, '🥵')
|
|
|
|
# Check article create and icon buttons
|
|
workspace_child = self.env['knowledge.article'].search([('name', '=', 'Workspace Child')])
|
|
self.assertEqual(workspace_child.parent_id, workspace_article)
|
|
self.assertEqual(workspace_child.icon, '😬')
|
|
self.assertTrue(workspace_child.is_user_favorite)
|
|
|
|
# Check drag and drop to trash
|
|
shared_article = self.env['knowledge.article'].with_context(active_test=False).search([('name', '=', 'Shared Article')])
|
|
self.assertTrue(bool(shared_article))
|
|
self.assertEqual(shared_article.category, 'shared')
|
|
self.assertFalse(shared_article.active)
|
|
|
|
# Check favorites resequencing
|
|
private_article = self.env['knowledge.article'].search([('name', '=', 'Private Article')])
|
|
self.assertTrue(bool(private_article))
|
|
self.assertEqual(private_article.category, 'private')
|
|
self.assertFalse(private_article.parent_id)
|
|
self.assertGreater(private_article.user_favorite_sequence, workspace_child.user_favorite_sequence)
|
|
|
|
# Check articles resequencing and article icon button
|
|
private_children = private_article.child_ids.sorted('sequence')
|
|
self.assertEqual(private_children[0].name, 'Private Child 3')
|
|
self.assertEqual(private_children[0].icon, '🥶')
|
|
self.assertEqual(private_children[1].name, 'Private Child 4')
|
|
self.assertEqual(private_children[2].name, 'Private Child 1')
|
|
|
|
# Check drag and drop to other section
|
|
moved_to_share = self.env['knowledge.article'].with_context(active_test=False).search([('name', '=', 'Moved to Share')])
|
|
self.assertTrue(bool(moved_to_share))
|
|
self.assertEqual(moved_to_share.parent_id, shared_article)
|
|
self.assertEqual(moved_to_share.category, 'shared')
|
|
self.assertFalse(moved_to_share.active)
|
|
|
|
# Check drag and drop to root and to trash
|
|
private_child_2 = self.env['knowledge.article'].with_context(active_test=False).search([('name', '=', 'Private Child 2')])
|
|
self.assertTrue(bool(private_child_2))
|
|
self.assertFalse(private_child_2.parent_id)
|
|
self.assertGreater(private_child_2.sequence, private_article.sequence)
|
|
self.assertFalse(private_child_2.active)
|
|
|
|
# Check that some features are restricted with read only articles
|
|
private_article.write({
|
|
'internal_permission': 'read',
|
|
'is_article_visible_by_everyone': True,
|
|
'sequence': workspace_article.sequence+1,
|
|
})
|
|
self.start_tour('/web', 'knowledge_sidebar_readonly_tour', login='demo')
|
|
|
|
# Check that articles did not move
|
|
self.assertFalse(workspace_article.parent_id)
|
|
self.assertGreater(private_article.sequence, workspace_article.sequence)
|
|
|
|
@tagged('external', 'post_install', '-at_install')
|
|
@skipIf(not os.getenv("UNSPLASH_APP_ID") or not os.getenv("UNSPLASH_ACCESS_KEY"), "no unsplash credentials")
|
|
class TestKnowledgeUIWithUnsplash(TestKnowledgeUICommon):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super(TestKnowledgeUIWithUnsplash, cls).setUpClass()
|
|
|
|
cls.UNSPLASH_APP_ID = os.getenv("UNSPLASH_APP_ID")
|
|
cls.UNSPLASH_ACCESS_KEY = os.getenv("UNSPLASH_ACCESS_KEY")
|
|
|
|
cls.env["ir.config_parameter"].set_param("unsplash.app_id", cls.UNSPLASH_APP_ID)
|
|
cls.env["ir.config_parameter"].set_param("unsplash.access_key", cls.UNSPLASH_ACCESS_KEY)
|
|
|
|
def test_knowledge_cover_selector_unsplash(self):
|
|
"""Check the behaviour of the cover selector when unsplash credentials
|
|
are set.
|
|
"""
|
|
self.start_tour('/web', 'knowledge_random_cover_tour', login='demo')
|