[IMP] /web/login redirects if already logged in
[odoo/odoo.git] / addons / web / controllers / testing.py
1 # coding=utf-8
2 # -*- encoding: utf-8 -*-
3
4 import glob
5 import itertools
6 import json
7 import operator
8 import os
9
10 from mako.template import Template
11 from openerp.modules import module
12 from openerp import http
13 from openerp.http import request
14
15 from .main import module_topological_sort
16
17 NOMODULE_TEMPLATE = Template(u"""<!DOCTYPE html>
18 <html>
19     <head>
20         <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
21         <meta http-equiv="content-type" content="text/html; charset=utf-8" />
22         <title>OpenERP Testing</title>
23     </head>
24     <body>
25         <form action="/web/tests" method="GET">
26             <button name="mod" value="*">Run all tests</button>
27             <ul>
28             % for name, module in modules:
29                 <li>${name} <button name="mod" value="${module}">
30                     Run Tests</button></li>
31             % endfor
32             </ul>
33         </form>
34     </body>
35 </html>
36 """, default_filters=['h'])
37 NOTFOUND = Template(u"""
38 <p>Unable to find the module [${module}], please check that the module
39    name is correct and the module is on OpenERP's path.</p>
40 <a href="/web/tests">&lt;&lt; Back to tests</a>
41 """, default_filters=['h'])
42 TESTING = Template(u"""<!DOCTYPE html>
43 <html style="height: 100%">
44 <%def name="to_path(module, p)">/${module}/${p}</%def>
45 <head>
46     <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
47     <meta http-equiv="content-type" content="text/html; charset=utf-8" />
48     <title>OpenERP Web Tests</title>
49     <link rel="shortcut icon" href="/web/static/src/img/favicon.ico" type="image/x-icon"/>
50
51     <link rel="stylesheet" href="/web/static/lib/qunit/qunit.css">
52     <script src="/web/static/lib/qunit/qunit.js"></script>
53
54     <script type="text/javascript">
55         var oe_db_info = ${db_info | n};
56         // List of modules, each module is preceded by its dependencies
57         var oe_all_dependencies = ${dependencies | n};
58         QUnit.config.testTimeout = 5 * 60 * 1000;
59     </script>
60 </head>
61 <body id="oe" class="openerp">
62     <div id="qunit"></div>
63     <div id="qunit-fixture"></div>
64 </body>
65 <!-- TODO xmo please use the regular template even for testing -->
66 % for module, jss, tests, templates in files:
67     % for js in jss:
68         % if not js.endswith('/apps.js'):
69             <script src="${to_path(module, js)}"></script>
70         % endif
71     % endfor
72     % if tests or templates:
73     <script>
74         openerp.testing.current_module = "${module}";
75         % for template in templates:
76         openerp.testing.add_template("${to_path(module, template)}");
77         % endfor
78     </script>
79     % endif
80     % if tests:
81         % for test in tests:
82             <script type="text/javascript" src="${to_path(module, test)}"></script>
83         % endfor
84     % endif
85 % endfor
86 </html>
87 """, default_filters=['h'])
88
89 class TestRunnerController(http.Controller):
90
91     @http.route('/web/tests', type='http', auth="none")
92     def index(self, mod=None, **kwargs):
93         ms = module.get_modules()
94         manifests = dict(
95             (name, desc)
96             for name, desc in zip(ms, map(self.load_manifest, ms))
97             if desc # remove not-actually-openerp-modules
98         )
99
100         if not mod:
101             return NOMODULE_TEMPLATE.render(modules=(
102                 (manifest['name'], name)
103                 for name, manifest in manifests.iteritems()
104                 if any(testfile.endswith('.js')
105                        for testfile in manifest['test'])
106             ))
107         sorted_mods = module_topological_sort(dict(
108             (name, manifest.get('depends', []))
109             for name, manifest in manifests.iteritems()
110         ))
111         # to_load and to_test should be zippable lists of the same length.
112         # A falsy value in to_test indicate nothing to test at that index (just
113         # load the corresponding part of to_load)
114         to_test = sorted_mods
115         if mod != '*':
116             if mod not in manifests:
117                 return request.not_found(NOTFOUND.render(module=mod))
118             idx = sorted_mods.index(mod)
119             to_test = [None] * len(sorted_mods)
120             to_test[idx] = mod
121
122         tests_candicates = [
123             filter(lambda path: path.endswith('.js'),
124                    manifests[mod]['test'] if mod else [])
125             for mod in to_test]
126         # remove trailing test-less modules
127         tests = reversed(list(
128             itertools.dropwhile(
129                 operator.not_,
130                 reversed(tests_candicates))))
131
132         files = [
133             (mod, manifests[mod]['js'], tests, manifests[mod]['qweb'])
134             for mod, tests in itertools.izip(sorted_mods, tests)
135         ]
136
137         # if all three db_info parameters are present, send them to the page
138         db_info = dict((k, v) for k, v in kwargs.iteritems()
139                        if k in ['source', 'supadmin', 'password'])
140         if len(db_info) != 3:
141             db_info = None
142
143         return TESTING.render(files=files, dependencies=json.dumps(
144             [name for name in sorted_mods
145              if module.get_module_resource(name, 'static')
146              if manifests[name]['js']]), db_info=json.dumps(db_info))
147
148     def load_manifest(self, name):
149         manifest = module.load_information_from_description_file(name)
150         if manifest:
151             path = module.get_module_path(name)
152             manifest['js'] = list(
153                 self.expand_patterns(path, manifest.get('js', [])))
154             manifest['test'] = list(
155                 self.expand_patterns(path, manifest.get('test', [])))
156             manifest['qweb'] = list(
157                 self.expand_patterns(path, manifest.get('qweb', [])))
158         return manifest
159
160     def expand_patterns(self, root, patterns):
161         for pattern in patterns:
162             normalized_pattern = os.path.normpath(os.path.join(root, pattern))
163             for path in glob.glob(normalized_pattern):
164                 # replace OS path separators (from join & normpath) by URI ones
165                 yield path[len(root):].replace(os.path.sep, '/')
166
167     @http.route('/web/tests/set_session_value', type='json', auth="none")
168     def set_session_value(self, value):
169         request.session.some_test_value = value
170
171     @http.route('/web/tests/get_session_value', type='json', auth="none")
172     def get_session_value(self):
173         return request.session.some_test_value