odoo.define('web.sample_server_tests', function (require) { "use strict"; const SampleServer = require('web.SampleServer'); const session = require('web.session'); const { mock } = require('web.test_utils'); const { MAIN_RECORDSET_SIZE, SEARCH_READ_LIMIT, // Limits SAMPLE_COUNTRIES, SAMPLE_PEOPLE, SAMPLE_TEXTS, // Text values MAX_COLOR_INT, MAX_FLOAT, MAX_INTEGER, MAX_MONETARY, // Number values SUB_RECORDSET_SIZE, // Records sise } = SampleServer; /** * Transforms random results into deterministic ones. */ class DeterministicSampleServer extends SampleServer { constructor() { super(...arguments); this.arrayElCpt = 0; this.boolCpt = 0; this.subRecordIdCpt = 0; } _getRandomArrayEl(array) { return array[this.arrayElCpt++ % array.length]; } _getRandomBool() { return Boolean(this.boolCpt++ % 2); } _getRandomSubRecordId() { return (this.subRecordIdCpt++ % SUB_RECORDSET_SIZE) + 1; } } QUnit.module("Sample Server (legacy)", { beforeEach() { this.fields = { 'res.users': { display_name: { string: "Name", type: 'char' }, name: { string: "Reference", type: 'char' }, email: { string: "Email", type: 'char' }, phone_number: { string: "Phone number", type: 'char' }, brol_machin_url_truc: { string: "URL", type: 'char' }, urlemailphone: { string: "Whatever", type: 'char' }, active: { string: "Active", type: 'boolean' }, is_alive: { string: "Is alive", type: 'boolean' }, description: { string: "Description", type: 'text' }, birthday: { string: "Birthday", type: 'date' }, arrival_date: { string: "Date of arrival", type: 'datetime' }, height: { string: "Height", type: 'float' }, color: { string: "Color", type: 'integer' }, age: { string: "Age", type: 'integer' }, salary: { string: "Salary", type: 'monetary' }, currency: { string: "Currency", type: 'many2one', relation: 'res.currency' }, manager_id: { string: "Manager", type: 'many2one', relation: 'res.users' }, cover_image_id: { string: "Cover Image", type: 'many2one', relation: 'ir.attachment' }, managed_ids: { string: "Managing", type: 'one2many', relation: 'res.users' }, tag_ids: { string: "Tags", type: 'many2many', relation: 'tag' }, type: { string: "Type", type: 'selection', selection: [ ['client', "Client"], ['partner', "Partner"], ['employee', "Employee"] ] }, }, 'res.country': { display_name: { string: "Name", type: 'char' }, }, 'hobbit': { display_name: { string: "Name", type: 'char' }, profession: { string: "Profession", type: 'selection', selection: [ ['gardener', "Gardener"], ['brewer', "Brewer"], ['adventurer', "Adventurer"] ] }, age: { string: "Age", type: 'integer' }, }, 'ir.attachment': { display_name: { string: "Name", type: 'char' }, } }; }, }, function () { QUnit.module("Basic behaviour"); QUnit.test("Sample data: people type + all field names", async function (assert) { assert.expect(26); mock.patch(session, { company_currency_id: 4, }); const allFieldNames = Object.keys(this.fields['res.users']); const server = new DeterministicSampleServer('res.users', this.fields['res.users']); const { records } = await server.mockRpc({ method: '/web/dataset/search_read', model: 'res.users', fields: allFieldNames, }); const rec = records[0]; function assertFormat(fieldName, regex) { if (regex instanceof RegExp) { assert.ok( regex.test(rec[fieldName].toString()), `Field "${fieldName}" has the correct format` ); } else { assert.strictEqual( typeof rec[fieldName], regex, `Field "${fieldName}" is of type ${regex}` ); } } function assertBetween(fieldName, min, max) { const val = rec[fieldName]; assert.ok( min <= val && val < max && parseInt(val, 10) === val, `Field "${fieldName}" should be an integer between ${min} and ${max}: ${val}` ); } // Basic fields assert.ok(SAMPLE_PEOPLE.includes(rec.display_name)); assert.ok(SAMPLE_PEOPLE.includes(rec.name)); assert.strictEqual(rec.email, `${rec.display_name.replace(/ /, ".").toLowerCase()}@sample.demo` ); assertFormat('phone_number', /\+1 555 754 000\d/); assertFormat('brol_machin_url_truc', /http:\/\/sample\d\.com/); assert.strictEqual(rec.urlemailphone, false); assert.strictEqual(rec.active, true); assertFormat('is_alive', 'boolean'); assert.ok(SAMPLE_TEXTS.includes(rec.description)); assertFormat('birthday', /\d{4}-\d{2}-\d{2}/); assertFormat('arrival_date', /\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/); assert.ok(rec.height >= 0 && rec.height <= MAX_FLOAT, "Field height should be between 0 and 100"); assertBetween('color', 0, MAX_COLOR_INT); assertBetween('age', 0, MAX_INTEGER); assertBetween('salary', 0, MAX_MONETARY); // check float field have 2 decimal rounding assert.strictEqual(rec.height, parseFloat(parseFloat(rec.height).toFixed(2))); const selectionValues = this.fields['res.users'].type.selection.map( (sel) => sel[0] ); assert.ok(selectionValues.includes(rec.type)); // Relational fields assert.strictEqual(rec.currency[0], 4); // Currently we expect the currency name to be a latin string, which // is not important; in most case we only need the ID. The following // assertion can be removed if needed. assert.ok(SAMPLE_TEXTS.includes(rec.currency[1])); assert.strictEqual(typeof rec.manager_id[0], 'number'); assert.ok(SAMPLE_PEOPLE.includes(rec.manager_id[1])); assert.strictEqual(rec.cover_image_id, false); assert.strictEqual(rec.managed_ids.length, 2); assert.ok(rec.managed_ids.every( (id) => typeof id === 'number') ); assert.strictEqual(rec.tag_ids.length, 2); assert.ok(rec.tag_ids.every( (id) => typeof id === 'number') ); mock.unpatch(session); }); QUnit.test("Sample data: country type", async function (assert) { assert.expect(1); const server = new DeterministicSampleServer('res.country', this.fields['res.country']); const { records } = await server.mockRpc({ method: '/web/dataset/search_read', model: 'res.country', fields: ['display_name'], }); assert.ok(SAMPLE_COUNTRIES.includes(records[0].display_name)); }); QUnit.test("Sample data: any type", async function (assert) { assert.expect(1); const server = new DeterministicSampleServer('hobbit', this.fields.hobbit); const { records } = await server.mockRpc({ method: '/web/dataset/search_read', model: 'hobbit', fields: ['display_name'], }); assert.ok(SAMPLE_TEXTS.includes(records[0].display_name)); }); QUnit.module("RPC calls"); QUnit.test("Send 'search_read' RPC: valid field names", async function (assert) { assert.expect(3); const server = new DeterministicSampleServer('hobbit', this.fields.hobbit); const result = await server.mockRpc({ method: '/web/dataset/search_read', model: 'hobbit', fields: ['display_name'], }); assert.deepEqual( Object.keys(result.records[0]), ['id', 'display_name'] ); assert.strictEqual(result.length, SEARCH_READ_LIMIT); assert.ok(/\w+/.test(result.records[0].display_name), "Display name has been mocked" ); }); QUnit.test("Send 'search_read' RPC: invalid field names", async function (assert) { assert.expect(3); const server = new DeterministicSampleServer('hobbit', this.fields.hobbit); const result = await server.mockRpc({ method: '/web/dataset/search_read', model: 'hobbit', fields: ['name'], }); assert.deepEqual( Object.keys(result.records[0]), ['id', 'name'] ); assert.strictEqual(result.length, SEARCH_READ_LIMIT); assert.strictEqual(result.records[0].name, false, `Field "name" doesn't exist => returns false` ); }); QUnit.test("Send 'web_read_group' RPC: no group", async function (assert) { assert.expect(1); const server = new DeterministicSampleServer('hobbit', this.fields.hobbit); server.setExistingGroups([]); const result = await server.mockRpc({ method: 'web_read_group', model: 'hobbit', groupBy: ['profession'], }); assert.deepEqual(result, { groups: [], length: 0 }); }); QUnit.test("Send 'web_read_group' RPC: 2 groups", async function (assert) { assert.expect(5); const server = new DeterministicSampleServer('hobbit', this.fields.hobbit); const existingGroups = [ { profession: 'gardener', profession_count: 0 }, { profession: 'adventurer', profession_count: 0 }, ]; server.setExistingGroups(existingGroups); const result = await server.mockRpc({ method: 'web_read_group', model: 'hobbit', groupBy: ['profession'], fields: [], }); assert.strictEqual(result.length, 2); assert.strictEqual(result.groups.length, 2); assert.deepEqual( result.groups.map((g) => g.profession), ["gardener", "adventurer"] ); assert.strictEqual( result.groups.reduce((acc, g) => acc + g.profession_count, 0), MAIN_RECORDSET_SIZE ); assert.ok( result.groups.every((g) => g.profession_count === g.__data.length) ); }); QUnit.test("Send 'web_read_group' RPC: all groups", async function (assert) { assert.expect(5); const server = new DeterministicSampleServer('hobbit', this.fields.hobbit); const existingGroups = [ { profession: 'gardener', profession_count: 0 }, { profession: 'brewer', profession_count: 0 }, { profession: 'adventurer', profession_count: 0 }, ]; server.setExistingGroups(existingGroups); const result = await server.mockRpc({ method: 'web_read_group', model: 'hobbit', groupBy: ['profession'], fields: [], }); assert.strictEqual(result.length, 3); assert.strictEqual(result.groups.length, 3); assert.deepEqual( result.groups.map((g) => g.profession), ["gardener", "brewer", "adventurer"] ); assert.strictEqual( result.groups.reduce((acc, g) => acc + g.profession_count, 0), MAIN_RECORDSET_SIZE ); assert.ok( result.groups.every((g) => g.profession_count === g.__data.length) ); }); QUnit.test("Send 'read_group' RPC: no group", async function (assert) { assert.expect(1); const server = new DeterministicSampleServer('hobbit', this.fields.hobbit); const result = await server.mockRpc({ method: 'read_group', model: 'hobbit', fields: [], groupBy: [], }); assert.deepEqual(result, [{ __count: MAIN_RECORDSET_SIZE, __domain: [], }]); }); QUnit.test("Send 'read_group' RPC: groupBy", async function (assert) { assert.expect(3); const server = new DeterministicSampleServer('hobbit', this.fields.hobbit); const result = await server.mockRpc({ method: 'read_group', model: 'hobbit', fields: [], groupBy: ['profession'], }); assert.strictEqual(result.length, 3); assert.deepEqual( result.map((g) => g.profession), ["adventurer", "brewer", "gardener"] ); assert.strictEqual( result.reduce((acc, g) => acc + g.profession_count, 0), MAIN_RECORDSET_SIZE, ); }); QUnit.test("Send 'read_group' RPC: groupBy and field", async function (assert) { assert.expect(4); const server = new DeterministicSampleServer('hobbit', this.fields.hobbit); const result = await server.mockRpc({ method: 'read_group', model: 'hobbit', fields: ['age'], groupBy: ['profession'], }); assert.strictEqual(result.length, 3); assert.deepEqual( result.map((g) => g.profession), ["adventurer", "brewer", "gardener"] ); assert.strictEqual( result.reduce((acc, g) => acc + g.profession_count, 0), MAIN_RECORDSET_SIZE, ); assert.strictEqual( result.reduce((acc, g) => acc + g.age, 0), server.data.hobbit.records.reduce((acc, g) => acc + g.age, 0) ); }); QUnit.test("Send 'read_group' RPC: multiple groupBys and lazy", async function (assert) { assert.expect(2); const server = new DeterministicSampleServer('hobbit', this.fields.hobbit); const result = await server.mockRpc({ method: 'read_group', model: 'hobbit', fields: [], groupBy: ['profession', 'age'], }); assert.ok('profession' in result[0]); assert.notOk('age' in result[0]); }); QUnit.test("Send 'read_group' RPC: multiple groupBys and not lazy", async function (assert) { assert.expect(2); const server = new DeterministicSampleServer('hobbit', this.fields.hobbit); const result = await server.mockRpc({ method: 'read_group', model: 'hobbit', fields: [], groupBy: ['profession', 'age'], lazy: false, }); assert.ok('profession' in result[0]); assert.ok('age' in result[0]); }); QUnit.test("Send 'read' RPC: no id", async function (assert) { assert.expect(1); const server = new DeterministicSampleServer('hobbit', this.fields.hobbit); const result = await server.mockRpc({ method: 'read', model: 'hobbit', args: [ [], ['display_name'] ], }); assert.deepEqual(result, []); }); QUnit.test("Send 'read' RPC: one id", async function (assert) { assert.expect(3); const server = new DeterministicSampleServer('hobbit', this.fields.hobbit); const result = await server.mockRpc({ method: 'read', model: 'hobbit', args: [ [1], ['display_name'] ], }); assert.strictEqual(result.length, 1); assert.ok( /\w+/.test(result[0].display_name), "Display name has been mocked" ); assert.strictEqual(result[0].id, 1); }); QUnit.test("Send 'read' RPC: more than all available ids", async function (assert) { assert.expect(1); const server = new DeterministicSampleServer('hobbit', this.fields.hobbit); const amount = MAIN_RECORDSET_SIZE + 3; const ids = new Array(amount).fill().map((_, i) => i + 1); const result = await server.mockRpc({ method: 'read', model: 'hobbit', args: [ ids, ['display_name'] ], }); assert.strictEqual(result.length, MAIN_RECORDSET_SIZE); }); // To be implemented if needed // QUnit.test("Send 'read_progress_bar' RPC", async function (assert) { ... }); }); });