diff options
| author | Frédéric van der Essen <fva@openerp.com> | 2014-02-07 16:50:55 +0100 |
|---|---|---|
| committer | Frédéric van der Essen <fva@openerp.com> | 2014-02-07 16:50:55 +0100 |
| commit | 4232de7bf2f19fd01fc9dcccf421eaeba819e586 (patch) | |
| tree | 5ee2ab75842426634cd0bbc24ed201ae8f08729d | |
| parent | 34bc74ea422230fd052382e16c0c5e3fcdd26d27 (diff) | |
| parent | c254b7ae2cee1a240380880d76f10aeb715a5b37 (diff) | |
[MERGE] from saas-3
bzr revid: fva@openerp.com-20140207155055-3z8o3fvx2zkoqe91
49 files changed, 869 insertions, 558 deletions
diff --git a/addons/auth_oauth_signup/__openerp__.py b/addons/auth_oauth_signup/__openerp__.py index 8fc93f6c920..d3660f7c052 100644 --- a/addons/auth_oauth_signup/__openerp__.py +++ b/addons/auth_oauth_signup/__openerp__.py @@ -2,7 +2,7 @@ ############################################################################## # # OpenERP, Open Source Management Solution -# Copyright (C) 2010-2012 OpenERP SA (<http://openerp.com>). +# Copyright (C) 2010-2014 OpenERP SA (<http://openerp.com>). # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -30,7 +30,9 @@ Allow users to sign up through OAuth2 Provider. 'author': 'OpenERP SA', 'website': 'http://www.openerp.com', 'depends': ['auth_oauth', 'auth_signup'], - 'data': [], + 'data': [ + 'views/auth_oauth_signup.xml', + ], 'js': [], 'css': [], 'qweb': [], diff --git a/addons/auth_oauth_signup/views/auth_oauth_signup.xml b/addons/auth_oauth_signup/views/auth_oauth_signup.xml new file mode 100644 index 00000000000..e6735ebd58a --- /dev/null +++ b/addons/auth_oauth_signup/views/auth_oauth_signup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- vim:et:si:ts=2:sts=2:sw=2 --> +<openerp> + <data> + <template id="auth_oauth_signup.signup" inherit_id="auth_signup.signup" name="OAuth Signup buttons"> + <xpath expr="//button[@type='submit']" position="before"> + <div class="pull-right"> + <div t-foreach="providers" t-as="p"> + <a t-att-href="p['auth_link']" class="btn btn-link"> + <i t-att-class="p['css_class']"/> + <t t-esc="p['body']"/> + </a> + </div> + </div> + </xpath> + </template> + </data> +</openerp> diff --git a/addons/auth_signup/views/auth_signup_login.xml b/addons/auth_signup/views/auth_signup_login.xml index bdc093e3c27..a279528e9ca 100644 --- a/addons/auth_signup/views/auth_signup_login.xml +++ b/addons/auth_signup/views/auth_signup_login.xml @@ -5,8 +5,8 @@ <data> <template id="auth_signup.login" inherit_id="web.login" name="Sign up - Reset Password"> <xpath expr="//button[@type='submit']" position="before"> - <a t-if="signup" t-attf-href="?mode=signup{{ '&debug' if debug else '' }}" class="btn btn-link pull-right">Sign up</a> - <a t-if="reset" t-attf-href="?mode=reset{{ '&debug' if debug else '' }}" class="btn btn-link pull-right">Reset Password</a> + <a t-if="signup" t-attf-href="?{{ keep_query('*', mode='signup') }}" class="btn btn-link pull-right">Sign up</a> + <a t-if="reset" t-attf-href="?{{ keep_query('*', mode='reset') }}" class="btn btn-link pull-right">Reset Password</a> </xpath> </template> @@ -29,7 +29,7 @@ <t t-set="reset_without_token" t-value="mode == 'reset' and not token"/> - <form class="oe_signup_form" role="form" t-attf-action="/web/login{{ '?debug' if debug else '' }}" method="post"> + <form class="oe_signup_form" role="form" method="post"> <t t-call="web.database_select"/> <div class="form-group field-name" t-if="not reset_without_token"> @@ -68,7 +68,7 @@ <input type="hidden" name="mode" t-att-value="mode"/> <input type="hidden" name="token" t-att-value="token"/> <div class="clearfix oe_login_buttons"> - <a href="/web/login" class="btn btn-link pull-right">Back to Login</a> + <a t-attf-href="?{{ keep_query('*', mode='login') }}" class="btn btn-link pull-right">Back to Login</a> <button type="submit" class="btn btn-primary pull-left"> <t t-if="mode == 'signup'">Sign up</t> <t t-if="mode == 'reset'">Reset password</t> diff --git a/addons/email_template/wizard/mail_compose_message_view.xml b/addons/email_template/wizard/mail_compose_message_view.xml index b7c57af2679..44145114fff 100644 --- a/addons/email_template/wizard/mail_compose_message_view.xml +++ b/addons/email_template/wizard/mail_compose_message_view.xml @@ -28,7 +28,8 @@ <div>Use template <!--FIX: To avoid css issue of many2one field in footer temporary used oe_form (BUG:1152464)--> <field name="template_id" nolabel="1" class='oe_inline' - on_change="onchange_template_id(template_id, composition_mode, model, res_id, context)" domain="[('model_id.model','=',model)]"/> + on_change="onchange_template_id(template_id, composition_mode, model, res_id, context)" domain="[('model_id.model','=',model)]" + context="{'default_model': model, 'default_body_html': body, 'default_subject': subject}"/> </div> <button icon="/email_template/static/src/img/email_template_save.png" type="object" name="save_as_template" string="Save as new template" class="oe_link" diff --git a/addons/gamification/models/challenge.py b/addons/gamification/models/challenge.py index ae27aa96719..fec8e04c4b7 100644 --- a/addons/gamification/models/challenge.py +++ b/addons/gamification/models/challenge.py @@ -387,6 +387,7 @@ class gamification_challenge(osv.Model): end_date = challenge.end_date for line in challenge.line_ids: + # FIXME: allow to restrict to a subset of users for user in challenge.user_ids: goal_obj = self.pool.get('gamification.goal') @@ -400,8 +401,9 @@ class gamification_challenge(osv.Model): # resume canceled goals domain.append(('state', '=', 'canceled')) canceled_goal_ids = goal_obj.search(cr, uid, domain, context=context) - goal_obj.write(cr, uid, canceled_goal_ids, {'state': 'inprogress'}, context=context) - goal_obj.update(cr, uid, canceled_goal_ids, context=context) + if canceled_goal_ids: + goal_obj.write(cr, uid, canceled_goal_ids, {'state': 'inprogress'}, context=context) + goal_obj.update(cr, uid, canceled_goal_ids, context=context) # skip to next user continue @@ -516,13 +518,6 @@ class gamification_challenge(osv.Model): domain.append(('user_id', '=', user_id)) sorting = goal_obj._order limit = 1 - # initialise in case search returns no results - line_data.update({ - 'id': 0, - 'current': 0, - 'completeness': 0, - 'state': 'draft', - }) else: line_data.update({ 'own_goal_id': False, @@ -559,7 +554,8 @@ class gamification_challenge(osv.Model): 'completeness': goal.completeness, 'state': goal.state, }) - res_lines.append(line_data) + if goal_ids: + res_lines.append(line_data) return res_lines ##### Reporting ##### diff --git a/addons/gamification/models/goal.py b/addons/gamification/models/goal.py index 8792e219d71..8e79c43c8a6 100644 --- a/addons/gamification/models/goal.py +++ b/addons/gamification/models/goal.py @@ -163,7 +163,8 @@ class gamification_goal(osv.Model): string="Challenge", type='many2one', relation='gamification.challenge', - store=True), + store=True, readonly=True, + help="Challenge that generated the goal, assign challenge to users to generate goals with a value in this field."), 'start_date': fields.date('Start Date'), 'end_date': fields.date('End Date'), # no start and end = always active 'target_goal': fields.float('To Reach', diff --git a/addons/gamification/models/res_users.py b/addons/gamification/models/res_users.py index 408ddddf082..d25d725a4ac 100644 --- a/addons/gamification/models/res_users.py +++ b/addons/gamification/models/res_users.py @@ -42,6 +42,7 @@ class res_users_gamification_group(osv.Model): challenge_ids = challenge_obj.search(cr, uid, [('autojoin_group_id', 'in', user_group_ids)], context=context) if challenge_ids: challenge_obj.write(cr, uid, challenge_ids, {'user_ids': [(4, user_id) for user_id in ids]}, context=context) + challenge_obj.generate_goals_from_challenge(cr, uid, challenge_ids, context=context) return write_res def create(self, cr, uid, vals, context=None): @@ -56,6 +57,7 @@ class res_users_gamification_group(osv.Model): challenge_ids = challenge_obj.search(cr, uid, [('autojoin_group_id', 'in', user_group_ids)], context=context) if challenge_ids: challenge_obj.write(cr, uid, challenge_ids, {'user_ids': [(4, write_res)]}, context=context) + challenge_obj.generate_goals_from_challenge(cr, uid, challenge_ids, context=context) return write_res # def get_goals_todo_info(self, cr, uid, context=None): @@ -83,13 +85,15 @@ class res_users_gamification_group(osv.Model): challenge_ids = challenge_obj.search(cr, uid, [('user_ids', 'in', uid), ('state', '=', 'inprogress')], context=context) for challenge in challenge_obj.browse(cr, uid, challenge_ids, context=context): # serialize goals info to be able to use it in javascript - all_goals_info.append({ - 'id': challenge.id, - 'name': challenge.name, - 'visibility_mode': challenge.visibility_mode, - 'currency': user.company_id.currency_id.id, - 'lines': challenge_obj._get_serialized_challenge_lines(cr, uid, challenge, user_id, restrict_top=MAX_VISIBILITY_RANKING, context=context), - }) + lines = challenge_obj._get_serialized_challenge_lines(cr, uid, challenge, user_id, restrict_top=MAX_VISIBILITY_RANKING, context=context) + if lines: + all_goals_info.append({ + 'id': challenge.id, + 'name': challenge.name, + 'visibility_mode': challenge.visibility_mode, + 'currency': user.company_id.currency_id.id, + 'lines': lines, + }) return all_goals_info @@ -129,4 +133,5 @@ class res_groups_gamification_group(osv.Model): challenge_ids = challenge_obj.search(cr, uid, [('autojoin_group_id', 'in', ids)], context=context) if challenge_ids: challenge_obj.write(cr, uid, challenge_ids, {'user_ids': [(4, user_id) for user_id in user_ids]}, context=context) + challenge_obj.generate_goals_from_challenge(cr, uid, challenge_ids, context=context) return write_res diff --git a/addons/gamification/views/goal.xml b/addons/gamification/views/goal.xml index a269f710c02..1c408e3ab11 100644 --- a/addons/gamification/views/goal.xml +++ b/addons/gamification/views/goal.xml @@ -55,7 +55,7 @@ <group string="Reference"> <field name="definition_id" on_change="on_change_definition_id(definition_id)" attrs="{'readonly':[('state','!=','draft')]}"/> <field name="user_id" attrs="{'readonly':[('state','!=','draft')]}"/> - <field name="challenge_id" attrs="{'readonly':[('state','!=','draft')]}"/> + <field name="challenge_id" /> </group> <group string="Schedule"> <field name="start_date" attrs="{'readonly':[('state','!=','draft')]}"/> @@ -280,8 +280,8 @@ <!-- menus in settings - technical feature required --> <menuitem id="gamification_menu" name="Gamification Tools" parent="base.menu_administration" groups="base.group_no_one" /> - <menuitem id="gamification_goal_menu" parent="gamification_menu" action="goal_list_action" sequence="0"/> - <menuitem id="gamification_challenge_menu" parent="gamification_menu" action="challenge_list_action" sequence="10"/> + <menuitem id="gamification_challenge_menu" parent="gamification_menu" action="challenge_list_action" sequence="0"/> + <menuitem id="gamification_goal_menu" parent="gamification_menu" action="goal_list_action" sequence="10"/> <menuitem id="gamification_definition_menu" parent="gamification_menu" action="goal_definition_list_action" sequence="20"/> <menuitem id="gamification_badge_menu" parent="gamification_menu" action="badge_list_action" sequence="30"/> diff --git a/addons/hr_gamification/__openerp__.py b/addons/hr_gamification/__openerp__.py index c75a4454437..14925421cb1 100644 --- a/addons/hr_gamification/__openerp__.py +++ b/addons/hr_gamification/__openerp__.py @@ -37,4 +37,5 @@ Badge received are displayed on the user profile. 'views/gamification.xml', ], 'js': ['static/src/js/gamification.js'], + 'auto_install': True, } diff --git a/addons/hr_gamification/views/gamification.xml b/addons/hr_gamification/views/gamification.xml index e2d45948b95..60c758a717d 100644 --- a/addons/hr_gamification/views/gamification.xml +++ b/addons/hr_gamification/views/gamification.xml @@ -50,7 +50,7 @@ <field name="view_type">form</field> <field name="name">Goals History</field> <field name="view_mode">tree,kanban</field> - <field name="context">{'search_default_group_by_user': True, 'search_default_group_by_type': True}</field> + <field name="context">{'search_default_group_by_user': True, 'search_default_group_by_definition': True}</field> <field name="domain">[('challenge_id.category', '=', 'hr')]</field> <field name="help" type="html"> <p class="oe_view_nocontent_create"> diff --git a/addons/website/controllers/main.py b/addons/website/controllers/main.py index 0a3f717740f..a6d8f2982f9 100644 --- a/addons/website/controllers/main.py +++ b/addons/website/controllers/main.py @@ -179,6 +179,7 @@ class Website(openerp.addons.web.controllers.main.Home): result.append({ 'name': v.inherit_option_id.name, 'id': v.id, + 'xml_id': v.xml_id, 'inherit_id': v.inherit_id.id, 'header': True, 'active': False @@ -187,6 +188,7 @@ class Website(openerp.addons.web.controllers.main.Home): result.append({ 'name': v.name, 'id': v.id, + 'xml_id': v.xml_id, 'inherit_id': v.inherit_id.id, 'header': False, 'active': (v.inherit_id.id == v.inherit_option_id.id) or (not optional and v.inherit_id.id) diff --git a/addons/website/models/ir_http.py b/addons/website/models/ir_http.py index 6948f102fec..46b9efc15a8 100644 --- a/addons/website/models/ir_http.py +++ b/addons/website/models/ir_http.py @@ -167,5 +167,5 @@ class PageConverter(werkzeug.routing.PathConverter): for view in views: xid = xids[view['id']] - if xid and (not query or query in xid): + if xid and (not query or query.lower() in xid.lower()): yield xid diff --git a/addons/website/models/ir_ui_view.py b/addons/website/models/ir_ui_view.py index 95e2179c9ec..f8c0eadbfa9 100644 --- a/addons/website/models/ir_ui_view.py +++ b/addons/website/models/ir_ui_view.py @@ -1,8 +1,12 @@ # -*- coding: utf-8 -*- import copy +import simplejson +import werkzeug from lxml import etree, html +from openerp.addons.website.models import website +from openerp.http import request from openerp.osv import osv, fields class view(osv.osv): @@ -117,6 +121,42 @@ class view(osv.osv): return arch + def render(self, cr, uid, id_or_xml_id, values=None, engine='ir.qweb', context=None): + if getattr(request, 'website_enabled', False): + engine='website.qweb' + + if isinstance(id_or_xml_id, list): + id_or_xml_id = id_or_xml_id[0] + if isinstance(id_or_xml_id, (int, long)): + id_or_xml_id = self.get_view_xmlid(cr, uid, id_or_xml_id) + + if not context: + context = {} + + qcontext = context.copy() + qcontext.update( + website=request.website, + url_for=website.url_for, + slug=website.slug, + res_company=request.website.company_id, + user_id=self.pool.get("res.users").browse(cr, uid, uid), + ) + + # add some values + if values: + qcontext.update(values) + + # in edit mode ir.ui.view will tag nodes + context['inherit_branding'] = qcontext['editable'] + + view_obj = request.website.get_template(id_or_xml_id) + if 'main_object' not in qcontext: + qcontext['main_object'] = view_obj + + values = qcontext + + return super(view, self).render(cr, uid, id_or_xml_id, values=values, engine=engine, context=context) + def save(self, cr, uid, res_id, value, xpath=None, context=None): """ Update a view section. The view section may embed fields to write diff --git a/addons/website/models/website.py b/addons/website/models/website.py index 26cf4822a33..e43efa6f0a3 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -import fnmatch import inspect import itertools import logging @@ -7,7 +6,6 @@ import math import re import urlparse -import simplejson import werkzeug import werkzeug.exceptions import werkzeug.wrappers @@ -24,17 +22,6 @@ from openerp.addons.web.http import request, LazyResponse logger = logging.getLogger(__name__) -def keep_query(*args, **kw): - if not args and not kw: - args = ('*',) - params = kw.copy() - query_params = frozenset(werkzeug.url_decode(request.httprequest.query_string).keys()) - for keep_param in args: - for param in fnmatch.filter(query_params, keep_param): - if param not in params and param in request.params: - params[param] = request.params[param] - return werkzeug.urls.url_encode(params) - def url_for(path_or_uri, lang=None): if isinstance(path_or_uri, unicode): path_or_uri = path_or_uri.encode('utf-8') @@ -225,7 +212,6 @@ class website(osv.osv): request.redirect = lambda url: werkzeug.utils.redirect(url_for(url)) request.context.update( - is_master_lang=is_master_lang, editable=is_website_publisher, translatable=not is_master_lang, ) @@ -238,37 +224,8 @@ class website(osv.osv): return self.pool["ir.ui.view"].browse(cr, uid, view_id, context=context) def _render(self, cr, uid, ids, template, values=None, context=None): - user = self.pool.get("res.users") - if not context: - context = {} - - # Take a context - qweb_values = context.copy() - # add some values - if values: - qweb_values.update(values) - # fill some defaults - qweb_values.update( - request=request, - json=simplejson, - website=request.website, - url_for=url_for, - keep_query=keep_query, - slug=slug, - res_company=request.website.company_id, - user_id=user.browse(cr, uid, uid), - quote_plus=werkzeug.url_quote_plus, - ) - qweb_values.setdefault('editable', False) - - # in edit mode ir.ui.view will tag nodes - context['inherit_branding'] = qweb_values['editable'] - - view = self.get_template(cr, uid, ids, template) - - if 'main_object' not in qweb_values: - qweb_values['main_object'] = view - return view.render(qweb_values, engine='website.qweb', context=context) + # TODO: remove this. (just kept for backward api compatibility for saas-3) + return self.pool['ir.ui.view'].render(cr, uid, template, values=values, context=context) def render(self, cr, uid, ids, template, values=None, status_code=None, context=None): def callback(template, values, context): diff --git a/addons/website/static/src/css/website.css b/addons/website/static/src/css/website.css index 6610a98d0f1..291051a0986 100644 --- a/addons/website/static/src/css/website.css +++ b/addons/website/static/src/css/website.css @@ -1,3 +1,4 @@ +@charset "utf-8"; /* THIS CSS FILE IS FOR WEBSITE THEMING CUSTOMIZATION ONLY * * css for editor buttons, openerp widget included in the website and other @@ -281,12 +282,16 @@ ul.nav-stacked > li > a { } /* ---- SNIPPETS --- */ +.oe_img_bg { + background-size: 100%; +} + [data-snippet-id], .colmd, .hr, .blockquote { overflow: hidden; } @media (max-width: 400px) { - [data-snippet-id] { + [data-snippet-id]:not([data-snippet-id="carousel"]) { height: auto !important; } } @@ -295,7 +300,7 @@ ul.nav-stacked > li > a { } .carousel-inner .item { height: 100%; - background-size: 100%; + background-size: cover; } .carousel .carousel-caption { @@ -365,7 +370,7 @@ div.carousel[data-snippet-id="slider"] .carousel-indicators .active { .parallax { position: relative; - background-size: 100%; + background-size: cover; display: table; width: 100%; min-height: 100px; @@ -445,10 +450,6 @@ div.carousel[data-snippet-id="slider"] .carousel-indicators .active { } /* Misc */ -.oe_img_bg { - background-size: 100%; -} - .texttop { vertical-align: top; } diff --git a/addons/website/static/src/css/website.sass b/addons/website/static/src/css/website.sass index 2c5f691fae6..f4c0b338b6a 100644 --- a/addons/website/static/src/css/website.sass +++ b/addons/website/static/src/css/website.sass @@ -228,19 +228,24 @@ ul.nav-stacked > li > a a color: white + /* ---- SNIPPETS --- */ + +.oe_img_bg + background-size: 100% + [data-snippet-id],.colmd,.hr,.blockquote overflow: hidden @media (max-width: 400px) - [data-snippet-id] + [data-snippet-id]:not([data-snippet-id="carousel"]) height: auto !important .carousel-inner height: 100% .item height: 100% - background-size: 100% + background-size: cover .carousel .carousel-caption left: auto @@ -300,7 +305,7 @@ div.carousel[data-snippet-id="slider"] .parallax position: relative - background-size: 100% + background-size: cover display: table width: 100% min-height: 100px @@ -362,9 +367,6 @@ div.carousel[data-snippet-id="slider"] /* Misc */ -.oe_img_bg - background-size: 100% - .texttop vertical-align: top diff --git a/addons/website/static/src/js/website.editor.js b/addons/website/static/src/js/website.editor.js index e5ba5018316..1e8246366a9 100644 --- a/addons/website/static/src/js/website.editor.js +++ b/addons/website/static/src/js/website.editor.js @@ -414,6 +414,7 @@ openerp.jsonRpc('/website/customize_template_get', 'call', { 'xml_id': view_name }).then( function(result) { _.each(result, function (item) { + if (item.xml_id === "website.debugger" && !window.location.search.match(/[&?]debug(&|$)/)) return; if (item.header) { menu.append('<li class="dropdown-header">' + item.name + '</li>'); } else { @@ -1082,7 +1083,7 @@ } else { // Create the page, get the URL back done = $.get(_.str.sprintf( - '/pagenew/%s?noredirect=1', encodeURI(data.id))) + '/website/add/%s?noredirect=1', encodeURI(data.id))) .then(function (response) { self.make_link(response, false, data.id); }); diff --git a/addons/website/static/src/js/website.snippets.animation.js b/addons/website/static/src/js/website.snippets.animation.js index 60c5aea49e2..048cf20231f 100644 --- a/addons/website/static/src/js/website.snippets.animation.js +++ b/addons/website/static/src/js/website.snippets.animation.js @@ -105,7 +105,7 @@ var offset = 0; var padding = parseInt($(document.body).css("padding-top")); if (speed < 1) { - var inner_offset = self.$target.outerHeight() - this.height / this.width * document.body.clientWidth; + var inner_offset = - self.$target.outerHeight() + this.height / this.width * document.body.clientWidth; var outer_offset = self.$target.offset().top - (document.body.clientHeight - self.$target.outerHeight()) - padding; offset = - outer_offset * speed + inner_offset; } else { diff --git a/addons/website/static/src/js/website.snippets.editor.js b/addons/website/static/src/js/website.snippets.editor.js index 8a3e7a4421d..e45bee9685b 100644 --- a/addons/website/static/src/js/website.snippets.editor.js +++ b/addons/website/static/src/js/website.snippets.editor.js @@ -9,7 +9,7 @@ var self = this; $("[data-oe-model]").on('click', function (event) { var $this = $(event.srcElement); - var tag = $this[0].tagName.toLowerCase(); + var tag = $this[0] && $this[0].tagName.toLowerCase(); if (!(tag === 'a' || tag === "button") && !$this.parents("a, button").length) { self.$('[data-action="edit"]').parent().effect('bounce', {distance: 18, times: 5}, 250); } @@ -416,6 +416,25 @@ } setTimeout(function () { $("#oe_snippets").trigger('snippet-dropped', $target); + + // reset snippet for rte + $target.removeData("snippet-editor"); + if ($target.data("overlay")) { + $target.data("overlay").remove(); + $target.removeData("overlay"); + } + self.create_overlay($target); + $target.find("[data-snippet-id]").each(function () { + var $snippet = $(this); + $snippet.removeData("snippet-editor"); + if ($snippet.data("overlay")) { + $snippet.data("overlay").remove(); + $snippet.removeData("overlay"); + } + self.create_overlay($snippet); + }); + // end + self.make_active($target); },0); } else { diff --git a/addons/website/static/src/js/website.tests.js b/addons/website/static/src/js/website.tests.js deleted file mode 100644 index 977dc8e1848..00000000000 --- a/addons/website/static/src/js/website.tests.js +++ /dev/null @@ -1,40 +0,0 @@ -var tests = {}; -var droptest = function () { - var errors = []; - for (var snippet_id in tests) { - if (!tests[snippet_id]['activated'] || !tests[snippet_id]['dropped']){ - console.log("Can't dropped or activated snippet: " + snippet_id); - } - } - if (errors.length) { - console.log(tests); - throw new Error("Can't dropped or activated at least one snippet"); - } - $("#oe_snippets").off('snippet-activated snippet-dropped'); -}; -var droptesttime = setTimeout(droptest,0); - -$("#oe_snippets").off('snippet-activated snippet-dropped') - .on('snippet-activated', function (event, dom) { - tests[$(dom).data('src-snippet-id')]['activated'] = true; - clearTimeout(droptesttime); - droptesttime = setTimeout(droptest,0); - }) - .on('snippet-dropped', function (event, dom, src_snipped_id) { - tests[$(dom).data('src-snippet-id')]['dropped'] = true; - clearTimeout(droptesttime); - droptesttime = setTimeout(droptest,0); - }); -var $thumbnails = $('#oe_snippets div.oe_snippet[data-snippet-id] .oe_snippet_thumbnail'); -$thumbnails.each(function () { - var $thumbnail = $(this); - tests[$thumbnail.parent().data('snippet-id')] = {}; - var position = $thumbnail.position(); - $thumbnail.trigger( $.Event( "mousedown", { which: 1, pageX: position.left, pageY: position.top } ) ); - $thumbnail.trigger( $.Event( "mousemove", { which: 1, pageX: position.left+100, pageY: position.top+100 } ) ); - $first_drop = $(".oe_drop_zone").first(); - position = $first_drop.position(); - $first_drop.trigger( $.Event( "mouseup", { which: 1, pageX: position.left+20, pageY: position.top+20 } ) ); - clearTimeout(droptesttime); - droptesttime = setTimeout(droptest,0); -}); diff --git a/addons/website/static/src/js/website.tour.banner.js b/addons/website/static/src/js/website.tour.banner.js index b079c4a670c..c5b04091b40 100644 --- a/addons/website/static/src/js/website.tour.banner.js +++ b/addons/website/static/src/js/website.tour.banner.js @@ -2,113 +2,112 @@ 'use strict'; var website = openerp.website; + var _t = openerp._t; website.EditorBar.include({ start: function () { - this.registerTour(new website.BannerTour(this)); + this.registerTour(new website.Tour.Banner(this)); return this._super(); }, }); - website.BannerTour = website.Tour.extend({ + website.Tour.Banner = website.Tour.extend({ id: 'banner', name: "Insert a banner", path: '/', - init: function (editor) { + init: function () { var self = this; self.steps = [ { - title: "Welcome to your website!", - content: "This tutorial will guide you to build your home page. We will start by adding a banner.", - template: self.popover({ next: "Start Tutorial", end: "Skip It" }), - backdrop: true, + title: _t("Welcome to your website!"), + content: _t("This tutorial will guide you to build your home page. We will start by adding a banner."), + popover: { next: _t("Start Tutorial"), end: _t("Skip It") }, }, { waitNot: '.popover.tour', element: 'button[data-action=edit]', placement: 'bottom', - title: "Edit this page", - content: "Every page of your website can be modified through the <i>Edit</i> button.", - template: self.popover({ fixed: true }), + title: _t("Edit this page"), + content: _t("Every page of your website can be modified through the <i>Edit</i> button."), + popover: { fixed: true }, }, { element: 'button[data-action=snippet]', placement: 'bottom', - title: "Insert building blocks", - content: "To add content in a page, you can insert building blocks.", - template: self.popover({ fixed: true }), + title: _t("Insert building blocks"), + content: _t("To add content in a page, you can insert building blocks."), + popover: { fixed: true }, }, { snippet: 'carousel', placement: 'bottom', - title: "Drag & Drop a Banner", - content: "Drag the Banner block and drop it in your page.", - template: self.popover({ fixed: true }), + title: _t("Drag & Drop a Banner"), + content: _t("Drag the Banner block and drop it in your page."), + popover: { fixed: true }, }, { waitFor: '.oe_overlay_options .oe_options:visible', element: '#wrap [data-snippet-id=carousel]:first .carousel-caption', - sampleText: 'My Title', placement: 'top', - title: "Customize banner's text", - content: "Click in the text and start editing it. Click continue once it's done.", - template: self.popover({ next: "Continue" }), + title: _t("Customize banner's text"), + content: _t("Click in the text and start editing it. Click continue once it's done."), + popover: { next: _t("Continue") }, }, { element: '.oe_overlay_options .oe_options', placement: 'left', - title: "Customize the banner", - content: "Customize any block through this menu. Try to change the background of the banner.", - template: self.popover({ next: "Continue" }), + title: _t("Customize the banner"), + content: _t("Customize any block through this menu. Try to change the background of the banner."), + popover: { next: _t("Continue") }, }, { waitNot: '.popover.tour', element: 'button[data-action=snippet]', placement: 'bottom', - title: "Add Another Block", - content: "Let's add another building block to your page.", - template: self.popover({ fixed: true }), + title: _t("Add Another Block"), + content: _t("Let's add another building block to your page."), + popover: { fixed: true }, }, { snippet: 'three-columns', placement: 'bottom', - title: "Drag & Drop a Block", - content: "Drag the <em>'3 Columns'</em> block and drop it below the banner.", - template: self.popover({ fixed: true }), + title: _t("Drag & Drop a Block"), + content: _t("Drag the <em>'3 Columns'</em> block and drop it below the banner."), + popover: { fixed: true }, }, { waitFor: '.oe_overlay_options .oe_options:visible', element: 'button[data-action=save]', placement: 'right', - title: "Save your modifications", - content: "Publish your page by clicking on the <em>'Save'</em> button.", - template: self.popover({ fixed: true }), + title: _t("Save your modifications"), + content: _t("Publish your page by clicking on the <em>'Save'</em> button."), + popover: { fixed: true }, }, { waitFor: 'button[data-action=edit]:visible', - title: "Congratulation!", - content: "Your homepage has been updated.", - template: self.popover({ next: "Continue" }), + title: _t("Congratulation!"), + content: _t("Your homepage has been updated."), + popover: { next: _t("Continue") }, }, { waitNot: '.popover.tour', element: 'a[data-action=show-mobile-preview]', placement: 'bottom', - title: "Test Your Mobile Version", - content: "Let's check how your homepage looks like on mobile devices.", - template: self.popover({ fixed: true }), + title: _t("Test Your Mobile Version"), + content: _t("Let's check how your homepage looks like on mobile devices."), + popover: { fixed: true }, }, { element: 'button[data-dismiss=modal]', placement: 'right', - title: "Close Mobile Preview", - content: "Scroll in the mobile preview to test the rendering. Once it's ok, close this dialog.", + title: _t("Close Mobile Preview"), + content: _t("Scroll in the mobile preview to test the rendering. Once it's ok, close this dialog."), }, { waitNot: '.modal', - title: "Congrats", - content: "Congratulation. This tour is finished.", - template: self.popover({ fixed: true, next: "Close Tutorial" }), + title: _t("Congrats"), + content: _t("Congratulation. This tour is finished."), + popover: { fixed: true, next: _t("Close Tutorial") }, }, ]; return this._super(); diff --git a/addons/website/static/src/js/website.tour.js b/addons/website/static/src/js/website.tour.js index 1db813e4cd4..408a4c01539 100644 --- a/addons/website/static/src/js/website.tour.js +++ b/addons/website/static/src/js/website.tour.js @@ -2,8 +2,11 @@ 'use strict'; var website = openerp.website; + +if (typeof QWeb2 !== "undefined") website.add_template_file('/website/static/src/xml/website.tour.xml'); +if (website.EditorBar) website.EditorBar.include({ tours: [], start: function () { @@ -17,31 +20,11 @@ website.EditorBar.include({ }); menu.append($menuItem); }); - - this.waitRTEReady = false; - this.on('rte:called', this, function () {self.waitRTEReady = true; }); - this.on('rte:ready', this, function () {self.waitRTEReady = false;}); - - var res = this._super(); - website.Tour.waitReady.call(this, this.testRunning); - return res; + return this._super(); }, registerTour: function (tour) { website.Tour.add(tour); this.tours.push(tour); - }, - testRunning: function () { - if (this.waitRTEReady) { - this.on('rte:ready', this, function () { - website.Tour.each(function () { - this.running(); - }); - }); - } else { - website.Tour.each(function () { - this.running(); - }); - } } }); @@ -78,23 +61,12 @@ $.ajaxSetup({ } }); - website.Tour = openerp.Class.extend({ steps: [], defaultDelay: 50, //ms + defaultOverLaps: 5000, //ms localStorage: window.localStorage, - init: function (url) { - this.tour = new Tour({ - name: this.id, - storage: this.tourStorage, - keyboard: false, - template: this.popover(), - onHide: function () { - window.scrollTo(0, 0); - } - }); - this.registerSteps(); - }, + init: function () {}, run: function (automatic) { this.reset(); @@ -105,9 +77,8 @@ website.Tour = openerp.Class.extend({ website.Tour.busy = true; - if (automatic) { - this.localStorage.setItem("tour-"+this.id+"-test-automatic", true); - } + this.localStorage.setItem("tour-"+this.id+"-test-automatic", automatic); + this.automatic = automatic; if (this.path) { // redirect to begin of the tour in function of the language @@ -123,12 +94,13 @@ website.Tour = openerp.Class.extend({ website.Tour.waitReady.call(this, function () {self._running();}); }, running: function () { + var self = this; if (+this.localStorage.getItem("tour-"+this.id+"-test") >= this.steps.length-1) { this.endTour(); return; } - if (website.Tour.is_busy() || !this.testPathUrl()) return; + if (website.Tour.is_busy()) return; // launch tour with url this.checkRunningUrl(); @@ -136,6 +108,21 @@ website.Tour = openerp.Class.extend({ // mark tour as busy (only one test running) if (this.localStorage.getItem("tour-"+this.id+"-test") != null) { website.Tour.busy = true; + this.automatic = !!this.localStorage.getItem("tour-"+this.id+"-test-automatic"); + } + + if (!this.testPathUrl()) { + if (this.automatic) { + this.timer = setTimeout(function () { + self.reset(); + throw new Error("Wrong url for running " + self.id + + '\ntestPath: ' + self.testPath + + '\nhref: ' + window.location.href + + "\nreferrer: " + document.referrer + ); + },this.defaultOverLaps); + } + return; } var self = this; @@ -143,14 +130,10 @@ website.Tour = openerp.Class.extend({ }, _running: function () { var stepId = this.localStorage.getItem("tour-"+this.id+"-test"); - var automatic = !!this.localStorage.getItem("tour-"+this.id+"-test-automatic"); - + if (stepId != null) { - if (!this.check(this.step(stepId))) { - var step = this.next(stepId); - stepId = step ? step.stepId : stepId; - } - this.nextStep(stepId, automatic ? this.autoNextStep : null, automatic ? 5000 : null); + this.registerTour(); + this.nextStep(stepId, this.automatic ? this.autoNextStep : null, this.automatic ? this.defaultOverLaps : null); } }, @@ -187,20 +170,35 @@ website.Tour = openerp.Class.extend({ } }, + registerTour: function () { + this.tour = new Tour({ + name: this.id, + storage: this.tourStorage, + keyboard: false, + template: this.popover(), + onHide: function () { + window.scrollTo(0, 0); + } + }); + this.registerSteps(); + }, registerSteps: function () { for (var index=0, len=this.steps.length; index<len; index++) { var step = this.steps[index]; step.stepId = step.stepId || ""+index; - if (!step.waitNot && index > 0 && $(this.steps[index-1].template).has("button[data-role='next']").size()) { - step.waitNot = '.popover.tour'; + if (!step.waitNot && index > 0 && this.steps[index-1] && + this.steps[index-1].popover && this.steps[index-1].popover.next) { + step.waitNot = '.popover.tour:visible'; } if (!step.waitFor && index > 0 && this.steps[index-1].snippet) { step.waitFor = '.oe_overlay_options .oe_options:visible'; } - step._title = step.title; - step.title = openerp.qweb.render('website.tour_popover_title', { title: step.title }); + step._title = step._title || step.title; + step.title = this.popoverTitle({ title: step._title }); + step.template = step.template || this.popover( step.popover ); + if (!step.element) step.orphan = true; if (step.snippet) { step.element = '#oe_snippets div.oe_snippet[data-snippet-id="'+step.snippet+'"] .oe_snippet_thumbnail'; @@ -208,9 +206,10 @@ website.Tour = openerp.Class.extend({ } - if ($(this.steps[index-1].template).has("button[data-role='next']").size()) { + if (this.steps[index-1] && + this.steps[index-1].popover && this.steps[index-1].popover.next) { var step = { - stepId: index, + stepId: ""+index, waitNot: '.popover.tour:visible' }; this.steps.push(step); @@ -219,8 +218,21 @@ website.Tour = openerp.Class.extend({ this.tour.addSteps(this.steps); }, + popoverTitle: function (options) { + try { + return openerp.qweb.render('website.tour_popover_title', options); + } catch (e) { + if (!this.automatic) throw e; + return options.title; + } + }, popover: function (options) { - return openerp.qweb.render('website.tour_popover', options); + try { + return openerp.qweb.render('website.tour_popover', options); + } catch (e) { + if (!this.automatic) throw e; + return ""; + } }, timer: null, @@ -268,7 +280,12 @@ website.Tour = openerp.Class.extend({ self.timer = setTimeout(checkNext, self.defaultDelay); } else { self.reset(); - throw new Error("Time overlaps to arrive to step " + step.stepId + ": '" + step._title + "'"); + throw new Error("Time overlaps to arrive to step " + step.stepId + ": '" + step._title + "'" + + '\nelement: ' + Boolean(!step.element || ($(step.element).size() && $(step.element).is(":visible") && !$(step.element).is(":hidden"))) + + '\nwaitNot: ' + Boolean(!step.waitNot || !$(step.waitNot).size()) + + '\nwaitFor: ' + Boolean(!step.waitFor || $(step.waitFor).size()) + + '\n\n' + $("body").html() + ); } } checkNext(); @@ -291,7 +308,7 @@ website.Tour = openerp.Class.extend({ $(".popover.tour").remove(); // go to step in bootstrap tour this.tour.goto(index); - if (step.callback) step.callback(); + if (step.onload) step.onload(); next = steps.shift(); break; } @@ -358,7 +375,10 @@ website.Tour = openerp.Class.extend({ } else if (step.sampleText) { $element.trigger($.Event("keydown", { srcElement: $element })); - if ($element.is("select") || $element.is("input") ) { + if ($element.is("input") ) { + $element.val(step.sampleText); + } if ($element.is("select")) { + $element.find("[value='"+step.sampleText+"'], option:contains('"+step.sampleText+"')").attr("selected", true); $element.val(step.sampleText); } else { $element.html(step.sampleText); @@ -404,7 +424,10 @@ website.Tour.busy = false; website.Tour.add = function (tour) { website.Tour.waitReady(function () { tour = tour.id ? tour : new tour(); - website.Tour.tours[tour.id] = tour; + if (!website.Tour.tours[tour.id]) { + website.Tour.tours[tour.id] = tour; + tour.running(); + } }); }; website.Tour.get = function (id) { @@ -420,7 +443,7 @@ website.Tour.each = function (callback) { website.Tour.waitReady = function (callback) { var self = this; $(document).ready(function () { - if ($.ajaxBusy == null || $.ajaxBusy) { + if ($.ajaxBusy) { $(document).ajaxStop(function() { setTimeout(function () { callback.call(self); @@ -435,18 +458,20 @@ website.Tour.waitReady = function (callback) { }); }; website.Tour.run_test = function (id) { - website.Tour.get(id).run(true); + website.Tour.waitReady(function () { + if (!website.Tour.is_busy()) { + website.Tour.tours[id].run(true); + } + }); }; website.Tour.is_busy = function () { for (var k in this.localStorage) { if (!k.indexOf("tour-")) { - return true; + return k; } } return website.Tour.busy; }; - - }()); diff --git a/addons/website/static/src/js/website.tour.test.admin.js b/addons/website/static/src/js/website.tour.test.admin.js new file mode 100644 index 00000000000..787f05beb23 --- /dev/null +++ b/addons/website/static/src/js/website.tour.test.admin.js @@ -0,0 +1,106 @@ +(function () { + 'use strict'; + + var website = openerp.website; + + website.Tour.LoginEdit = website.Tour.extend({ + id: 'login_edit', + name: "Try to log as admin and check editor", + path: '/', + init: function () { + var self = this; + self.steps = [ + { + title: "click login", + element: '#top_menu a[href*="/web/login"]', + }, + { + title: "insert login", + element: '.oe_login_form input[name="login"]', + sampleText: "admin", + }, + { + title: "insert password", + waitFor: '.oe_login_form input[name="login"][value!=""]', + element: '.oe_login_form input[name="password"]', + sampleText: "admin", + }, + { + title: "select 2 Standard tickets", + waitFor: '.oe_login_form input[name="password"][value!=""]', + element: '.oe_login_form button', + }, + { + title: "go back to website from backend", + element: 'a[data-action-model="ir.actions.act_url"]:contains("Website")', + }, + { + title: 'try to edit', + waitNot: '#wrap .carousel', + element: 'button[data-action=edit]:visible', + }, + { + title: 'check edit mode', + waitFor: 'button[data-action=save]:visible', + }, + { + title: 'check branding', + waitFor: '#wrap[data-oe-model="ir.ui.view"]', + }, + { + title: 'check rte', + waitFor: '#oe_rte_toolbar', + }, + { + title: 'check insert block button', + element: '[data-action="snippet"]:visible', + }, + { + title: 'add snippets', + snippet: 'carousel', + }, + { + title: 'try to save', + waitFor: '.oe_overlay_options .oe_options:visible', + element: 'button[data-action=save]:visible', + }, + { + title: 'check saved', + waitFor: '#wrap div.carousel', + element: 'button[data-action=edit]:visible', + }, + { + title: 'try to re-edit', + waitFor: 'button[data-action=save]:visible', + element: '#wrap .carousel', + }, + { + title: 'remove snippet', + element: '.oe_snippet_remove', + }, + { + title: 'try to re-save', + waitNot: '#wrap .carousel', + element: 'button[data-action=save]:visible', + }, + { + title: "click admin", + waitFor: 'button[data-action=edit]:visible', + element: 'a:contains("Administrator")', + }, + { + title: "click logout", + element: '#top_menu a[href*="/logout"]', + }, + { + title: "check logout", + waitFor: '#top_menu a[href*="/web/login"]', + }, + ]; + return this._super(); + }, + }); + // for test without editor bar + website.Tour.add(website.Tour.LoginEdit); + +}()); diff --git a/addons/website/static/src/js/website.tour.test.js b/addons/website/static/src/js/website.tour.test.js new file mode 100644 index 00000000000..3e85ce20dae --- /dev/null +++ b/addons/website/static/src/js/website.tour.test.js @@ -0,0 +1,34 @@ +(function () { + +function LoadScript(src) { + xmlHttp = new XMLHttpRequest(); + xmlHttp.onreadystatechange = function() { + if(xmlHttp.readyState == 4) { + if (xmlHttp.status == 200 || xmlHttp.status == 304) { + new Function(xmlHttp.responseText)(); + } else { + throw new Error("Can't load JavaScript.\nhref: " + window.location.href + "\nsrc: " + src); + } + } + }; + xmlHttp.open("GET", src, false); + xmlHttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + xmlHttp.send(null); +} + +if (typeof jQuery === "undefined") + LoadScript("/web/static/lib/jquery/jquery.js"); +if (typeof _ === "undefined") + LoadScript("/web/static/lib/underscore/underscore.js"); +if (typeof openerp === "undefined") { + LoadScript("/web/static/lib/qweb/qweb2.js"); + LoadScript("/web/static/src/js/openerpframework.js"); +} +if (typeof openerp === "undefined" || !openerp.website || !openerp.website.add_template_file) + LoadScript("/website/static/src/js/website.js"); +if (typeof Tour === "undefined") + LoadScript("/website/static/lib/bootstrap-tour/bootstrap-tour.js"); +if (typeof openerp === "undefined" || !openerp.website.Tour) + LoadScript("/website/static/src/js/website.tour.js"); + +})();
\ No newline at end of file diff --git a/addons/website/tests/test_ui.py b/addons/website/tests/test_ui.py index 96484882577..e79a2eb13ec 100644 --- a/addons/website/tests/test_ui.py +++ b/addons/website/tests/test_ui.py @@ -41,7 +41,7 @@ class WebsiteUiSuite(unittest.TestSuite): # timeout in seconds def __init__(self, testfile, options, timeout=60.0): self._testfile = testfile - self._timeout = timeout + self._timeout = timeout + 5.0 self._options = options self._test = None self._ignore_filters = [ @@ -58,7 +58,6 @@ class WebsiteUiSuite(unittest.TestSuite): return iter([self]) def run(self, result): - return # clean slate if sql_db._Pool is not None: sql_db._Pool.close_all(sql_db.dsn(tools.config['db_name'])) @@ -79,18 +78,25 @@ class WebsiteUiSuite(unittest.TestSuite): del result._exc_info_to_string def _run(self, result): - self._test = WebsiteUiTest(self._testfile) + self._test = WebsiteUiTest("%s (as %s)" % + (self._testfile, self._options.get('user') or "Anonymous" if 'user' in self._options else "admin" )) start_time = time.time() last_check_time = time.time() self._options['timeout'] = self._timeout self._options['port'] = tools.config.get('xmlrpc_port', 80) self._options['db'] = tools.config.get('db_name', '') - self._options['user'] = 'admin' - self._options['password'] = tools.config.get('admin_passwd', 'admin') + # FIX for runbot test + if hasattr(os, 'uname') and os.uname()[1].startswith('runbot.'): + self._options['host'] = tools.config.get('db_name', '') + ".runbot.openerp.com" + + if 'user' not in self._options: + self._options['user'] = 'admin' + self._options['password'] = 'admin' phantom = subprocess.Popen([ 'phantomjs', + #'--debug=true', self._testfile, json.dumps(self._options) ], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) @@ -148,6 +154,7 @@ def full_path(pyfile, filename): def load_tests(loader, base, _): base.addTest(WebsiteUiSuite(full_path(__file__, 'dummy_test.js'), {}, 5.0)) + base.addTest(WebsiteUiSuite(full_path(__file__, 'login_test.js'), {'path': '/', 'user': None}, 60.0)) base.addTest(WebsiteUiSuite(full_path(__file__, 'simple_dom_test.js'), {'redirect': '/page/website.homepage'}, 60.0)) base.addTest(WebsiteUiSuite(full_path(__file__, 'homepage_test.js'), {'redirect': '/page/website.homepage'}, 60.0)) return base diff --git a/addons/website/tests/ui_suite/homepage_test.js b/addons/website/tests/ui_suite/homepage_test.js index 2560e5af4d5..9c7375589a6 100644 --- a/addons/website/tests/ui_suite/homepage_test.js +++ b/addons/website/tests/ui_suite/homepage_test.js @@ -1,17 +1,3 @@ var testRunner = require('./ui_test_runner.js'); -var waitFor = testRunner.waitFor; - -testRunner.run(function homepageTest (page, timeout) { - page.evaluate(function () { localStorage.clear(); }); - waitFor(function clientReady () { - return page.evaluate(function () { - return window.$ && window.openerp && window.openerp.website - && window.openerp.website.Tour; - }); - }, function executeTest () { - page.evaluate(function () { - window.openerp.website.Tour.run_test('banner'); - }); - }, timeout); -}); +testRunner.run_test('banner');
\ No newline at end of file diff --git a/addons/website/tests/ui_suite/login_test.js b/addons/website/tests/ui_suite/login_test.js new file mode 100644 index 00000000000..3ac403e586d --- /dev/null +++ b/addons/website/tests/ui_suite/login_test.js @@ -0,0 +1,7 @@ +var testRunner = require('./ui_test_runner.js'); + +testRunner.run_test('login_edit', { + "inject": [ + "./../../../website/static/src/js/website.tour.test.js", + "./../../../website/static/src/js/website.tour.test.admin.js"] + });
\ No newline at end of file diff --git a/addons/website/tests/ui_suite/ui_test_runner.js b/addons/website/tests/ui_suite/ui_test_runner.js index 507a8ca1e72..745adc15f0c 100644 --- a/addons/website/tests/ui_suite/ui_test_runner.js +++ b/addons/website/tests/ui_suite/ui_test_runner.js @@ -19,15 +19,15 @@ function waitFor (ready, callback, timeout, timeoutMessageCallback) { }, 100); } -function run (test) { +function run (test, onload, inject) { var options = JSON.parse(phantom.args); - var timeout = options.timeout ? Math.round(parseFloat(options.timeout)*1000) : 60000; + var timeout = options.timeout ? Math.round(parseFloat(options.timeout)*1000-5000) : 60000; var scheme = options.scheme ? options.scheme+'://' : 'http://'; var host = options.host ? options.host : 'localhost'; var port = options.port ? ':'+options.port : ''; - var path = options.path ? options.path : '/login'; + var path = options.path ? options.path : '/web/login'; var queryParams = []; if (options.db) queryParams.push('db='+options.db); @@ -56,24 +56,52 @@ function run (test) { }; page.onConsoleMessage = function(message) { console.log(message); + try { + var result = JSON.parse(message); + if (result.event === 'success') { + phantom.exit(0); + } else if (result.event === 'console') { + return; + } else { + phantom.exit(1); + } + } catch (exception) { + phantom.exit(1); + } }; page.onCallback = function(data) { if (data.event && data.event === 'start') { - test(page, timeout); + if (test) test(page, timeout, options); + } + }; + page.onLoadFinished = function(status) { + if (status === "success") { + if (inject) { + if (!inject instanceof Array) { + inject = [inject]; + } + for (var k in inject) { + if(!page.injectJs(inject[k])) { + console.log("Can't inject "+inject[k]); + phantom.exit(1); + } + } + } + if (onload) onload(page, timeout, options); } }; var maxRetries = 10; var retryDelay = 1000; // ms - var tries = 0; + var tries = 0; page.open(url, function openPage (status) { if (status !== 'success') { tries++; if (tries < maxRetries) { - setTimeout(function () { - page.open(url, openPage); - }, retryDelay); + setTimeout(function () { + page.open(url, openPage); + }, retryDelay); } else { console.log('{ "event": "error", "message": "'+url+' failed to load '+tries+' times ('+status+')"}'); phantom.exit(1); @@ -85,9 +113,45 @@ function run (test) { } }); + + setTimeout(function () { + page.evaluate(function (timeout) { + var message = ("Timeout after " +(timeout/1000)+ " s" + + "\nhref: " + window.location.href + + "\nreferrer: " + document.referrer + + "\n\n" + document.body.innerHTML).replace(/[^a-z0-9\s~!@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, "*"); + console.log(JSON.stringify({ "event": "error", "message": message})); + phantom.exit(1); + },timeout); + }, timeout); +} + +function run_test (testname, options) { + options = options || {}; + run( + function start (page, timeout) { + page.evaluate(function () { localStorage.clear(); }); + }, + function onload (page, timeout) { + waitFor(function clientReady () { + return page.evaluate(function () { + return window.$ + && window.openerp + && window.openerp.website + && window.openerp.website.Tour; + }); + }, function executeTest () { + page.evaluate(function (testname) { + window.openerp.website.Tour.run_test(testname); + }, testname); + }, timeout); + }, + options.inject || null + ); } module.exports = { waitFor: waitFor, - run: run + run: run, + run_test: run_test } diff --git a/addons/website/views/snippets.xml b/addons/website/views/snippets.xml index 2012340dea1..5d1cd1d30f2 100644 --- a/addons/website/views/snippets.xml +++ b/addons/website/views/snippets.xml @@ -903,7 +903,7 @@ </div> <div data-snippet-style-id='background' data-selector="section, section[data-snippet-id='parallax'], div[data-snippet-id='carousel'], div[data-snippet-id='slider']"> - <li class="dropdown-submenu"> + <li class="dropdown-submenu" data-required="true"> <a tabindex="-1" href="#">Background</a> <ul class="dropdown-menu"> <li class="dropdown-submenu"> diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml index 0f77d7e2914..bf61f64a1b9 100644 --- a/addons/website/views/website_templates.xml +++ b/addons/website/views/website_templates.xml @@ -284,6 +284,13 @@ </xpath> </template> +<template id="debugger" inherit_option_id="website.layout" name="Debugger & Tests"> + <xpath expr='//t[@name="layout_head"]' position="after"> + <script type="text/javascript" src="/website/static/src/js/website.tour.test.js"></script> + <script type="text/javascript" src="/website/static/src/js/website.tour.test.admin.js"></script> + </xpath> +</template> + <template id="login_layout" inherit_id="web.login_layout" name="Website Login Layout"> <xpath expr="t" position="replace"> <t t-call="website.layout"> diff --git a/addons/website_blog/static/src/js/website.tour.blog.js b/addons/website_blog/static/src/js/website.tour.blog.js index c943d46e0bb..b6701d74634 100644 --- a/addons/website_blog/static/src/js/website.tour.blog.js +++ b/addons/website_blog/static/src/js/website.tour.blog.js @@ -2,115 +2,115 @@ 'use strict'; var website = openerp.website; + var _t = openerp._t; website.EditorBar.include({ start: function () { - this.registerTour(new website.BlogTour(this)); + this.registerTour(new website.Tour.Blog(this)); return this._super(); }, }); - website.BlogTour = website.Tour.extend({ + website.Tour.Blog = website.Tour.extend({ id: 'blog', name: "Create a blog post", testPath: '/(blog|blogpost)', - init: function (editor) { + init: function () { var self = this; self.steps = [ { - title: "New Blog Post", - content: "Let's go through the first steps to write beautiful blog posts.", - template: self.popover({ next: "Start Tutorial", end: "Skip" }), + title: _t("New Blog Post"), + content: _t("Let's go through the first steps to write beautiful blog posts."), + popover: { next: _t("Start Tutorial"), end: _t("Skip") }, }, { element: '#content-menu-button', placement: 'left', - title: "Add Content", - content: "Create new pages, blogs, menu items and products through the <em>'Content'</em> menu.", - template: self.popover({ fixed: true }), + title: _t("Add Content"), + content: _t("Create new pages, blogs, menu items and products through the <em>'Content'</em> menu."), + popover: { fixed: true }, }, { element: 'a[data-action=new_blog_post]', placement: 'left', - title: "New Blog Post", - content: "Select this menu item to create a new blog post.", - template: self.popover({ fixed: true }), + title: _t("New Blog Post"), + content: _t("Select this menu item to create a new blog post."), + popover: { fixed: true }, }, { element: '.modal:has(#editor_new_blog) button.btn-primary', placement: 'right', - title: "Create Blog Post", - content: "Click <em>Continue</em> to create the blog post.", + title: _t("Create Blog Post"), + content: _t("Click <em>Continue</em> to create the blog post."), }, { - waitNot: '.modal', waitFor: 'body:has(button[data-action=save]:visible):has(.js_blog)', - title: "Blog Post Created", - content: "This is your new blog post. Let's edit it.", - template: self.popover({ next: "Continue" }), + title: _t("Blog Post Created"), + content: _t("This is your new blog post. Let's edit it."), + popover: { next: _t("Continue") }, }, { element: 'h1[data-oe-expression="blog_post.name"]', placement: 'bottom', sampleText: 'New Blog', - title: "Set a Title", - content: "Click on this area and set a catchy title for your blog post.", + title: _t("Set a Title"), + content: _t("Click on this area and set a catchy title for your blog post."), }, { waitNot: '#wrap h1[data-oe-model="blog.post"]:contains("Blog Post Title")', element: 'button[data-action=snippet]', placement: 'bottom', - title: "Layout Your Blog Post", - content: "Use well designed building blocks to structure the content of your blog. Click 'Insert Blocks' to add new content.", - template: self.popover({ fixed: true }), + title: _t("Layout Your Blog Post"), + content: _t("Use well designed building blocks to structure the content of your blog. Click 'Insert Blocks' to add new content."), + popover: { fixed: true }, }, { snippet: 'image-text', placement: 'bottom', - title: "Drag & Drop a Block", - content: "Drag the <em>'Image-Text'</em> block and drop it in your page.", - template: self.popover({ fixed: true }), + title: _t("Drag & Drop a Block"), + content: _t("Drag the <em>'Image-Text'</em> block and drop it in your page."), + popover: { fixed: true }, }, { element: 'button[data-action=snippet]', placement: 'bottom', - title: "Add Another Block", - content: "Let's add another block to your post.", - template: self.popover({ fixed: true }), + title: _t("Add Another Block"), + content: _t("Let's add another block to your post."), + popover: { fixed: true }, }, { snippet: 'text-block', placement: 'bottom', - title: "Drag & Drop a block", - content: "Drag the <em>'Text Block'</em> block and drop it below the image block.", - template: self.popover({ fixed: true }), + title: _t("Drag & Drop a block"), + content: _t("Drag the <em>'Text Block'</em> block and drop it below the image block."), + popover: { fixed: true }, }, { element: '.oe_active .oe_snippet_remove', placement: 'top', - title: "Delete the Title", - content: "From this toolbar you can move, duplicate or delete the selected zone. Click on the garbage can image to delete the title.", + title: _t("Delete the Title"), + content: _t("From this toolbar you can move, duplicate or delete the selected zone. Click on the garbage can image to delete the title."), }, { waitNot: '.oe_active .oe_snippet_remove:visible', element: 'button[data-action=save]', placement: 'right', - title: "Save Your Blog", - content: "Click the <em>Save</em> button to record changes on the page.", - template: self.popover({ fixed: true }), + title: _t("Save Your Blog"), + content: _t("Click the <em>Save</em> button to record changes on the page."), + popover: { fixed: true }, }, { waitFor: 'button[data-action=edit]:visible', element: 'button.btn-danger.js_publish_btn', placement: 'top', - title: "Publish Your Post", - content: "Your blog post is not yet published. You can update this draft version and publish it once you are ready.", + title: _t("Publish Your Post"), + content: _t("Your blog post is not yet published. You can update this draft version and publish it once you are ready."), }, { waitFor: '.js_publish_management button.js_publish_btn.btn-success:visible', title: "Thanks!", - content: "This tutorial is finished. To discover more features, improve the content of this page and try the <em>Promote</em> button in the top right menu.", - template: self.popover({ end: "Close Tutorial" }), + content: _t("This tutorial is finished. To discover more features, improve the content of this page and try the <em>Promote</em> button in the top right menu."), + popover: { next: _t("Close Tutorial") }, }, ]; return this._super(); diff --git a/addons/website_blog/tests/ui_suite/post_test.js b/addons/website_blog/tests/ui_suite/post_test.js index f8d729bee0b..1152ab1da55 100644 --- a/addons/website_blog/tests/ui_suite/post_test.js +++ b/addons/website_blog/tests/ui_suite/post_test.js @@ -1,17 +1,3 @@ var testRunner = require('../../../website/tests/ui_suite/ui_test_runner.js'); -var waitFor = testRunner.waitFor; - -testRunner.run(function blogTest (page, timeout) { - page.evaluate(function () { localStorage.clear(); }); - waitFor(function clientReady () { - return page.evaluate(function () { - return window.$ && window.openerp && window.openerp.website - && window.openerp.website.Tour; - }); - }, function executeTest () { - page.evaluate(function () { - window.openerp.website.Tour.run_test('blog'); - }); - }, timeout); -});
\ No newline at end of file +testRunner.run_test('blog');
\ No newline at end of file diff --git a/addons/website_event/controllers/main.py b/addons/website_event/controllers/main.py index e7e0b4ec9ff..2a6e1d134e3 100644 --- a/addons/website_event/controllers/main.py +++ b/addons/website_event/controllers/main.py @@ -203,7 +203,9 @@ class website_event(http.Controller): def add_event(self, event_name="New Event", **kwargs): return self._add_event(event_name, request.context, **kwargs) - def _add_event(self, event_name="New Event", context={}, **kwargs): + def _add_event(self, event_name=None, context={}, **kwargs): + if not event_name: + event_name = _("New Event") Event = request.registry.get('event.event') date_begin = datetime.today() + timedelta(days=(14)) vals = { diff --git a/addons/website_event/static/src/js/website.tour.event.js b/addons/website_event/static/src/js/website.tour.event.js index c84c0b36bb6..73510b455dd 100644 --- a/addons/website_event/static/src/js/website.tour.event.js +++ b/addons/website_event/static/src/js/website.tour.event.js @@ -2,6 +2,7 @@ 'use strict'; var website = openerp.website; + var _t = openerp._t; website.EditorBar.include({ start: function () { @@ -18,93 +19,93 @@ var self = this; self.steps = [ { - title: "Create an Event", - content: "Let's go through the first steps to publish a new event.", - template: self.popover({ next: "Start Tutorial", end: "Skip It" }), + title: _t("Create an Event"), + content: _t("Let's go through the first steps to publish a new event."), + popover: { next: _("Start Tutorial"), end: _("Skip It") }, }, { element: '#content-menu-button', placement: 'left', - title: "Add Content", - content: "The <em>Content</em> menu allows you to create new pages, events, menus, etc.", - template: self.popover({ fixed: true }), + title: _t("Add Content"), + content: _t("The <em>Content</em> menu allows you to create new pages, events, menus, etc."), + popover: { fixed: true }, }, { element: 'a[data-action=new_event]', placement: 'left', - title: "New Event", - content: "Click here to create a new event.", - template: self.popover({ fixed: true }), + title: _t("New Event"), + content: _t("Click here to create a new event."), + popover: { fixed: true }, }, { element: '.modal #editor_new_event input[type=text]', sampleText: 'Advanced Technical Training', placement: 'right', - title: "Create an Event Name", - content: "Create a name for your new event and click <em>'Continue'</em>. e.g: Technical Training", + title: _t("Create an Event Name"), + content: _t("Create a name for your new event and click <em>'Continue'</em>. e.g: Technical Training"), }, { waitNot: '.modal input[type=text]:not([value!=""])', element: '.modal button.btn-primary', placement: 'right', - title: "Create Event", - content: "Click <em>Continue</em> to create the event.", + title: _t("Create Event"), + content: _t("Click <em>Continue</em> to create the event."), }, { waitFor: 'body:has(button[data-action=save]:visible):has(.js_event)', - title: "New Event Created", - content: "This is your new event page. We will edit the event presentation page.", - template: self.popover({ next: "Continue" }), + title: _t("New Event Created"), + content: _t("This is your new event page. We will edit the event presentation page."), + popover: { next: _t("Continue") }, }, { element: 'button[data-action=snippet]', placement: 'bottom', - title: "Layout your event", - content: "Insert blocks to layout the body of your event.", - template: self.popover({ fixed: true }), + title: _t("Layout your event"), + content: _t("Insert blocks to layout the body of your event."), + popover: { fixed: true }, }, { snippet: 'image-text', placement: 'bottom', - title: "Drag & Drop a block", - content: "Drag the 'Image-Text' block and drop it in your page.", - template: self.popover({ fixed: true }), + title: _t("Drag & Drop a block"), + content: _t("Drag the 'Image-Text' block and drop it in your page."), + popover: { fixed: true }, }, { element: 'button[data-action=snippet]', placement: 'bottom', - title: "Layout your event", - content: "Insert another block to your event.", - template: self.popover({ fixed: true }), + title: _t("Layout your event"), + content: _t("Insert another block to your event."), + popover: { fixed: true }, }, { snippet: 'text-block', placement: 'bottom', - title: "Drag & Drop a block", - content: "Drag the 'Text Block' in your event page.", - template: self.popover({ fixed: true }), + title: _t("Drag & Drop a block"), + content: _t("Drag the 'Text Block' in your event page."), + popover: { fixed: true }, }, { element: 'button[data-action=save]', placement: 'right', - title: "Save your modifications", - content: "Once you click on save, your event is updated.", - template: self.popover({ fixed: true }), + title: _t("Save your modifications"), + content: _t("Once you click on save, your event is updated."), + popover: { fixed: true }, }, { waitFor: 'button[data-action=edit]:visible', element: 'button.btn-danger.js_publish_btn', placement: 'top', - title: "Publish your event", - content: "Click to publish your event.", + title: _t("Publish your event"), + content: _t("Click to publish your event."), }, { waitFor: '.js_publish_management button.js_publish_btn.btn-success:visible', element: '.js_publish_management button[data-toggle="dropdown"]', placement: 'left', - title: "Customize your event", - content: "Click here to customize your event further.", + title: _t("Customize your event"), + content: _t("Click here to customize your event further."), }, { element: '.js_publish_management ul>li>a:last:visible', diff --git a/addons/website_event/tests/ui_suite/event_test.js b/addons/website_event/tests/ui_suite/event_test.js index 06d6dc12b5f..946427b6b6d 100644 --- a/addons/website_event/tests/ui_suite/event_test.js +++ b/addons/website_event/tests/ui_suite/event_test.js @@ -1,17 +1,3 @@ var testRunner = require('../../../website/tests/ui_suite/ui_test_runner.js'); -var waitFor = testRunner.waitFor; - -testRunner.run(function eventTest (page, timeout) { - page.evaluate(function () { localStorage.clear(); }); - waitFor(function clientReady () { - return page.evaluate(function () { - return window.$ && window.openerp && window.openerp.website - && window.openerp.website.Tour; - }); - }, function executeTest () { - page.evaluate(function () { - window.openerp.website.Tour.get('event').run(true); - }); - }, timeout); -}); +testRunner.run_test('event');
\ No newline at end of file diff --git a/addons/website_event_sale/static/src/js/website.tour.event_sale.js b/addons/website_event_sale/static/src/js/website.tour.event_sale.js new file mode 100644 index 00000000000..7991831400c --- /dev/null +++ b/addons/website_event_sale/static/src/js/website.tour.event_sale.js @@ -0,0 +1,80 @@ +(function () { + 'use strict'; + + var website = openerp.website; + + website.Tour.EventSaleTest = website.Tour.extend({ + id: 'event_buy_tickets', + name: "Try to buy tickets for event", + path: '/event', + testPath: '/(event|shop)', + init: function () { + var self = this; + self.steps = [ + { + title: "select event", + element: 'a[href*="/event"]:contains("Open Days in Los Angeles")', + }, + { + title: "go to register page", + waitNot: 'a[href*="/event"]:contains("Functional Webinar")', + onload: function () { + // use onload if website_event_track is installed + if (!$('form:contains("Ticket Type")').size()) { + window.location.href = $('a[href*="/event"][href*="/register"]').attr("href"); + } + }, + }, + { + title: "select 2 Standard tickets", + element: 'select[name="ticket-1"]', + sampleText: '2', + }, + { + title: "select 3 VIP tickets", + waitFor: 'select[name="ticket-1"] option:contains(2):selected', + element: 'select[name="ticket-2"]', + sampleText: '3', + }, + { + title: "Order Now", + waitFor: 'select[name="ticket-2"] option:contains(3):selected', + element: 'button.btn-primary:contains("Order Now")', + }, + { + title: "Complete checkout", + waitFor: '#top_menu .my_cart_quantity:contains(5)', + element: 'form[action="/shop/confirm_order/"] button', + onload: function (tour) { + if ($("input[name='name']").val() === "") + $("input[name='name']").val("website_sale-test-shoptest"); + if ($("input[name='email']").val() === "") + $("input[name='email']").val("website_event_sale_test_shoptest@websiteeventsaletest.optenerp.com"); + $("input[name='phone']").val("123"); + $("input[name='street']").val("123"); + $("input[name='city']").val("123"); + $("input[name='zip']").val("123"); + $("select[name='country_id']").val("21"); + }, + }, + { + title: "select payment", + element: '#payment_method label:has(img[title="transfer"]) input', + }, + { + title: "Pay Now", + waitFor: '#payment_method label:has(input:checked):has(img[title="transfer"])', + element: '.oe_sale_acquirer_button button[name="submit"]:visible', + }, + { + title: "finish", + waitFor: '.oe_website_sale:contains("Thank you for your order")', + } + ]; + return this._super(); + }, + }); + // for test without editor bar + website.Tour.add(website.Tour.EventSaleTest); + +}()); diff --git a/addons/website_event_sale/tests/__init__.py b/addons/website_event_sale/tests/__init__.py new file mode 100644 index 00000000000..bf8ba6321ad --- /dev/null +++ b/addons/website_event_sale/tests/__init__.py @@ -0,0 +1 @@ +import test_ui diff --git a/addons/website_event_sale/tests/test_ui.py b/addons/website_event_sale/tests/test_ui.py new file mode 100644 index 00000000000..1e5d727de92 --- /dev/null +++ b/addons/website_event_sale/tests/test_ui.py @@ -0,0 +1,10 @@ +import openerp.addons.website.tests.test_ui as test_ui + +def load_tests(loader, base, _): + base.addTest(test_ui.WebsiteUiSuite(test_ui.full_path(__file__,'website_event_sale_test.js'), + {'redirect': '/page/website.homepage'})) + base.addTest(test_ui.WebsiteUiSuite(test_ui.full_path(__file__,'website_event_sale_test.js'), + {'redirect': '/page/website.homepage', 'user': 'demo', 'password': 'demo'})) + base.addTest(test_ui.WebsiteUiSuite(test_ui.full_path(__file__,'website_event_sale_test.js'), + {'path': '/', 'user': None})) + return base
\ No newline at end of file diff --git a/addons/website_event_sale/tests/ui_suite/website_event_sale_test.js b/addons/website_event_sale/tests/ui_suite/website_event_sale_test.js new file mode 100644 index 00000000000..6662c6a2a55 --- /dev/null +++ b/addons/website_event_sale/tests/ui_suite/website_event_sale_test.js @@ -0,0 +1,7 @@ +var testRunner = require('../../../website/tests/ui_suite/ui_test_runner.js'); + +testRunner.run_test('event_buy_tickets', { + "inject": [ + "./../../../website/static/src/js/website.tour.test.js", + "./../../../website_event_sale/static/src/js/website.tour.event_sale.js"] + });
\ No newline at end of file diff --git a/addons/website_event_sale/views/website_event_sale.xml b/addons/website_event_sale/views/website_event_sale.xml index aa56a5542ac..c3e08031ddb 100644 --- a/addons/website_event_sale/views/website_event_sale.xml +++ b/addons/website_event_sale/views/website_event_sale.xml @@ -2,6 +2,13 @@ <openerp> <data> +<template id="debugger" inherit_id="website.debugger" name="Event Debugger"> + <xpath expr="//script[last()]" position="after"> + <script type="text/javascript" src="/website_event_sale/static/src/js/website.tour.event_sale.js"></script> + </xpath> +</template> + + <template id="index" inherit_id="website_event.index" name="Event's Ticket"> <xpath expr="//li[@t-foreach='event_ids']/div/h4" position="before"> <t t-if="event.state in ['draft', 'confirm'] and event.event_ticket_ids"> diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py index b9119b486ff..5e02def1494 100644 --- a/addons/website_sale/controllers/main.py +++ b/addons/website_sale/controllers/main.py @@ -6,6 +6,7 @@ import werkzeug from openerp import SUPERUSER_ID from openerp.addons.web import http from openerp.addons.web.http import request +from openerp.tools.translate import _ PPG = 20 # Products Per Page PPR = 4 # Products Per Row @@ -294,7 +295,9 @@ class Ecommerce(http.Controller): return werkzeug.utils.redirect(request.httprequest.referrer + "#comments") @http.route(['/shop/add_product/'], type='http', auth="user", methods=['POST'], website=True, multilang=True) - def add_product(self, name="New Product", category=0, **post): + def add_product(self, name=None, category=0, **post): + if not name: + name = _("New Product") Product = request.registry.get('product.product') product_id = Product.create(request.cr, request.uid, { 'name': name, 'public_categ_id': category diff --git a/addons/website_sale/static/src/js/website.tour.sale.js b/addons/website_sale/static/src/js/website.tour.sale.js new file mode 100644 index 00000000000..83696c4e3cb --- /dev/null +++ b/addons/website_sale/static/src/js/website.tour.sale.js @@ -0,0 +1,95 @@ +(function () { + 'use strict'; + + var website = openerp.website; + + website.Tour.ShopTest = website.Tour.extend({ + id: 'shop_buy_product', + name: "Try to buy products", + path: '/shop', + testPath: '/shop', + init: function () { + var self = this; + self.steps = [ + { + title: "select ipod", + element: '.oe_product_cart a:contains("iPod")', + }, + { + title: "select ipod 32Go", + element: 'input[name="product_id"]:not([checked])', + }, + { + title: "click on add to cart", + waitFor: 'input[name="product_id"]:eq(1)[checked]', + element: 'form[action="/shop/add_cart/"] button', + }, + { + title: "add suggested", + element: 'form[action="/shop/add_cart/"] button.btn-link:contains("Add to Cart")', + }, + { + title: "add one more iPod", + waitFor: '.my_cart_quantity:contains(2)', + element: '#mycart_products tr:contains("iPod: 32 Gb") a.js_add_cart_json:eq(1)', + }, + { + title: "remove Headphones", + waitFor: '#mycart_products tr:contains("iPod: 32 Gb") input.js_quantity[value=2]', + element: '#mycart_products tr:contains("Apple In-Ear Headphones") a.js_add_cart_json:first', + }, + { + title: "set one iPod", + waitNot: '#mycart_products tr:contains("Apple In-Ear Headphones")', + element: '#mycart_products input.js_quantity', + sampleText: '1', + }, + { + title: "go to checkout", + waitFor: '#mycart_products input.js_quantity[value=1]', + element: 'a[href="/shop/checkout/"]', + }, + { + title: "test with input error", + element: 'form[action="/shop/confirm_order/"] button', + onload: function (tour) { + $("input[name='phone']").val(""); + }, + }, + { + title: "test without input error", + waitFor: 'form[action="/shop/confirm_order/"] .has-error', + element: 'form[action="/shop/confirm_order/"] button', + onload: function (tour) { + if ($("input[name='name']").val() === "") + $("input[name='name']").val("website_sale-test-shoptest"); + if ($("input[name='email']").val() === "") + $("input[name='email']").val("website_sale_test_shoptest@websitesaletest.optenerp.com"); + $("input[name='phone']").val("123"); + $("input[name='street']").val("123"); + $("input[name='city']").val("123"); + $("input[name='zip']").val("123"); + $("select[name='country_id']").val("21"); + }, + }, + { + title: "select payment", + element: '#payment_method label:has(img[title="transfer"]) input', + }, + { + title: "Pay Now", + waitFor: '#payment_method label:has(input:checked):has(img[title="transfer"])', + element: '.oe_sale_acquirer_button button[name="submit"]:visible', + }, + { + title: "finish", + waitFor: '.oe_website_sale:contains("Thank you for your order")', + } + ]; + return this._super(); + }, + }); + // for test without editor bar + website.Tour.add(website.Tour.ShopTest); + +}()); diff --git a/addons/website_sale/static/src/js/website.tour.shop.js b/addons/website_sale/static/src/js/website.tour.shop.js index ae67d9b8c46..24dd5fd2d66 100644 --- a/addons/website_sale/static/src/js/website.tour.shop.js +++ b/addons/website_sale/static/src/js/website.tour.shop.js @@ -2,238 +2,143 @@ 'use strict'; var website = openerp.website; + var _t = openerp._t; website.EditorBar.include({ start: function () { - this.registerTour(new website.EditorShopTour(this)); - var res = this._super(); - return res; + this.registerTour(new website.Tour.Shop(this)); + return this._super(); }, }); - website.EditorShopTour = website.Tour.extend({ + website.Tour.Shop = website.Tour.extend({ id: 'shop', name: "Create a product", testPath: '/shop', - init: function (editor) { + init: function () { var self = this; self.steps = [ { - title: "Welcome to your shop", - content: "You successfully installed the e-commerce. This guide will help you to create your product and promote your sales.", - template: self.popover({ next: "Start Tutorial", end: "Skip It" }), + title: _t("Welcome to your shop"), + content: _t("You successfully installed the e-commerce. This guide will help you to create your product and promote your sales."), + popover: { next: _t("Start Tutorial"), end: _t("Skip It") }, }, { element: '#content-menu-button', placement: 'left', - title: "Create your first product", - content: "Click here to add a new product.", - template: self.popover({ fixed: true }), + title: _t("Create your first product"), + content: _t("Click here to add a new product."), + popover: { fixed: true }, }, { element: 'a[data-action=new_product]', placement: 'left', - title: "Create a new product", - content: "Select 'New Product' to create it and manage its properties to boost your sales.", - template: self.popover({ fixed: true }), + title: _t("Create a new product"), + content: _t("Select 'New Product' to create it and manage its properties to boost your sales."), + popover: { fixed: true }, }, { element: '.modal #editor_new_product input[type=text]', sampleText: 'New Product', placement: 'right', - title: "Choose name", - content: "Enter a name for your new product then click 'Continue'.", + title: _t("Choose name"), + content: _t("Enter a name for your new product then click 'Continue'."), }, { waitNot: '.modal input[type=text]:not([value!=""])', element: '.modal button.btn-primary', placement: 'right', - title: "Create Product", - content: "Click <em>Continue</em> to create the product.", + title: _t("Create Product"), + content: _t("Click <em>Continue</em> to create the product."), }, { waitFor: 'body:has(button[data-action=save]:visible):has(.js_sale)', - title: "New product created", - content: "This page contains all the information related to the new product.", - template: self.popover({ next: "Continue" }), + title: _t("New product created"), + content: _t("This page contains all the information related to the new product."), + popover: { next: _t("Continue") }, }, { element: '.product_price .oe_currency_value', sampleText: '20.50', placement: 'left', - title: "Change the price", - content: "Edit the price of this product by clicking on the amount.", + title: _t("Change the price"), + content: _t("Edit the price of this product by clicking on the amount."), }, { waitNot: '.product_price .oe_currency_value:containsExact(1.00)', element: '#wrap img.img:first', placement: 'top', - title: "Update image", - content: "Click here to set an image describing your product.", + title: _t("Update image"), + content: _t("Click here to set an image describing your product."), }, { element: 'button.hover-edition-button:visible', placement: 'top', - title: "Update image", - content: "Click here to set an image describing your product.", + title: _t("Update image"), + content: _t("Click here to set an image describing your product."), }, { wait: 500, element: '.well a.pull-right', placement: 'bottom', - title: "Select an Image", - content: "Let's select an existing image.", - template: self.popover({ fixed: true }), + title: _t("Select an Image"), + content: _t("Let's select an existing image."), + popover: { fixed: true }, }, { element: 'img[alt=imac]', placement: 'bottom', - title: "Select an Image", - content: "Let's select an imac image.", - template: self.popover({ fixed: true }), + title: _t("Select an Image"), + content: _t("Let's select an imac image."), + popover: { fixed: true }, }, { waitNot: 'img[alt=imac]', element: '.modal-content button.save', placement: 'bottom', - title: "Select this Image", - content: "Click to add the image to the product decsription.", - template: self.popover({ fixed: true }), + title: _t("Select this Image"), + content: _t("Click to add the image to the product decsription."), + popover: { fixed: true }, }, { waitNot: '.modal-content:visible', element: 'button[data-action=snippet]', placement: 'bottom', - title: "Describe the Product", - content: "Insert blocks like text-image, or gallery to fully describe the product.", - template: self.popover({ fixed: true }), + title: _t("Describe the Product"), + content: _t("Insert blocks like text-image, or gallery to fully describe the product."), + popover: { fixed: true }, }, { snippet: 'big-picture', placement: 'bottom', - title: "Drag & Drop a block", - content: "Drag the 'Big Picture' block and drop it in your page.", - template: self.popover({ fixed: true }), + title: _t("Drag & Drop a block"), + content: _t("Drag the 'Big Picture' block and drop it in your page."), + popover: { fixed: true }, }, { element: 'button[data-action=save]', placement: 'right', - title: "Save your modifications", - content: "Once you click on save, your product is updated.", - template: self.popover({ fixed: true }), + title: _t("Save your modifications"), + content: _t("Once you click on save, your product is updated."), + popover: { fixed: true }, }, { waitFor: '#website-top-navbar button[data-action="edit"]:visible', element: '.js_publish_management button.js_publish_btn.btn-danger', placement: 'top', - title: "Publish your product", - content: "Click to publish your product so your customers can see it.", + title: _t("Publish your product"), + content: _t("Click to publish your product so your customers can see it."), }, { waitFor: '.js_publish_management button.js_publish_btn.btn-success:visible', - title: "Congratulations", - content: "Congratulations! You just created and published your first product.", - template: self.popover({ next: "Close Tutorial" }), + title: _t("Congratulations"), + content: _t("Congratulations! You just created and published your first product."), + popover: { next: _t("Close Tutorial") }, }, ]; return this._super(); } }); - website.EditorShopTest = website.Tour.extend({ - id: 'shop_buy_product', - name: "Try to buy products", - path: '/shop', - testPath: '/shop', - init: function (editor) { - var self = this; - self.steps = [ - { - title: 'begin-test', - template: self.popover({ next: "Start Test"}), - backdrop: true, - }, - { - title: "select ipod", - element: '.oe_product_cart a:contains("iPod")', - }, - { - title: "select ipod 32Go", - element: 'input[name="product_id"]:not([checked])', - }, - { - title: "click on add to cart", - waitFor: 'input[name="product_id"]:eq(1)[checked]', - element: 'form[action="/shop/add_cart/"] button', - }, - { - title: "add suggested", - element: 'form[action="/shop/add_cart/"] button.btn-link:contains("Add to Cart")', - }, - { - title: "add one more iPod", - waitFor: '.my_cart_quantity:contains(2)', - element: '#mycart_products tr:contains("iPod: 32 Gb") a.js_add_cart_json:eq(1)', - }, - { - title: "remove Headphones", - waitFor: '#mycart_products tr:contains("iPod: 32 Gb") input.js_quantity[value=2]', - element: '#mycart_products tr:contains("Apple In-Ear Headphones") a.js_add_cart_json:first', - }, - { - title: "set one iPod", - waitNot: '#mycart_products tr:contains("Apple In-Ear Headphones")', - element: '#mycart_products input.js_quantity', - sampleText: '1', - }, - { - title: "go to checkout", - waitFor: '#mycart_products input.js_quantity[value=1]', - element: 'a[href="/shop/checkout/"]', - }, - { - title: "test with input error", - element: 'form[action="/shop/confirm_order/"] button', - callback: function (tour) { - $("input[name='phone']").val(""); - }, - }, - { - title: "test without input error", - waitFor: 'form[action="/shop/confirm_order/"] .has-error', - element: 'form[action="/shop/confirm_order/"] button', - callback: function (tour) { - if ($("input[name='name']").val() === "") - $("input[name='name']").val("website_sale-test-shoptest"); - if ($("input[name='email']").val() === "") - $("input[name='email']").val("website_sale-test-shoptest@website_sale-test-shoptest.optenerp.com"); - $("input[name='phone']").val("123"); - $("input[name='street']").val("123"); - $("input[name='city']").val("123"); - $("input[name='zip']").val("123"); - $("select[name='country_id']").val("21"); - }, - }, - { - title: "select acquirer", - element: 'input[name="acquirer"]:first', - }, - { - title: "Pay Now", - element: 'button:contains("Pay Now")', - }, - { - title: "finish", - waitFor: '.oe_website_sale:contains("Thank you for your order")', - } - ]; - return this._super(); - }, - }); - // for test without editor bar - $(document).ready(function () { - website.Tour.add(website.EditorShopTest); - }); - }()); diff --git a/addons/website_sale/static/src/js/website_sale.js b/addons/website_sale/static/src/js/website_sale.js index 7f6d24373f7..3c9bb6ea05c 100644 --- a/addons/website_sale/static/src/js/website_sale.js +++ b/addons/website_sale/static/src/js/website_sale.js @@ -26,11 +26,12 @@ $(document).ready(function () { if (isNaN(value)) value = 0; openerp.jsonRpc("/shop/set_cart_json/", 'call', {'order_line_id': $input.data('id'), 'set_number': value}) .then(function (data) { - set_my_cart_quantity(data[1]); - $input.val(data[0]); - if (!data[0]) { + if (!data) { location.reload(); + return; } + set_my_cart_quantity(data[1]); + $input.val(data[0]); }); }); diff --git a/addons/website_sale/tests/test_ui.py b/addons/website_sale/tests/test_ui.py index 72b948830b1..394a9ca8e4f 100644 --- a/addons/website_sale/tests/test_ui.py +++ b/addons/website_sale/tests/test_ui.py @@ -1,8 +1,12 @@ import openerp.addons.website.tests.test_ui as test_ui def load_tests(loader, base, _): + base.addTest(test_ui.WebsiteUiSuite(test_ui.full_path(__file__,'website_sale-add_product-test.js'), + {'redirect': '/page/website.homepage'})) base.addTest(test_ui.WebsiteUiSuite(test_ui.full_path(__file__,'website_sale-sale_process-test.js'), - { 'action': 'website.action_website_homepage' })) - base.addTest(test_ui.WebsiteUiSuite(test_ui.full_path(__file__,'website_sale-sale_process-test-2.js'), - { 'action': 'website.action_website_homepage' })) + {'redirect': '/page/website.homepage'})) + base.addTest(test_ui.WebsiteUiSuite(test_ui.full_path(__file__,'website_sale-sale_process-test.js'), + {'redirect': '/page/website.homepage', 'user': 'demo', 'password': 'demo'})) + base.addTest(test_ui.WebsiteUiSuite(test_ui.full_path(__file__,'website_sale-sale_process-test.js'), + {'path': '/', 'user': None})) return base
\ No newline at end of file diff --git a/addons/website_sale/tests/ui_suite/website_sale-add_product-test.js b/addons/website_sale/tests/ui_suite/website_sale-add_product-test.js new file mode 100644 index 00000000000..24befb7f15d --- /dev/null +++ b/addons/website_sale/tests/ui_suite/website_sale-add_product-test.js @@ -0,0 +1,3 @@ +var testRunner = require('../../../website/tests/ui_suite/ui_test_runner.js'); + +testRunner.run_test('shop');
\ No newline at end of file diff --git a/addons/website_sale/tests/ui_suite/website_sale-sale_process-test-2.js b/addons/website_sale/tests/ui_suite/website_sale-sale_process-test-2.js deleted file mode 100644 index c55eec11617..00000000000 --- a/addons/website_sale/tests/ui_suite/website_sale-sale_process-test-2.js +++ /dev/null @@ -1,17 +0,0 @@ -var testRunner = require('../../../website/tests/ui_suite/ui_test_runner.js'); - -var waitFor = testRunner.waitFor; - -testRunner.run(function websiteSaleTest (page, timeout) { - page.evaluate(function () { localStorage.clear(); }); - waitFor(function clientReady () { - return page.evaluate(function () { - return window.$ && window.openerp && window.openerp.website - && window.openerp.website.Tour; - }); - }, function executeTest () { - page.evaluate(function () { - window.openerp.website.Tour.run_test('shop'); - }); - }, timeout); -}); diff --git a/addons/website_sale/tests/ui_suite/website_sale-sale_process-test.js b/addons/website_sale/tests/ui_suite/website_sale-sale_process-test.js index 5611d8400de..ca3943d5e85 100644 --- a/addons/website_sale/tests/ui_suite/website_sale-sale_process-test.js +++ b/addons/website_sale/tests/ui_suite/website_sale-sale_process-test.js @@ -1,17 +1,7 @@ var testRunner = require('../../../website/tests/ui_suite/ui_test_runner.js'); -var waitFor = testRunner.waitFor; - -testRunner.run(function websiteSaleTest (page, timeout) { - page.evaluate(function () { localStorage.clear(); }); - waitFor(function clientReady () { - return page.evaluate(function () { - return window.$ && window.openerp && window.openerp.website - && window.openerp.website.Tour; - }); - }, function executeTest () { - page.evaluate(function () { - window.openerp.website.Tour.run_test('shop_buy_product'); - }); - }, timeout); -});
\ No newline at end of file +testRunner.run_test('shop_buy_product', { + "inject": [ + "./../../../website/static/src/js/website.tour.test.js", + "./../../../website_sale/static/src/js/website.tour.sale.js"] + }); diff --git a/addons/website_sale/views/website_sale.xml b/addons/website_sale/views/website_sale.xml index ed2007e27c7..bd51d35cc14 100644 --- a/addons/website_sale/views/website_sale.xml +++ b/addons/website_sale/views/website_sale.xml @@ -4,6 +4,12 @@ <!-- Layout add nav and footer --> +<template id="debugger" inherit_id="website.debugger" name="Event Debugger"> + <xpath expr="//script[last()]" position="after"> + <script type="text/javascript" src="/website_sale/static/src/js/website.tour.sale.js"></script> + </xpath> +</template> + <template id="editor_head" inherit_id="website.editor_head" name="Shop Editor" groups="base.group_sale_manager"> <xpath expr="//script[@id='website_tour_js']" position="after"> <script type="text/javascript" src="/website_sale/static/src/js/website_sale.editor.js"></script> @@ -530,7 +536,7 @@ <a t-if="website_sale_order and website_sale_order.website_order_line" href="/shop/checkout/" class="btn btn-primary pull-right mb32">Process Checkout <span class="fa fa-long-arrow-right"/></a> <div class="oe_structure"/> </div> - <div class="col-lg-3 col-lg-offset-1 col-sm-3 text-muted" id="right_column"> + <div class="col-lg-3 col-lg-offset-1 col-sm-3 col-md-3 text-muted" id="right_column"> <h4>Policies</h4> <ul class="list-unstyled mb32"> <li>☑ 30-days money-back guarantee</li> @@ -828,7 +834,7 @@ </div> <button type="submit" class="btn btn-default btn-primary pull-right mb32">Confirm <span class="fa fa-long-arrow-right"/></button> </div> - <div class="col-lg-offset-1 col-lg-3 text-muted"> + <div class="col-lg-offset-1 col-lg-3 col-md-3 text-muted"> <h3 class="page-header mt16">Your Order <small><a href="/shop/mycart"><span class="fa fa-arrow-right"/> change</a></small></h3> <div class="row"> <div class="col-sm-6 text-right">Subtotal:</div> |
