288 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			288 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
| # -*- coding: utf-8 -*-
 | |
| # Copyright 2017 Camptocamp SA
 | |
| # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
 | |
| 
 | |
| from contextlib import contextmanager
 | |
| from odoo.addons.component.core import (
 | |
|     Component,
 | |
| )
 | |
| from .common import TransactionComponentRegistryCase
 | |
| from odoo.addons.component.exception import (
 | |
|     NoComponentError,
 | |
|     SeveralComponentError,
 | |
| )
 | |
| 
 | |
| 
 | |
| class TestComponent(TransactionComponentRegistryCase):
 | |
|     """ Test usage of components
 | |
| 
 | |
|     These tests are a bit more broad that mere unit tests.
 | |
|     We test the chain odoo Model -> generate a WorkContext instance -> Work
 | |
|     with Component.
 | |
| 
 | |
|     Tests are inside Odoo transactions, so we can work
 | |
|     with Odoo's env / models.
 | |
|     """
 | |
| 
 | |
|     def setUp(self):
 | |
|         super(TestComponent, self).setUp()
 | |
|         self.collection = self.env['collection.base']
 | |
| 
 | |
|         # create some Component to play with
 | |
|         class Component1(Component):
 | |
|             _name = 'component1'
 | |
|             _collection = 'collection.base'
 | |
|             _usage = 'for.test'
 | |
|             _apply_on = ['res.partner']
 | |
| 
 | |
|         class Component2(Component):
 | |
|             _name = 'component2'
 | |
|             _collection = 'collection.base'
 | |
|             _usage = 'for.test'
 | |
|             _apply_on = ['res.users']
 | |
| 
 | |
|         # build the components and register them in our
 | |
|         # test component registry
 | |
|         Component1._build_component(self.comp_registry)
 | |
|         Component2._build_component(self.comp_registry)
 | |
| 
 | |
|         # our collection, in a less abstract use case, it
 | |
|         # could be a record of 'magento.backend' for instance
 | |
|         self.collection_record = self.collection.new()
 | |
| 
 | |
|         @contextmanager
 | |
|         def get_base():
 | |
|             # Our WorkContext, it will be passed along in every
 | |
|             # components so we can share data transversally.
 | |
|             # We are working with res.partner in the following tests,
 | |
|             # unless we change it in the test.
 | |
|             with self.collection_record.work_on(
 | |
|                     'res.partner',
 | |
|                     # we use a custom registry only
 | |
|                     # for the sake of the tests
 | |
|                     components_registry=self.comp_registry
 | |
|                     ) as work:
 | |
|                 # We get the 'base' component, handy to test the base
 | |
|                 # methods component, many_components, ...
 | |
|                 yield work.component_by_name('base')
 | |
|         self.get_base = get_base
 | |
| 
 | |
|     def test_component_attrs(self):
 | |
|         """ Basic access to a Component's attribute """
 | |
|         with self.get_base() as base:
 | |
|             # as we are working on res.partner, we should get 'component1'
 | |
|             comp = base.work.component(usage='for.test')
 | |
|             # but this is not what we test here, we test the attributes:
 | |
|             self.assertEqual(self.collection_record, comp.collection)
 | |
|             self.assertEqual(base.work, comp.work)
 | |
|             self.assertEqual(self.env, comp.env)
 | |
|             self.assertEqual(self.env['res.partner'], comp.model)
 | |
| 
 | |
|     def test_component_get_by_name_same_model(self):
 | |
|         """ Use component_by_name with current working model """
 | |
|         with self.get_base() as base:
 | |
|             # we ask a component directly by it's name, considering
 | |
|             # we work with res.partner, we should get 'component1'
 | |
|             # this is ok because it's _apply_on contains res.partner
 | |
|             comp = base.component_by_name('component1')
 | |
|             self.assertEqual('component1', comp._name)
 | |
|             self.assertEqual(self.env['res.partner'], comp.model)
 | |
| 
 | |
|     def test_component_get_by_name_other_model(self):
 | |
|         """ Use component_by_name with another model """
 | |
|         with self.get_base() as base:
 | |
|             # we ask a component directly by it's name, but we
 | |
|             # want to work with 'res.users', this is ok since
 | |
|             # component2's _apply_on contains res.users
 | |
|             comp = base.component_by_name(
 | |
|                 'component2', model_name='res.users'
 | |
|             )
 | |
