CONTEXT: emoji_char_field and emoji_text_field previously used the emoji_dropdown component along with some utility functions to display a very limited number of emojis. As discuss now has a feature-full emoji picker we aim to take advantage of it by bootstrapping the picker from the existing components, using a new javascript model for the insert/update logic AFTER THE CHANGE: Users should be able to select the same emojis they can use in the discuss chat in all fields that previously featured an emoji picker. Such as the livechat bot responses, social posts, mailing subjects and the sms message composer. RELATED CHANGES: Some tests are updated to use the pyEnv testing 'framework', as we now need to have access to the messaging service to test these fields. emoji_mixin is modified to match any emoji character. The mixin is consolidated into a single function is actually called anymore. The text to emoji dict from emojis.js is removed as it is no longer relevant where it was used. The fields that used the emoji_mixin will no longer trigger onChange on inserting an emoji. However both the input and keydown event are fired to allow fields using onchange_on_keydown to trigger the onchange. The emoji button was turned into an actual button replacing the original span used in these fields. This required a different approach to styling as buttons inherently behave differently than spans. The button is now moved using an empty div next to the input and absolute positioning. Most of the code used for char and text emoji fields is consolidated into emojis_field_common. task-3035021 closes odoo/odoo#104799 Signed-off-by: Warnon Aurélien (awa) <awa@odoo.com>
264 lines
12 KiB
JavaScript
264 lines
12 KiB
JavaScript
/** @odoo-module **/
|
|
|
|
import { addFakeModel } from '@bus/../tests/helpers/model_definitions_helpers';
|
|
import { click, getFixture } from "@web/../tests/helpers/utils";
|
|
import FormView from 'web.FormView';
|
|
import ListView from 'web.ListView';
|
|
import { setupViewRegistries } from "@web/../tests/views/helpers";
|
|
import { start, startServer } from '@mail/../tests/helpers/test_utils';
|
|
import testUtils from 'web.test_utils';
|
|
|
|
const createView = testUtils.createView;
|
|
|
|
|
|
|
|
QUnit.module('SmsWidget', (hooks) => {
|
|
|
|
let target = undefined;
|
|
let pyEnv = undefined;
|
|
|
|
addFakeModel('fields.sms.emojis.partner', {
|
|
message: {string: "message", type: "text"},
|
|
});
|
|
|
|
/**
|
|
* Open the correct view based on inputs
|
|
*
|
|
* @param {string} model full name of the model to display
|
|
* @param {string} viewArch layout of the view in xml
|
|
* @param {number} recordId id of the record to open, new record created if falsy
|
|
* @param {boolean} debug
|
|
*/
|
|
async function openTestView(model, viewArch, recordId = null) {
|
|
const views = {};
|
|
const resModel = model;
|
|
views[`${resModel},false,form`] = viewArch;
|
|
const startParams = {serverData: {views}};
|
|
const { openView } = await start(startParams);
|
|
const openViewParams = {
|
|
res_model: resModel,
|
|
views: [[false, 'form']],
|
|
};
|
|
if (recordId) {
|
|
openViewParams.res_id = recordId;
|
|
}
|
|
await openView(openViewParams);
|
|
}
|
|
|
|
hooks.beforeEach(async () => {
|
|
pyEnv = await startServer();
|
|
target = getFixture();
|
|
});
|
|
|
|
QUnit.test('Sms widgets are correctly rendered', async function (assert) {
|
|
assert.expect(9);
|
|
await openTestView('fields.sms.emojis.partner', `<form><sheet><field name="message" widget="sms_widget"/></sheet></form>`);
|
|
|
|
assert.containsOnce(target, '.o_sms_count', "Should have a sms counter");
|
|
assert.strictEqual(target.querySelector('.o_sms_count').textContent, "0 characters, fits in 0 SMS (GSM7) ");
|
|
// GSM-7
|
|
await testUtils.fields.editAndTrigger(target.querySelector('.o_input'), "Hello from Odoo", 'input');
|
|
assert.strictEqual(target.querySelector('.o_sms_count').textContent, "15 characters, fits in 1 SMS (GSM7) ");
|
|
// GSM-7 with \n => this one count as 2 characters
|
|
await testUtils.fields.editAndTrigger(target.querySelector('.o_input'), "Hello from Odoo\n", 'input');
|
|
assert.strictEqual(target.querySelector('.o_sms_count').textContent, "17 characters, fits in 1 SMS (GSM7) ");
|
|
// Unicode => ê
|
|
await testUtils.fields.editAndTrigger(target.querySelector('.o_input'), "Hêllo from Odoo", 'input');
|
|
assert.strictEqual(target.querySelector('.o_sms_count').textContent, "15 characters, fits in 1 SMS (UNICODE) ");
|
|
// GSM-7 with 160c
|
|
var text = Array(161).join('a');
|
|
await testUtils.fields.editAndTrigger(target.querySelector('.o_input'), text, 'input');
|
|
assert.strictEqual(target.querySelector('.o_sms_count').textContent, "160 characters, fits in 1 SMS (GSM7) ");
|
|
// GSM-7 with 161c
|
|
text = Array(162).join('a');
|
|
await testUtils.fields.editAndTrigger(target.querySelector('.o_input'), text, 'input');
|
|
assert.strictEqual(target.querySelector('.o_sms_count').textContent, "161 characters, fits in 2 SMS (GSM7) ");
|
|
// Unicode with 70c
|
|
text = Array(71).join('ê');
|
|
await testUtils.fields.editAndTrigger(target.querySelector('.o_input'), text, 'input');
|
|
assert.strictEqual(target.querySelector('.o_sms_count').textContent, "70 characters, fits in 1 SMS (UNICODE) ");
|
|
// Unicode with 71c
|
|
text = Array(72).join('ê');
|
|
await testUtils.fields.editAndTrigger(target.querySelector('.o_input'), text, 'input');
|
|
assert.strictEqual(target.querySelector('.o_sms_count').textContent, "71 characters, fits in 2 SMS (UNICODE) ");
|
|
});
|
|
|
|
QUnit.test('SMS widgets update with emoji picker', async function (assert) {
|
|
assert.expect(3);
|
|
await openTestView('fields.sms.emojis.partner', `<form><sheet><field name="message" widget="sms_widget"/></sheet></form>`);
|
|
|
|
assert.containsOnce(target, '.o_sms_count', "Should have a sms counter");
|
|
|
|
// insert some text
|
|
const baseString = "Hello from Odoo";
|
|
await testUtils.fields.editAndTrigger(target.querySelector('.o_input'), baseString, 'input');
|
|
|
|
// insert an emoji
|
|
await click(target, ".o_field_sms_widget button");
|
|
let emojiItem = target.querySelector(".o_EmojiGridRowView_item");
|
|
let emojiItemCharacter = emojiItem.textContent;
|
|
await click(emojiItem);
|
|
|
|
// check everything is there
|
|
let stringLength = baseString.length + emojiItemCharacter.length;
|
|
assert.strictEqual(target.querySelector('.o_sms_count').textContent, `${stringLength} characters, fits in 1 SMS (UNICODE) `,
|
|
"Should have included the length of the new emoji");
|
|
|
|
// check insertion after selection (replacing selection)
|
|
target.querySelector('.o_input').setSelectionRange(0, stringLength);
|
|
await click(target, ".o_field_sms_widget button");
|
|
emojiItem = target.querySelector(".o_EmojiGridRowView_item");
|
|
emojiItemCharacter = emojiItem.textContent;
|
|
await click(emojiItem);
|
|
stringLength = emojiItemCharacter.length;
|
|
assert.strictEqual(target.querySelector('.o_sms_count').textContent, `${stringLength} characters, fits in 1 SMS (UNICODE) `,
|
|
"Should have included the length of the new emoji and removed the length of the replaced characters");
|
|
|
|
});
|
|
|
|
QUnit.test('Sms widgets with non-empty initial value', async function (assert) {
|
|
assert.expect(1);
|
|
const recordId = pyEnv['fields.sms.emojis.partner'].create({ message: '123test' });
|
|
await openTestView('fields.sms.emojis.partner', `<form><sheet><field name="message" widget="sms_widget" readonly="true"/></sheet></form>`, recordId);
|
|
|
|
assert.strictEqual(target.querySelector('.o_field_text span').textContent, '123test', 'Should have the initial value');
|
|
|
|
});
|
|
|
|
QUnit.test('Sms widgets with empty initial value', async function (assert) {
|
|
const recordId = pyEnv['fields.sms.emojis.partner'].create({ message: '' });
|
|
assert.expect(1);
|
|
await openTestView('fields.sms.emojis.partner', `<form><sheet><field name="message" widget="sms_widget" readonly="true"/></sheet></form>`, recordId);
|
|
|
|
assert.strictEqual(target.querySelector('.o_field_text span').textContent, '',
|
|
'Should have the empty initial value');
|
|
|
|
});
|
|
});
|
|
|
|
QUnit.module('PhoneWidget', (hooks) => {
|
|
|
|
let data = undefined;
|
|
hooks.beforeEach(() => {
|
|
data = {
|
|
partner: {
|
|
fields: {
|
|
message: {string: "message", type: "text"},
|
|
foo: {string: "Foo", type: "char", default: "My little Foo Value"},
|
|
mobile: {string: "mobile", type: "text"},
|
|
},
|
|
records: [{
|
|
id: 1,
|
|
message: "",
|
|
foo: 'yop',
|
|
mobile: "+32494444444",
|
|
}, {
|
|
id: 2,
|
|
message: "",
|
|
foo: 'bayou',
|
|
}],
|
|
},
|
|
};
|
|
setupViewRegistries();
|
|
});
|
|
|
|
QUnit.test('phone field in editable list view on normal screens', async function (assert) {
|
|
assert.expect(11);
|
|
var doActionCount = 0;
|
|
|
|
var list = await createView({
|
|
View: ListView,
|
|
model: 'partner',
|
|
data: data,
|
|
debug: true,
|
|
arch: '<tree editable="bottom"><field name="foo" widget="phone"/></tree>',
|
|
intercepts: {
|
|
do_action(ev) {
|
|
assert.equal(ev.data.action.res_model, 'sms.composer',
|
|
'The action to send an SMS should have been executed');
|
|
doActionCount += 1;
|
|
}
|
|
}
|
|
});
|
|
|
|
assert.containsN(list, 'tbody td:not(.o_list_record_selector)', 4);
|
|
assert.strictEqual(list.$('tbody td:not(.o_list_record_selector)').first().text(), 'yopSMS',
|
|
"value should be displayed properly with a link to send SMS");
|
|
|
|
assert.containsN(list, 'div.o_field_widget.o_form_uri.o_field_phone > a', 2,
|
|
"should have the correct classnames");
|
|
|
|
// Edit a line and check the result
|
|
var $cell = list.$('tbody td:not(.o_list_record_selector)').first();
|
|
await testUtils.dom.click($cell);
|
|
assert.hasClass($cell.parent(),'o_selected_row', 'should be set as edit mode');
|
|
assert.strictEqual($cell.find('input').val(), 'yop',
|
|
'should have the corect value in internal input');
|
|
await testUtils.fields.editInput($cell.find('input'), 'new');
|
|
|
|
// save
|
|
await testUtils.dom.click(list.$buttons.find('.o_list_button_save'));
|
|
$cell = list.$('tbody td:not(.o_list_record_selector)').first();
|
|
assert.doesNotHaveClass($cell.parent(), 'o_selected_row', 'should not be in edit mode anymore');
|
|
assert.strictEqual(list.$('tbody td:not(.o_list_record_selector)').first().text(), 'newSMS',
|
|
"value should be properly updated");
|
|
assert.containsN(list, 'div.o_field_widget.o_form_uri.o_field_phone > a', 2,
|
|
"should still have links with correct classes");
|
|
|
|
await testUtils.dom.click(list.$('tbody td:not(.o_list_record_selector) .o_field_phone_sms').first());
|
|
assert.equal(doActionCount, 1, 'Only one action should have been executed');
|
|
assert.containsNone(list, '.o_selected_row',
|
|
'None of the list element should have been activated');
|
|
|
|
list.destroy();
|
|
});
|
|
|
|
QUnit.test('readonly sms phone field is properly rerendered after been changed by onchange', async function (assert) {
|
|
assert.expect(4);
|
|
|
|
const NEW_PHONE = '+32595555555';
|
|
|
|
const form = await createView({
|
|
View: FormView,
|
|
model: 'partner',
|
|
data: data,
|
|
arch: '<form string="Partners">' +
|
|
'<sheet>' +
|
|
'<group>' +
|
|
'<field name="foo" on_change="1"/>' + // onchange to update mobile in readonly mode directly
|
|
'<field name="mobile" widget="phone" readonly="1"/>' + // readonly only, we don't want to go through write mode
|
|
'</group>' +
|
|
'</sheet>' +
|
|
'</form>',
|
|
res_id: 1,
|
|
viewOptions: {mode: 'edit'},
|
|
mockRPC: function (route, args) {
|
|
if (args.method === 'onchange') {
|
|
return Promise.resolve({
|
|
value: {
|
|
mobile: NEW_PHONE, // onchange to update mobile in readonly mode directly
|
|
},
|
|
});
|
|
}
|
|
return this._super.apply(this, arguments);
|
|
},
|
|
});
|
|
// check initial rendering
|
|
assert.strictEqual(form.$('.o_field_phone').text(), "+32494444444",
|
|
'Initial Phone text should be set');
|
|
assert.strictEqual(form.$('.o_field_phone_sms').text(), 'SMS',
|
|
'SMS button label should be rendered');
|
|
|
|
// trigger the onchange to update phone field, but still in readonly mode
|
|
await testUtils.fields.editInput($('input[name="foo"]'), 'someOtherFoo');
|
|
|
|
// check rendering after changes
|
|
assert.strictEqual(form.$('.o_field_phone').text(), NEW_PHONE,
|
|
'Phone text should be updated');
|
|
assert.strictEqual(form.$('.o_field_phone_sms').text(), 'SMS',
|
|
'SMS button label should not be changed');
|
|
|
|
form.destroy();
|
|
});
|
|
});
|