[IMP] point_of_sale: rewrite frontend tests

There used to be some acceptance tests but they have not actually been
run since 8.0.

These acceptance tests are very useful for the POS because the POS is a
somewhat unusual module and it happens occasionally that a change in
another module unintentionally breaks the POS.

Another reason these tests are nice to have is because the POS
duplicates a lot of calculations in JS because it's meant to be able to
work offline (eg. calculation of taxes, orderline totals etc.). Subtle
differences (eg. rounding) between the frontend JS calculations and the
backend calculations make up the most common category of bugs in the
POS. These frontend tests will make it easy to write tests for these
scenario's. When phantom_js finishes we look through all the received
pos.order records and see that all of them are paid. If there would be a
difference between frontend and backend calculations the order would not
have the paid status because the payment on the order would be different
from the total. An example test for a previous bug has been included.

This will also come in handy for testing the upcoming pricelist
functionality that will be implemented in the POS.
This commit is contained in:
Joren Van Onder
2016-04-28 10:26:09 +02:00
parent 5dc6828631
commit 5c545e3244
4 changed files with 175 additions and 92 deletions
+112 -83
View File
@@ -1,90 +1,119 @@
odoo.define('point_of_sale.Tour', function (require) {
"use strict";
"use strict";
var Tour = require('web.Tour');
var Tour = require('web.Tour');
Tour.register({
id: 'pos_basic_order',
name: 'Complete a basic order trough the Front-End',
path: '/web#model=pos.config&action=point_of_sale.action_pos_config_kanban',
mode: 'test',
steps: [
{
title: 'Wait fot the bloody screen to be ready',
wait: 200,
},
{
title: 'Load the Session',
waitNot: '.oe_loading:visible',
element: 'span:contains("Resume Session"),span:contains("Start Session")',
},
{
title: 'Loading the Loading Screen',
function add_product_to_order(product_name) {
return [{
title: 'buy ' + product_name,
element: '.product-list .product-name:contains("' + product_name + '")',
}, {
title: 'the ' + product_name + ' have been added to the order',
waitFor: '.order .product-name:contains("' + product_name + '")',
}];
}
function generate_keypad_steps(amount_str, keypad_selector) {
var i, steps = [], current_char;
for (i = 0; i < amount_str.length; ++i) {
current_char = amount_str[i];
steps.push({
title: 'press ' + current_char + ' on payment keypad',
element: keypad_selector + ' .input-button:contains("' + current_char + '"):visible'
});
}
return steps;
}
function generate_payment_screen_keypad_steps(amount_str) {
return generate_keypad_steps(amount_str, '.payment-numpad');
}
function generate_product_screen_keypad_steps(amount_str) {
return generate_keypad_steps(amount_str, '.numpad');
}
function verify_order_total(total_str) {
return [{
title: 'order total contains ' + total_str,
waitFor: '.order .total .value:contains("' + total_str + '")',
}];
}
function goto_payment_screen_and_select_payment_method() {
return [{
title: "go to payment screen",
element: '.button.pay',
}, {
title: "pay with cash",
element: '.paymentmethod:contains("Cash")',
}];
}
function finish_order() {
return [{
title: "validate the order",
element: '.button.next:visible',
}, {
title: "verify that the order is being sent to the backend",
waitFor: ".js_connecting:visible",
}, {
title: "verify that the order has been succesfully sent to the backend",
waitFor: ".js_connected:visible",
}, {
title: "next order",
element: '.button.next:visible',
}];
}
var steps = [{
title: 'wait for loading screen',
waitFor: '.loader'
},
{
title: 'Waiting for the end of loading...',
}, {
title: 'waiting for loading to finish',
waitFor: '.loader:hidden',
},
{
title: 'Loading The Point of Sale',
waitFor: '.pos',
},
{
title: 'On va manger des CHIPS!',
element: '.product-list .product-name:contains("250g Lays Pickels")',
},
{
title: 'The chips have been added to the Order',
waitFor: '.order .product-name:contains("250g Lays Pickels")',
},
{
title: 'The order total has been updated to the correct value',
wait: 2000,
waitFor: '.order .total .value:contains("1.48 €")',
},
{
title: "Let's buy more chips",
element: '.product-list .product-name:contains("250g Lays Pickels")',
},
{
title: "Let's veryify we pay the correct price for two bags of chips",
waitFor: '.order .total .value:contains("2.96 €")',
},
{
title: "Let's pay with a debit card",
element: ".paypad-button:contains('Bank')",
},
{
title: "Let's accept the payment",
onload: function(){
// The test cannot validate or cancel the print() ... so we replace it by a noop !.
window._print = window.print;
window.print = function(){ console.log('Print!') };
},
element: ".button .iconlabel:contains('Validate'):visible",
},
{
title: "Let's finish the order",
element: ".button:not(.disabled) .iconlabel:contains('Next'):visible",
},
{
onload: function(){
window.print = window._print;
window._print = undefined;
},
title: "Let's wait for the order posting",
waitFor: ".oe_status.js_synch .js_connected:visible",
},
{
title: "Let's close the Point of Sale",
element: ".header-button:contains('Close')",
},
{
title: "Wait for the backend to ready itself",
element: 'span:contains("Resume Session"),span:contains("Start Session")',
},
],
});
}];
steps = steps.concat(add_product_to_order('Peaches'));
steps = steps.concat(verify_order_total('5.10'));
steps = steps.concat(add_product_to_order('Peaches')); // buy another kg of peaches
steps = steps.concat(verify_order_total('10.20'));
steps = steps.concat(goto_payment_screen_and_select_payment_method());
steps = steps.concat(generate_payment_screen_keypad_steps("12.20"));
steps = steps.concat([{
title: "verify tendered",
waitFor: '.col-tendered:contains("12.20")',
}, {
title: "verify change",
waitFor: '.col-change:contains("2.00")',
}]);
steps = steps.concat(finish_order());
// test opw-672118 orderline subtotal rounding
steps = steps.concat(add_product_to_order('Peaches'));
steps = steps.concat(generate_product_screen_keypad_steps('.999')); // sets orderline qty
steps = steps.concat(verify_order_total('5.09'));
steps = steps.concat(goto_payment_screen_and_select_payment_method());
steps = steps.concat(generate_payment_screen_keypad_steps("10"));
steps = steps.concat(finish_order());
steps = steps.concat([{
title: "close the Point of Sale frontend",
element: ".header-button",
}, {
title: "confirm closing the frontend",
element: ".header-button",
}]);
Tour.register({
id: 'pos_basic_order',
name: 'Complete a basic order trough the Front-End',
path: '/pos/web',
steps: steps,
});
});
@@ -1,8 +0,0 @@
import openerp.tests
@openerp.tests.common.at_install(False)
@openerp.tests.common.post_install(True)
class TestUi(openerp.tests.HttpCase):
def test_01_pos_basic_order(self):
self.phantom_js("/", "odoo.__DEBUG__.services['web.Tour'].run('pos_basic_order', 'test')", "odoo.__DEBUG__.services['web.Tour'].tours.pos_basic_order", login="admin")
+2 -1
View File
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import test_point_of_sale_flow
import test_point_of_sale_flow
import test_frontend
@@ -0,0 +1,61 @@
import odoo.tests
from openerp.api import Environment
class TestUi(odoo.tests.HttpCase):
def test_01_pos_basic_order(self):
cr = self.registry.cursor()
assert cr == self.registry.test_cr
env = Environment(cr, self.uid, {})
journal_obj = env['account.journal']
account_obj = env['account.account']
main_company = env.ref('base.main_company')
main_pos_config = env.ref('point_of_sale.pos_config_main')
account_receivable = account_obj.create({'code': 'X1012',
'name': 'Account Receivable - Test',
'user_type_id': env.ref('account.data_account_type_receivable').id,
'reconcile': True})
field = self.env['ir.model.fields'].search([('name', '=', 'property_account_receivable_id'),
('model', '=', 'res.partner'),
('relation', '=', 'account.account')], limit=1)
env['ir.property'].create({'name': 'property_account_receivable_id',
'company_id': main_company.id,
'fields_id': field.id,
'value': 'account.account,' + str(account_receivable.id)})
# set the company currency to USD, otherwise it will assume
# euro's. this will cause issues as the sale journal is in
# USD, because of this all products would have a different
# price
main_company.currency_id = env.ref('base.USD')
test_sale_journal = journal_obj.create({'name': 'Sale Journal - Test',
'code': 'TSJ',
'type': 'sale',
'company_id': main_company.id})
main_pos_config.journal_id = test_sale_journal
main_pos_config.write({'journal_ids': [(0, 0, {'name': 'Cash Journal - Test',
'code': 'TSC',
'type': 'cash',
'company_id': main_company.id,
'journal_user': True})]})
# open a session, the /pos/web controller will redirect to it
main_pos_config.open_session_cb()
# needed because tests are run before the module is marked as
# installed. In js web will only load qweb coming from modules
# that are returned by the backend in module_boot. Without
# this you end up with js, css but no qweb.
env['ir.module.module'].search([('name', '=', 'point_of_sale')], limit=1).state = 'installed'
cr.release()
self.phantom_js("/pos/web",
"odoo.__DEBUG__.services['web.Tour'].run('pos_basic_order', 'test')",
"odoo.__DEBUG__.services['web.Tour'].tours.pos_basic_order",
login="admin")
for order in env['pos.order'].search([]):
self.assertEqual(order.state, 'paid', "Validated order has payment of " + str(order.amount_paid) + " and total of " + str(order.amount_total))