|             self.assertEqual('component2', comp._name)
 | |
|             self.assertEqual(self.env['res.users'], comp.model)
 | |
|             # what happens under the hood, is that a new WorkContext
 | |
|             # has been created for this model, with all the other values
 | |
|             # identical to the previous WorkContext (the one for res.partner)
 | |
|             # We can check that with:
 | |
|             self.assertNotEqual(base.work, comp.work)
 | |
|             self.assertEqual('res.partner', base.work.model_name)
 | |
|             self.assertEqual('res.users', comp.work.model_name)
 | |
| 
 | |
|     def test_component_get_by_name_wrong_model(self):
 | |
|         """ Use component_by_name with a model not in _apply_on """
 | |
|         msg = ("Component with name 'component2' can't be used "
 | |
|                "for model 'res.partner'.*")
 | |
|         with self.get_base() as base:
 | |
|             with self.assertRaisesRegex(NoComponentError, msg):
 | |
|                 # we ask for the model 'component2' but we are working
 | |
|                 # with res.partner, and it only accepts res.users
 | |
|                 base.component_by_name('component2')
 | |
| 
 | |
|     def test_component_get_by_name_not_exist(self):
 | |
|         """ Use component_by_name on a component that do not exist """
 | |
|         msg = "No component with name 'foo' found."
 | |
|         with self.get_base() as base:
 | |
|             with self.assertRaisesRegex(NoComponentError, msg):
 | |
|                 base.component_by_name('foo')
 | |
| 
 | |
|     def test_component_by_usage_same_model(self):
 | |
|         """ Use component(usage=...) on the same model """
 | |
|         # we ask for a component having _usage == 'for.test', and
 | |
|         # model being res.partner (the model in the current WorkContext)
 | |
|         with self.get_base() as base:
 | |
|             comp = base.component(usage='for.test')
 | |
|             self.assertEqual('component1', comp._name)
 | |
|             self.assertEqual(self.env['res.partner'], comp.model)
 | |
| 
 | |
|     def test_component_by_usage_other_model(self):
 | |
|         """ Use component(usage=...) on a different model (name) """
 | |
|         # we ask for a component having _usage == 'for.test', and
 | |
|         # a different model (res.users)
 | |
|         with self.get_base() as base:
 | |
|             comp = base.component(usage='for.test', model_name='res.users')
 | |
|             self.assertEqual('component2', comp._name)
 | |
|             self.assertEqual(self.env['res.users'], comp.model)
 | |
|             # what happens under the hood, is that a new WorkContext
 | |
|             # has been created for this model, with all the other values
 | |
|             # identical to the previous WorkContext (the one for res.partner)
 | |
|             # We can check that with:
 | |
|             self.assertNotEqual(base.work, comp.work)
 | |
|             self.assertEqual('res.partner', base.work.model_name)
 | |
|             self.assertEqual('res.users', comp.work.model_name)
 | |
| 
 | |
|     def test_component_by_usage_other_model_env(self):
 | |
|         """ Use component(usage=...) on a different model (instance) """
 | |
|         with self.get_base() as base:
 | |
|             comp = base.component(usage='for.test',
 | |
|                                   model_name=self.env['res.users'])
 | |
|             self.assertEqual('component2', comp._name)
 | |
|             self.assertEqual(self.env['res.users'], comp.model)
 | |
| 
 | |
|     def test_component_error_several(self):
 | |
|         """ Use component(usage=...) when more than one component match """
 | |
|         # we create a new Component with _usage 'for.test', in the same
 | |
|         # collection and no _apply_on
 | |
|         class Component3(Component):
 | |
|             _name = 'component3'
 | |
|             _collection = 'collection.base'
 | |
|             _usage = 'for.test'
 | |
| 
 | |
|         Component3._build_component(self.comp_registry)
 | |
| 
 | |
|         with self.get_base() as base:
 | |
|             with self.assertRaises(SeveralComponentError):
 | |
|                 # When a component has no _apply_on, it means it can be applied
 | |
|                 # on *any* model. Here, the candidates components would be:
 | |
|                 # component1 (because we are working with res.partner),
 | |
|                 # component3 (because it has no _apply_on so apply in any case)
 | |
|                 base.component(usage='for.test')
 | |
| 
 | |
|     def test_many_components(self):
 | |
|         """ Use many_components(usage=...) on the same model """
 | |
