From 106dc30bebf49335eb7d2450a55d14cd46469e17 Mon Sep 17 00:00:00 2001 From: FrancoisGe Date: Thu, 6 Oct 2022 07:33:25 +0000 Subject: [PATCH] [FIX] barcodes: barcode_trigger does not work in the new view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before this commit, in new views, it is no longer possible to click on buttons with barcode_trigger="..." using a barcode scanner. Why: New views no longer propagate all attributes to the button. How to reproduce: - Go to a form view with a button with barcode_trigger="doit" - Scan "O-BTN.doit", "Enter". Before this commit: Nothing happens. After this commit: An event click is applied to the button. closes odoo/odoo#102423 X-original-commit: b4cb2ee2662173e08f671eb736130f8332265de7 Signed-off-by: Georis François (fge) --- addons/barcodes/__manifest__.py | 2 +- .../static/tests/basic/barcode_tests.js | 510 +++++++----------- .../{ => legacy}/barcode_parser_tests.js | 0 .../tests/basic/legacy/barcode_tests.js | 337 ++++++++++++ .../src/views/view_button/view_button.js | 2 +- .../src/views/view_button/view_button.xml | 2 +- addons/web/static/src/views/view_compiler.js | 28 +- .../views/fields/one2many_field_tests.js | 4 +- 8 files changed, 552 insertions(+), 333 deletions(-) rename addons/barcodes/static/tests/basic/{ => legacy}/barcode_parser_tests.js (100%) create mode 100644 addons/barcodes/static/tests/basic/legacy/barcode_tests.js diff --git a/addons/barcodes/__manifest__.py b/addons/barcodes/__manifest__.py index 96cc1fcdc59..162fd973308 100644 --- a/addons/barcodes/__manifest__.py +++ b/addons/barcodes/__manifest__.py @@ -16,7 +16,7 @@ 'barcodes/static/src/**/*', ], 'web.tests_assets': ['barcodes/static/tests/helpers.js'], - 'web.qunit_suite_tests': ['barcodes/static/tests/basic/*.js'], + 'web.qunit_suite_tests': ['barcodes/static/tests/basic/**/*.js'], 'web.qunit_mobile_suite_tests': ['barcodes/static/tests/mobile/*.js'], }, 'license': 'LGPL-3', diff --git a/addons/barcodes/static/tests/basic/barcode_tests.js b/addons/barcodes/static/tests/basic/barcode_tests.js index 15b2cc04536..f9af853d6b0 100644 --- a/addons/barcodes/static/tests/basic/barcode_tests.js +++ b/addons/barcodes/static/tests/basic/barcode_tests.js @@ -1,337 +1,217 @@ -odoo.define('barcodes.tests', function (require) { -"use strict"; +/** @odoo-module **/ -const {barcodeService} = require("@barcodes/barcode_service"); -const {barcodeGenericHandlers} = require("@barcodes/barcode_handlers"); -const {barcodeRemapperService} = require("@barcodes/js/barcode_events"); -const { makeTestEnv } = require("@web/../tests/helpers/mock_env"); -const { registry } = require("@web/core/registry"); -const { mockTimeout } = require("@web/../tests/helpers/utils"); -const { simulateBarCode } = require("@barcodes/../tests/helpers"); +import { barcodeGenericHandlers } from "@barcodes/barcode_handlers"; +import { barcodeService } from "@barcodes/barcode_service"; +import { + editInput, + getFixture, + mockTimeout, + nextTick, + patchWithCleanup, +} from "@web/../tests/helpers/utils"; +import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; +import { registry } from "@web/core/registry"; +import { simulateBarCode } from "../helpers"; -var FormController = require('web.FormController'); -var FormView = require('web.FormView'); -var testUtils = require('web.test_utils'); +const serviceRegistry = registry.category("services"); -var createView = testUtils.createView; -var core = require('web.core'); +let serverData; +let target; -const maxTimeBetweenKeysInMs = barcodeService.maxTimeBetweenKeysInMs; - -QUnit.module('Barcodes', { - beforeEach: function () { - barcodeService.maxTimeBetweenKeysInMs = 0; - registry.category("services").add("barcode", barcodeService, { force: true}); - registry.category("services").add("barcode_autoclick", barcodeGenericHandlers, { force: true}); - // remove this one later - registry.category("services").add("barcode_remapper", barcodeRemapperService); - this.env = makeTestEnv(); - - this.data = { - order: { - fields: { - _barcode_scanned: {string: 'Barcode scanned', type: 'char'}, - line_ids: {string: 'Order lines', type: 'one2many', relation: 'order_line'}, +QUnit.module("Barcodes", (hooks) => { + hooks.beforeEach(() => { + target = getFixture(); + serverData = { + models: { + order: { + fields: { + _barcode_scanned: { string: "Barcode scanned", type: "char" }, + line_ids: { + string: "Order lines", + type: "one2many", + relation: "order_line", + }, + }, + records: [{ id: 1, line_ids: [1, 2] }], }, - records: [ - {id: 1, line_ids: [1, 2]}, - ], - }, - order_line: { - fields: { - product_id: {string: 'Product', type: 'many2one', relation: 'product'}, - product_barcode: {string: 'Product Barcode', type: 'char'}, - quantity: {string: 'Quantity', type: 'integer'}, + order_line: { + fields: { + product_id: { string: "Product", type: "many2one", relation: "product" }, + product_barcode: { string: "Product Barcode", type: "char" }, + quantity: { string: "Quantity", type: "integer" }, + }, + records: [ + { id: 1, product_id: 1, quantity: 0, product_barcode: "1234567890" }, + { id: 2, product_id: 2, quantity: 0, product_barcode: "0987654321" }, + ], }, - records: [ - {id: 1, product_id: 1, quantity: 0, product_barcode: '1234567890'}, - {id: 2, product_id: 2, quantity: 0, product_barcode: '0987654321'}, - ], - }, - product: { - fields: { - name: {string : "Product name", type: "char"}, - int_field: {string : "Integer", type: "integer"}, - int_field_2: {string : "Integer", type: "integer"}, - barcode: {string: "Barcode", type: "char"}, + product: { + fields: { + name: { string: "Product name", type: "char" }, + int_field: { string: "Integer", type: "integer" }, + int_field_2: { string: "Integer", type: "integer" }, + barcode: { string: "Barcode", type: "char" }, + }, + records: [ + { id: 1, name: "Large Cabinet", barcode: "1234567890" }, + { id: 2, name: "Cabinet with Doors", barcode: "0987654321" }, + ], }, - records: [ - {id: 1, name: "Large Cabinet", barcode: '1234567890'}, - {id: 2, name: "Cabinet with Doors", barcode: '0987654321'}, - ], }, }; - }, - afterEach: function () { - barcodeService.maxTimeBetweenKeysInMs = maxTimeBetweenKeysInMs; - }, -}); -QUnit.test('Button with barcode_trigger', async function (assert) { - assert.expect(2); + setupViewRegistries(); + serviceRegistry.add("barcode", barcodeService, { force: true }); + serviceRegistry.add("barcode_autoclick", barcodeGenericHandlers, { force: true }); + }); - var form = await createView({ - View: FormView, - model: 'product', - data: this.data, - arch: '
' + - '
' + - '
' + - '
', - res_id: 2, - services: { - notification: { - notify: function (params) { + QUnit.test("Button with barcode_trigger", async (assert) => { + const form = await makeView({ + type: "form", + resModel: "product", + serverData, + arch: `
+
+
+
`, + resId: 2, + }); + + patchWithCleanup(form.env.services.action, { + doActionButton: (data) => { + assert.step(data.name); + }, + }); + patchWithCleanup(form.env.services.notification, { + add: (params) => { + assert.step(params.type); + }, + }); + + // O-BTN.doit + simulateBarCode(["O", "-", "B", "T", "N", ".", "d", "o", "i", "t", "Enter"]); + await nextTick(); + // O-BTN.dothat (should not call execute_action as the button isn't visible) + simulateBarCode(["O", "-", "B", "T", "N", ".", "d", "o", "t", "h", "a", "t", "Enter"]); + await nextTick(); + assert.verifySteps(["do_something"]); + + assert.containsOnce(target, ".o_form_statusbar > .o_statusbar_buttons"); + }); + + QUnit.test( + "Two buttons with same barcode_trigger and the same string and action", + async function (assert) { + const form = await makeView({ + type: "form", + resModel: "product", + serverData, + arch: `
+
+
+
`, + resId: 2, + }); + + patchWithCleanup(form.env.services.action, { + doActionButton: (data) => { + assert.step(data.name); + }, + }); + patchWithCleanup(form.env.services.notification, { + add: (params) => { assert.step(params.type); + }, + }); + // O-BTN.doit should call execute_action as the first button is visible + simulateBarCode(["O", "-", "B", "T", "N", ".", "d", "o", "i", "t", "Enter"]); + await nextTick(); + assert.verifySteps(["do_something"]); + } + ); + + QUnit.test("edit, save and cancel buttons", async function (assert) { + await makeView({ + type: "form", + resModel: "product", + serverData, + arch: '
', + mockRPC: function (route, args) { + if (args.method === "write") { + assert.step("save"); } }, - }, - intercepts: { - execute_action: function (event) { - assert.strictEqual(event.data.action_data.name, 'do_something', - "do_something method call verified"); - }, - }, + resId: 1, + }); + + // O-CMD.EDIT + simulateBarCode(["O", "-", "C", "M", "D", ".", "E", "D", "I", "T", "Enter"]); + await nextTick(); + assert.containsOnce(target, ".o_form_editable", "should have switched to 'edit' mode"); + // dummy change to check that it actually saves + await editInput(target.querySelector(".o_field_widget input"), null, "test"); + // O-CMD.SAVE + simulateBarCode(["O", "-", "C", "M", "D", ".", "S", "A", "V", "E", "Enter"]); + await nextTick(); + assert.verifySteps(["save"], "should have saved"); + + // O-CMD.EDIT + simulateBarCode(["O", "-", "C", "M", "D", ".", "E", "D", "I", "T", "Enter"]); + await nextTick(); + // dummy change to check that it correctly discards + await editInput(target.querySelector(".o_field_widget input"), null, "test"); + // O-CMD.CANCEL + simulateBarCode(["O", "-", "C", "M", "D", ".", "D", "I", "S", "C", "A", "R", "D", "Enter"]); + await nextTick(); + assert.verifySteps([], "should not have saved"); }); - // O-BTN.doit - simulateBarCode(['O','-','B','T','N','.','d','o','i','t','Enter']); - // O-BTN.dothat (should not call execute_action as the button isn't visible) - simulateBarCode(['O','-','B','T','N','.','d','o','t','h','a','t','Enter']); - await testUtils.nextTick(); - assert.verifySteps([], "no warning should be displayed"); + QUnit.test("pager buttons", async function (assert) { + const mock = mockTimeout(); - form.destroy(); -}); + await makeView({ + type: "form", + resModel: "product", + serverData, + arch: '
', + resId: 1, + resIds: [1, 2], + }); -QUnit.test('Two buttons with same barcode_trigger and the same string and action', async function (assert) { - assert.expect(2); + assert.strictEqual(target.querySelector(".o_field_widget input").value, "Large Cabinet"); - var form = await createView({ - View: FormView, - model: 'product', - data: this.data, - arch: '
' + - '
' + - '
' + - '
', - res_id: 2, + // O-CMD.PAGER-NEXT + simulateBarCode(["O", "-", "C", "M", "D", ".", "N", "E", "X", "T", "Enter"]); + await nextTick(); + assert.strictEqual( + target.querySelector(".o_field_widget input").value, + "Cabinet with Doors" + ); - services: { - notification: { - notify: function (params) { - assert.step(params.type); - } - }, - }, - intercepts: { - execute_action: function (event) { - assert.strictEqual(event.data.action_data.name, 'do_something', - "do_something method call verified"); - }, - }, + // O-CMD.PAGER-PREV + simulateBarCode(["O", "-", "C", "M", "D", ".", "P", "R", "E", "V", "Enter"]); + await nextTick(); + assert.strictEqual(target.querySelector(".o_field_widget input").value, "Large Cabinet"); + + // O-CMD.PAGER-LAST + simulateBarCode(["O","-","C","M","D",".","P","A","G","E","R","-","L","A","S","T","Enter"]); + // need to await 2 macro steps + await mock.advanceTime(20); + await mock.advanceTime(20); + assert.strictEqual( + target.querySelector(".o_field_widget input").value, + "Cabinet with Doors" + ); + + // O-CMD.PAGER-FIRST + simulateBarCode(["O","-","C","M","D",".","P","A","G","E","R","-","F","I","R","S","T","Enter"]); + // need to await 2 macro steps + await mock.advanceTime(20); + await mock.advanceTime(20); + assert.strictEqual(target.querySelector(".o_field_widget input").value, "Large Cabinet"); }); - - // O-BTN.doit should call execute_action as the first button is visible - simulateBarCode(['O','-','B','T','N','.','d','o','i','t','Enter']); - await testUtils.nextTick(); - assert.verifySteps([], "no warning should be displayed"); - - form.destroy(); -}); - -QUnit.test('edit, save and cancel buttons', async function (assert) { - assert.expect(6); - - var form = await createView({ - View: FormView, - model: 'product', - data: this.data, - arch: '
', - mockRPC: function (route, args) { - if (args.method === 'write') { - assert.step('save'); - } - return this._super.apply(this, arguments); - }, - res_id: 1, - }); - - // O-CMD.EDIT - simulateBarCode(["O","-","C","M","D",".","E","D","I","T","Enter"], document.body); - await testUtils.nextTick(); - assert.containsOnce(form, ".o_form_editable", - "should have switched to 'edit' mode"); - // dummy change to check that it actually saves - await testUtils.fields.editInput(form.$('.o_field_widget'), 'test'); - // O-CMD.SAVE - simulateBarCode(["O","-","C","M","D",".","S","A","V","E","Enter"], document.body); - await testUtils.nextTick(); - assert.containsOnce(form, ".o_form_readonly", - "should have switched to 'readonly' mode"); - assert.verifySteps(['save'], 'should have saved'); - - // O-CMD.EDIT - simulateBarCode(["O","-","C","M","D",".","E","D","I","T","Enter"], document.body); - await testUtils.nextTick(); - // dummy change to check that it correctly discards - await testUtils.fields.editInput(form.$('.o_field_widget'), 'test'); - // O-CMD.CANCEL - simulateBarCode(["O","-","C","M","D",".","D","I","S","C","A","R","D","Enter"], document.body); - await testUtils.nextTick(); - assert.containsOnce(form, ".o_form_readonly", - "should have switched to 'readonly' mode"); - assert.verifySteps([], 'should not have saved'); - - form.destroy(); -}); - -QUnit.test('pager buttons', async function (assert) { - const mock = mockTimeout(); - assert.expect(5); - - var form = await createView({ - View: FormView, - model: 'product', - data: this.data, - arch: '
', - res_id: 1, - viewOptions: { - ids: [1, 2], - index: 0, - }, - }); - - assert.strictEqual(form.$('.o_field_widget').text(), 'Large Cabinet'); - // O-CMD.PAGER-NEXT - simulateBarCode(["O","-","C","M","D",".","N","E","X","T","Enter"]); - await testUtils.nextTick(); - assert.strictEqual(form.$('.o_field_widget').text(), 'Cabinet with Doors'); - // O-CMD.PAGER-PREV - simulateBarCode(["O","-","C","M","D",".","P","R","E","V","Enter"]); - await testUtils.nextTick(); - assert.strictEqual(form.$('.o_field_widget').text(), 'Large Cabinet'); - // O-CMD.PAGER-LAST - simulateBarCode(["O","-","C","M","D",".","P","A","G","E","R","-","L","A","S","T","Enter"]); - // need to await 2 macro steps - await mock.advanceTime(20); - await mock.advanceTime(20); - assert.strictEqual(form.$('.o_field_widget').text(), 'Cabinet with Doors'); - // O-CMD.PAGER-FIRST - simulateBarCode(["O","-","C","M","D",".","P","A","G","E","R","-","F","I","R","S","T","Enter"]); - // need to await 2 macro steps - await mock.advanceTime(20); - await mock.advanceTime(20); - assert.strictEqual(form.$('.o_field_widget').text(), 'Large Cabinet'); - - form.destroy(); -}); - -QUnit.test('do no update form twice after a command barcode scanned', async function (assert) { - assert.expect(5); - - testUtils.mock.patch(FormController, { - update: function () { - assert.step('update'); - return this._super.apply(this, arguments); - }, - }); - - var form = await createView({ - View: FormView, - model: 'product', - data: this.data, - arch: '
' + - '' + - '' + - '', - mockRPC: function (route, args) { - if (args.method === 'read') { - assert.step('read'); - } - return this._super.apply(this, arguments); - }, - res_id: 1, - viewOptions: { - ids: [1, 2], - index: 0, - }, - }); - - assert.verifySteps(['read'], "update should not have been called yet"); - - // switch to next record - simulateBarCode(["O","-","C","M","D",".","N","E","X","T","Enter"]); - await testUtils.nextTick(); - // a first update is done to reload the data (thus followed by a read), but - // update shouldn't be called afterwards - assert.verifySteps(['update', 'read']); - - form.destroy(); - testUtils.mock.unpatch(FormController); -}); - -QUnit.test('widget field_float_scannable', async function (assert) { - this.data.product.records[0].int_field = 4; - - function _onBarcodeScanned (code) { - assert.step(`barcode scanned ${code}`) - } - core.bus.on('barcode_scanned', null, _onBarcodeScanned); - - var form = await createView({ - View: FormView, - model: 'product', - data: this.data, - arch: '
' + - '' + - '' + - '' + - '', - mockRPC: function (route, args) { - if (args.method === 'onchange') { - assert.step('onchange'); - assert.strictEqual(args.args[1].int_field, 426, - "should send correct value for int_field"); - } - return this._super.apply(this, arguments); - }, - fieldDebounce: 1000, - res_id: 1, - }); - - assert.strictEqual(form.$('.o_field_widget[name=int_field]').text(), '4', - "should display the correct value in readonly"); - - await testUtils.form.clickEdit(form); - - assert.strictEqual(form.$('.o_field_widget[name=int_field]').val(), '4', - "should display the correct value in edit"); - - // simulates keypress events in the input to replace 0.00 by 26 (should not trigger onchanges) - form.$('.o_field_widget[name=int_field]').focus(); - - // we check here that a scan on the fieldflotscannable widget triggers a - // barcode event - simulateBarCode(["6", "0", "1", "6", "4", "7", "8", "5"], document.activeElement) - await testUtils.nextTick(); - assert.verifySteps(['barcode scanned 60164785']); - assert.strictEqual(form.$('.o_field_widget[name=int_field]').get(0), document.activeElement, - "int field should be focused"); - - // we check here that a scan on the field without widget does not trigger a - // barcode event - form.$('.o_field_widget[name=int_field_2]').focus(); - simulateBarCode(["6", "0", "1", "6", "4", "7", "8", "5"], document.activeElement) - await testUtils.nextTick(); - assert.verifySteps([]); - - form.destroy(); - core.bus.off('barcode_scanned', null, _onBarcodeScanned) -}); - }); diff --git a/addons/barcodes/static/tests/basic/barcode_parser_tests.js b/addons/barcodes/static/tests/basic/legacy/barcode_parser_tests.js similarity index 100% rename from addons/barcodes/static/tests/basic/barcode_parser_tests.js rename to addons/barcodes/static/tests/basic/legacy/barcode_parser_tests.js diff --git a/addons/barcodes/static/tests/basic/legacy/barcode_tests.js b/addons/barcodes/static/tests/basic/legacy/barcode_tests.js new file mode 100644 index 00000000000..e56cdb187ad --- /dev/null +++ b/addons/barcodes/static/tests/basic/legacy/barcode_tests.js @@ -0,0 +1,337 @@ +odoo.define('barcodes.tests', function (require) { +"use strict"; + +const {barcodeService} = require("@barcodes/barcode_service"); +const {barcodeGenericHandlers} = require("@barcodes/barcode_handlers"); +const {barcodeRemapperService} = require("@barcodes/js/barcode_events"); +const { makeTestEnv } = require("@web/../tests/helpers/mock_env"); +const { registry } = require("@web/core/registry"); +const { mockTimeout } = require("@web/../tests/helpers/utils"); +const { simulateBarCode } = require("@barcodes/../tests/helpers"); + +var FormController = require('web.FormController'); +var FormView = require('web.FormView'); +var testUtils = require('web.test_utils'); + +var createView = testUtils.createView; +var core = require('web.core'); + +const maxTimeBetweenKeysInMs = barcodeService.maxTimeBetweenKeysInMs; + +QUnit.module('Legacy Barcodes', { + beforeEach: function () { + barcodeService.maxTimeBetweenKeysInMs = 0; + registry.category("services").add("barcode", barcodeService, { force: true}); + registry.category("services").add("barcode_autoclick", barcodeGenericHandlers, { force: true}); + // remove this one later + registry.category("services").add("barcode_remapper", barcodeRemapperService); + this.env = makeTestEnv(); + + this.data = { + order: { + fields: { + _barcode_scanned: {string: 'Barcode scanned', type: 'char'}, + line_ids: {string: 'Order lines', type: 'one2many', relation: 'order_line'}, + }, + records: [ + {id: 1, line_ids: [1, 2]}, + ], + }, + order_line: { + fields: { + product_id: {string: 'Product', type: 'many2one', relation: 'product'}, + product_barcode: {string: 'Product Barcode', type: 'char'}, + quantity: {string: 'Quantity', type: 'integer'}, + }, + records: [ + {id: 1, product_id: 1, quantity: 0, product_barcode: '1234567890'}, + {id: 2, product_id: 2, quantity: 0, product_barcode: '0987654321'}, + ], + }, + product: { + fields: { + name: {string : "Product name", type: "char"}, + int_field: {string : "Integer", type: "integer"}, + int_field_2: {string : "Integer", type: "integer"}, + barcode: {string: "Barcode", type: "char"}, + }, + records: [ + {id: 1, name: "Large Cabinet", barcode: '1234567890'}, + {id: 2, name: "Cabinet with Doors", barcode: '0987654321'}, + ], + }, + }; + }, + afterEach: function () { + barcodeService.maxTimeBetweenKeysInMs = maxTimeBetweenKeysInMs; + }, +}); + +QUnit.test('Button with barcode_trigger', async function (assert) { + assert.expect(2); + + var form = await createView({ + View: FormView, + model: 'product', + data: this.data, + arch: '
' + + '
' + + '
' + + '
', + res_id: 2, + services: { + notification: { + notify: function (params) { + assert.step(params.type); + } + }, + }, + intercepts: { + execute_action: function (event) { + assert.strictEqual(event.data.action_data.name, 'do_something', + "do_something method call verified"); + }, + }, + }); + + // O-BTN.doit + simulateBarCode(['O','-','B','T','N','.','d','o','i','t','Enter']); + // O-BTN.dothat (should not call execute_action as the button isn't visible) + simulateBarCode(['O','-','B','T','N','.','d','o','t','h','a','t','Enter']); + await testUtils.nextTick(); + assert.verifySteps([], "no warning should be displayed"); + + form.destroy(); +}); + +QUnit.test('Two buttons with same barcode_trigger and the same string and action', async function (assert) { + assert.expect(2); + + var form = await createView({ + View: FormView, + model: 'product', + data: this.data, + arch: '
' + + '
' + + '
' + + '
', + res_id: 2, + + services: { + notification: { + notify: function (params) { + assert.step(params.type); + } + }, + }, + intercepts: { + execute_action: function (event) { + assert.strictEqual(event.data.action_data.name, 'do_something', + "do_something method call verified"); + }, + }, + }); + + // O-BTN.doit should call execute_action as the first button is visible + simulateBarCode(['O','-','B','T','N','.','d','o','i','t','Enter']); + await testUtils.nextTick(); + assert.verifySteps([], "no warning should be displayed"); + + form.destroy(); +}); + +QUnit.test('edit, save and cancel buttons', async function (assert) { + assert.expect(6); + + var form = await createView({ + View: FormView, + model: 'product', + data: this.data, + arch: '
', + mockRPC: function (route, args) { + if (args.method === 'write') { + assert.step('save'); + } + return this._super.apply(this, arguments); + }, + res_id: 1, + }); + + // O-CMD.EDIT + simulateBarCode(["O","-","C","M","D",".","E","D","I","T","Enter"], document.body); + await testUtils.nextTick(); + assert.containsOnce(form, ".o_form_editable", + "should have switched to 'edit' mode"); + // dummy change to check that it actually saves + await testUtils.fields.editInput(form.$('.o_field_widget'), 'test'); + // O-CMD.SAVE + simulateBarCode(["O","-","C","M","D",".","S","A","V","E","Enter"], document.body); + await testUtils.nextTick(); + assert.containsOnce(form, ".o_form_readonly", + "should have switched to 'readonly' mode"); + assert.verifySteps(['save'], 'should have saved'); + + // O-CMD.EDIT + simulateBarCode(["O","-","C","M","D",".","E","D","I","T","Enter"], document.body); + await testUtils.nextTick(); + // dummy change to check that it correctly discards + await testUtils.fields.editInput(form.$('.o_field_widget'), 'test'); + // O-CMD.CANCEL + simulateBarCode(["O","-","C","M","D",".","D","I","S","C","A","R","D","Enter"], document.body); + await testUtils.nextTick(); + assert.containsOnce(form, ".o_form_readonly", + "should have switched to 'readonly' mode"); + assert.verifySteps([], 'should not have saved'); + + form.destroy(); +}); + +QUnit.test('pager buttons', async function (assert) { + const mock = mockTimeout(); + assert.expect(5); + + var form = await createView({ + View: FormView, + model: 'product', + data: this.data, + arch: '
', + res_id: 1, + viewOptions: { + ids: [1, 2], + index: 0, + }, + }); + + assert.strictEqual(form.$('.o_field_widget').text(), 'Large Cabinet'); + // O-CMD.PAGER-NEXT + simulateBarCode(["O","-","C","M","D",".","N","E","X","T","Enter"]); + await testUtils.nextTick(); + assert.strictEqual(form.$('.o_field_widget').text(), 'Cabinet with Doors'); + // O-CMD.PAGER-PREV + simulateBarCode(["O","-","C","M","D",".","P","R","E","V","Enter"]); + await testUtils.nextTick(); + assert.strictEqual(form.$('.o_field_widget').text(), 'Large Cabinet'); + // O-CMD.PAGER-LAST + simulateBarCode(["O","-","C","M","D",".","P","A","G","E","R","-","L","A","S","T","Enter"]); + // need to await 2 macro steps + await mock.advanceTime(20); + await mock.advanceTime(20); + assert.strictEqual(form.$('.o_field_widget').text(), 'Cabinet with Doors'); + // O-CMD.PAGER-FIRST + simulateBarCode(["O","-","C","M","D",".","P","A","G","E","R","-","F","I","R","S","T","Enter"]); + // need to await 2 macro steps + await mock.advanceTime(20); + await mock.advanceTime(20); + assert.strictEqual(form.$('.o_field_widget').text(), 'Large Cabinet'); + + form.destroy(); +}); + +QUnit.test('do no update form twice after a command barcode scanned', async function (assert) { + assert.expect(5); + + testUtils.mock.patch(FormController, { + update: function () { + assert.step('update'); + return this._super.apply(this, arguments); + }, + }); + + var form = await createView({ + View: FormView, + model: 'product', + data: this.data, + arch: '
' + + '' + + '' + + '', + mockRPC: function (route, args) { + if (args.method === 'read') { + assert.step('read'); + } + return this._super.apply(this, arguments); + }, + res_id: 1, + viewOptions: { + ids: [1, 2], + index: 0, + }, + }); + + assert.verifySteps(['read'], "update should not have been called yet"); + + // switch to next record + simulateBarCode(["O","-","C","M","D",".","N","E","X","T","Enter"]); + await testUtils.nextTick(); + // a first update is done to reload the data (thus followed by a read), but + // update shouldn't be called afterwards + assert.verifySteps(['update', 'read']); + + form.destroy(); + testUtils.mock.unpatch(FormController); +}); + +QUnit.test('widget field_float_scannable', async function (assert) { + this.data.product.records[0].int_field = 4; + + function _onBarcodeScanned (code) { + assert.step(`barcode scanned ${code}`) + } + core.bus.on('barcode_scanned', null, _onBarcodeScanned); + + var form = await createView({ + View: FormView, + model: 'product', + data: this.data, + arch: '
' + + '' + + '' + + '' + + '', + mockRPC: function (route, args) { + if (args.method === 'onchange') { + assert.step('onchange'); + assert.strictEqual(args.args[1].int_field, 426, + "should send correct value for int_field"); + } + return this._super.apply(this, arguments); + }, + fieldDebounce: 1000, + res_id: 1, + }); + + assert.strictEqual(form.$('.o_field_widget[name=int_field]').text(), '4', + "should display the correct value in readonly"); + + await testUtils.form.clickEdit(form); + + assert.strictEqual(form.$('.o_field_widget[name=int_field]').val(), '4', + "should display the correct value in edit"); + + // simulates keypress events in the input to replace 0.00 by 26 (should not trigger onchanges) + form.$('.o_field_widget[name=int_field]').focus(); + + // we check here that a scan on the fieldflotscannable widget triggers a + // barcode event + simulateBarCode(["6", "0", "1", "6", "4", "7", "8", "5"], document.activeElement) + await testUtils.nextTick(); + assert.verifySteps(['barcode scanned 60164785']); + assert.strictEqual(form.$('.o_field_widget[name=int_field]').get(0), document.activeElement, + "int field should be focused"); + + // we check here that a scan on the field without widget does not trigger a + // barcode event + form.$('.o_field_widget[name=int_field_2]').focus(); + simulateBarCode(["6", "0", "1", "6", "4", "7", "8", "5"], document.activeElement) + await testUtils.nextTick(); + assert.verifySteps([]); + + form.destroy(); + core.bus.off('barcode_scanned', null, _onBarcodeScanned) +}); + +}); diff --git a/addons/web/static/src/views/view_button/view_button.js b/addons/web/static/src/views/view_button/view_button.js index e9c7222a764..b6181c2a4de 100644 --- a/addons/web/static/src/views/view_button/view_button.js +++ b/addons/web/static/src/views/view_button/view_button.js @@ -129,10 +129,10 @@ ViewButton.props = [ "id?", "tag?", "record?", + "attrs?", "className?", "context?", "clickParams?", - "hotkey?", "icon?", "defaultRank?", "disable?", diff --git a/addons/web/static/src/views/view_button/view_button.xml b/addons/web/static/src/views/view_button/view_button.xml index 029cc22d641..b06e58f4b94 100644 --- a/addons/web/static/src/views/view_button/view_button.xml +++ b/addons/web/static/src/views/view_button/view_button.xml @@ -3,6 +3,7 @@ { assert.notOk(target.querySelector(btn2Warn).disabled); assert.strictEqual( target.querySelector(btn2Warn).getAttribute("warn"), - null, - "the warn attribute is not copied onto the button" + "warn", + "Should have a button type object with warn attr in area 2" ); // click all buttons