|         class Component3(Component):
 | |
|             _name = 'component3'
 | |
|             _collection = 'collection.base'
 | |
|             _usage = 'for.test'
 | |
| 
 | |
|         Component3._build_component(self.comp_registry)
 | |
| 
 | |
|         with self.get_base() as base:
 | |
|             comps = base.many_components(usage='for.test')
 | |
| 
 | |
|         # When a component has no _apply_on, it means it can be applied
 | |
|         # on *any* model. So here, both component1 and component3 match
 | |
|         self.assertEqual(
 | |
|             ['component1', 'component3'],
 | |
|             [c._name for c in comps]
 | |
|         )
 | |
| 
 | |
|     def test_many_components_other_model(self):
 | |
|         """ Use many_components(usage=...) on a different model (name) """
 | |
|         class Component3(Component):
 | |
|             _name = 'component3'
 | |
|             _collection = 'collection.base'
 | |
|             _apply_on = 'res.users'
 | |
|             _usage = 'for.test'
 | |
| 
 | |
|         Component3._build_component(self.comp_registry)
 | |
| 
 | |
|         with self.get_base() as base:
 | |
|             comps = base.many_components(usage='for.test',
 | |
|                                          model_name='res.users')
 | |
| 
 | |
|         self.assertEqual(
 | |
|             ['component2', 'component3'],
 | |
|             [c._name for c in comps]
 | |
|         )
 | |
| 
 | |
|     def test_many_components_other_model_env(self):
 | |
|         """ Use many_components(usage=...) on a different model (instance) """
 | |
|         class Component3(Component):
 | |
|             _name = 'component3'
 | |
|             _collection = 'collection.base'
 | |
|             _apply_on = 'res.users'
 | |
|             _usage = 'for.test'
 | |
| 
 | |
|         Component3._build_component(self.comp_registry)
 | |
| 
 | |
|         with self.get_base() as base:
 | |
|             comps = base.many_components(usage='for.test',
 | |
|                                          model_name=self.env['res.users'])
 | |
| 
 | |
|         self.assertEqual(
 | |
|             ['component2', 'component3'],
 | |
|             [c._name for c in comps]
 | |
|         )
 | |
| 
 | |
|     def test_no_component(self):
 | |
|         """ No component found for asked usage """
 | |
|         with self.get_base() as base:
 | |
|             with self.assertRaises(NoComponentError):
 | |
|                 base.component(usage='foo')
 | |
| 
 | |
|     def test_no_many_component(self):
 | |
|         """ No component found for asked usage for many_components() """
 | |
|         with self.get_base() as base:
 | |
|             self.assertEqual([], base.many_components(usage='foo'))
 | |
| 
 | |
|     def test_work_on_component(self):
 | |
|         """ Check WorkContext.component() (shortcut to Component.component) """
 | |
|         with self.get_base() as base:
 | |
|             comp = base.work.component(usage='for.test')
 | |
|             self.assertEqual('component1', comp._name)
 | |
| 
 | |
|     def test_work_on_many_components(self):
 | |
|         """ Check WorkContext.many_components()
 | |
| 
 | |
|         (shortcut to Component.many_components)
 | |
|         """
 | |
|         with self.get_base() as base:
 | |
|             comps = base.work.many_components(usage='for.test')
 | |
|             self.assertEqual('component1', comps[0]._name)
 | |
| 
 | |
|     def test_component_match(self):
 | |
|         """ Lookup with match method """
 | |
|         class Foo(Component):
 | |
|             _name = 'foo'
 | |
|             _collection = 'collection.base'
 | |
|             _usage = 'speaker'
 | |
|             _apply_on = ['res.partner']
 | |
| 
 | |
|             @classmethod
 | |
|             def _component_match(cls, work):
 | |
|                 return False
 | |
| 
 | |
|         class Bar(Component):
 | |
|             _name = 'bar'
 | |
|             _collection = 'collection.base'
 | |
|             _usage = 'speaker'
 | |
|             _apply_on = ['res.partner']
 | |
| 
 | |
|         self._build_components(Foo, Bar)
 | |
| 
 | |
|         with self.get_base() as base:
 | |
|             # both components would we returned without the
 | |
|             # _component_match method
 | |
|             comp = base.component(usage='speaker',
 | |
|                                   model_name=self.env['res.partner'])
 | |
|             self.assertEqual('bar', comp._name)
 |