summaryrefslogtreecommitdiff
path: root/addons/web/static/tests/views/basic_model_tests.js
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web/static/tests/views/basic_model_tests.js')
-rw-r--r--addons/web/static/tests/views/basic_model_tests.js1220
1 files changed, 1220 insertions, 0 deletions
diff --git a/addons/web/static/tests/views/basic_model_tests.js b/addons/web/static/tests/views/basic_model_tests.js
new file mode 100644
index 00000000000..15947103e6e
--- /dev/null
+++ b/addons/web/static/tests/views/basic_model_tests.js
@@ -0,0 +1,1220 @@
+odoo.define('web.basic_model_tests', function (require) {
+"use strict";
+
+var BasicModel = require('web.BasicModel');
+var testUtils = require('web.test_utils');
+
+var createModel = testUtils.createModel;
+
+QUnit.module('Views', {
+ beforeEach: function () {
+ this.data = {
+ partner: {
+ fields: {
+ display_name: {string: "STRING", type: 'char'},
+ total: {string: "Total", type: 'integer'},
+ foo: {string: "Foo", type: 'char'},
+ bar: {string: "Bar", type: 'integer'},
+ qux: {string: "Qux", type: 'many2one', relation: 'partner'},
+ product_id: {string: "Favorite product", type: 'many2one', relation: 'product'},
+ product_ids: {string: "Favorite products", type: 'one2many', relation: 'product'},
+ category: {string: "Category M2M", type: 'many2many', relation: 'partner_type'},
+ date: {string: "Date Field", type: 'date'},
+ },
+ records: [
+ {id: 1, foo: 'blip', bar: 1, product_id: 37, category: [12], display_name: "first partner", date: "2017-01-25"},
+ {id: 2, foo: 'gnap', bar: 2, product_id: 41, display_name: "second partner"},
+ ],
+ onchanges: {},
+ },
+ product: {
+ fields: {
+ name: {string: "Product Name", type: "char"}
+ },
+ records: [
+ {id: 37, display_name: "xphone"},
+ {id: 41, display_name: "xpad"}
+ ]
+ },
+ partner_type: {
+ fields: {
+ display_name: {string: "Partner Type", type: "char"},
+ date: {string: "Date Field", type: 'date'},
+ },
+ records: [
+ {id: 12, display_name: "gold", date: "2017-01-25"},
+ {id: 14, display_name: "silver"},
+ {id: 15, display_name: "bronze"}
+ ]
+ },
+ };
+
+ // add related fields to category.
+ this.data.partner.fields.category.relatedFields =
+ $.extend(true, {}, this.data.partner_type.fields);
+ this.params = {
+ res_id: 2,
+ modelName: 'partner',
+ fields: this.data.partner.fields,
+ };
+ }
+}, function () {
+ QUnit.module('BasicModel');
+
+ QUnit.test('simple functionality', function (assert) {
+ assert.expect(6);
+
+ this.params.fieldNames = ['foo'];
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ });
+
+ assert.strictEqual(model.get(1), null, "should return null for non existing key");
+
+ model.load(this.params).then(function (resultID) {
+ // it is a string, because it is used as a key in an object
+ assert.strictEqual(typeof resultID, 'string', "result should be a valid id");
+
+ var record = model.get(resultID);
+ assert.strictEqual(record.res_id, 2, "res_id read should be the same as asked");
+ assert.strictEqual(record.type, 'record', "should be of type 'record'");
+ assert.strictEqual(record.data.foo, "gnap", "should correctly read value");
+ assert.strictEqual(record.data.bar, undefined, "should not fetch the field 'bar'");
+ });
+ model.destroy();
+ });
+
+ QUnit.test('notify change with many2one', function (assert) {
+ assert.expect(2);
+
+ this.params.fieldNames = ['foo', 'qux'];
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ });
+
+ model.load(this.params).then(function (resultID) {
+ var record = model.get(resultID);
+ assert.strictEqual(record.data.qux, false, "qux field should be false");
+ model.notifyChanges(resultID, {qux: {id: 1, display_name: "hello"}});
+
+ record = model.get(resultID);
+ assert.strictEqual(record.data.qux.data.id, 1, "qux field should be 1");
+ });
+ model.destroy();
+ });
+
+ QUnit.test('write on a many2one', function (assert) {
+ assert.expect(4);
+ var self = this;
+
+ this.params.fieldNames = ['product_id'];
+
+ var rpcCount = 0;
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ mockRPC: function (route, args) {
+ rpcCount++;
+ return this._super(route, args);
+ },
+ });
+
+ model.load(this.params).then(function (resultID) {
+ var record = model.get(resultID);
+ assert.strictEqual(record.data.product_id.data.display_name, 'xpad',
+ "should be initialized with correct value");
+
+ model.notifyChanges(resultID, {product_id: {id: 37, display_name: 'xphone'}});
+
+ record = model.get(resultID);
+ assert.strictEqual(record.data.product_id.data.display_name, 'xphone',
+ "should be changed with correct value");
+
+ model.save(resultID);
+
+ assert.strictEqual(self.data.partner.records[1].product_id, 37,
+ "should have really saved the data");
+ assert.strictEqual(rpcCount, 3, "should have done 3 rpc: 1 read, 1 write, 1 read");
+ });
+ model.destroy();
+ });
+
+ QUnit.test('basic onchange', function (assert) {
+ assert.expect(5);
+
+ this.data.partner.onchanges.foo = function (obj) {
+ obj.bar = obj.foo.length;
+ };
+
+ this.params.fieldNames = ['foo', 'bar'];
+ this.params.context = {hello: 'world'};
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ mockRPC: function (route, args) {
+ if (args.method === 'onchange') {
+ var context = args.args[4];
+ assert.deepEqual(context, {hello: 'world'},
+ "context should be sent by the onchange");
+ }
+ return this._super(route, args);
+ },
+ });
+
+ model.load(this.params).then(function (resultID) {
+ var record = model.get(resultID);
+ assert.strictEqual(record.data.foo, 'gnap', "foo field is properly initialized");
+ assert.strictEqual(record.data.bar, 2, "bar field is properly initialized");
+
+ model.notifyChanges(resultID, {foo: 'mary poppins'});
+
+ record = model.get(resultID);
+ assert.strictEqual(record.data.foo, 'mary poppins', "onchange has been applied");
+ assert.strictEqual(record.data.bar, 12, "onchange has been applied");
+ });
+ model.destroy();
+ });
+
+ QUnit.test('onchange with a many2one', function (assert) {
+ assert.expect(5);
+
+ this.data.partner.onchanges.product_id = function (obj) {
+ if (obj.product_id === 37) {
+ obj.foo = "space lollipop";
+ }
+ };
+
+ this.params.fieldNames = ['foo', 'product_id'];
+
+ var rpcCount = 0;
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ mockRPC: function (route, args) {
+ if (args.method === 'onchange') {
+ assert.strictEqual(args.args[2], "product_id",
+ "should send the only changed field as a string, not a list");
+ }
+ rpcCount++;
+ return this._super(route, args);
+ },
+ });
+
+ model.load(this.params).then(function (resultID) {
+ var record = model.get(resultID);
+ assert.strictEqual(record.data.foo, 'gnap', "foo field is properly initialized");
+ assert.strictEqual(record.data.product_id.data.id, 41, "product_id field is properly initialized");
+
+ model.notifyChanges(resultID, {product_id: {id: 37, display_name: 'xphone'}});
+
+ record = model.get(resultID);
+ assert.strictEqual(record.data.foo, 'space lollipop', "onchange has been applied");
+ assert.strictEqual(rpcCount, 2, "should have done 2 rpc: 1 read and 1 onchange");
+ });
+ model.destroy();
+ });
+
+ QUnit.test('notifyChange on a many2one, without display_name', function (assert) {
+ assert.expect(3);
+
+ this.params.fieldNames = ['product_id'];
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ mockRPC: function (route, args) {
+ if (args.method === 'name_get') {
+ assert.strictEqual(args.model, 'product');
+ }
+ return this._super(route, args);
+ },
+ });
+
+ model.load(this.params).then(function (resultID) {
+ var record = model.get(resultID);
+ assert.strictEqual(record.data.product_id.data.display_name, 'xpad',
+ "product_id field is set to xpad");
+
+ model.notifyChanges(resultID, {product_id: {id: 37}});
+
+ record = model.get(resultID);
+ assert.strictEqual(record.data.product_id.data.display_name, 'xphone',
+ "display_name should have been fetched");
+ });
+ model.destroy();
+ });
+
+ QUnit.test('onchange on a char with an unchanged many2one', function (assert) {
+ assert.expect(2);
+
+ this.data.partner.onchanges.foo = function (obj) {
+ obj.foo = obj.foo + " alligator";
+ };
+
+ this.params.fieldNames = ['foo', 'product_id'];
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ mockRPC: function (route, args) {
+ if (args.method === 'onchange') {
+ assert.strictEqual(args.args[1].product_id, 41, "should send correct value");
+ }
+ return this._super(route, args);
+ },
+ });
+
+ model.load(this.params).then(function (resultID) {
+ model.notifyChanges(resultID, {foo: 'cookie'});
+ var record = model.get(resultID);
+ assert.strictEqual(record.data.foo, 'cookie alligator', "onchange has been applied");
+ });
+ model.destroy();
+ });
+
+ QUnit.test('onchange on a char with another many2one not set to a value', function (assert) {
+ assert.expect(2);
+ this.data.partner.records[0].product_id = false;
+ this.data.partner.onchanges.foo = function (obj) {
+ obj.foo = obj.foo + " alligator";
+ };
+
+ this.params.fieldNames = ['foo', 'product_id'];
+ this.params.res_id = 1;
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ });
+
+ model.load(this.params).then(function (resultID) {
+ var record = model.get(resultID);
+ assert.strictEqual(record.data.product_id, false, "product_id is not set");
+
+ model.notifyChanges(resultID, {foo: 'cookie'});
+ record = model.get(resultID);
+ assert.strictEqual(record.data.foo, 'cookie alligator', "onchange has been applied");
+ });
+ model.destroy();
+ });
+
+ QUnit.test('can get a many2many', function (assert) {
+ assert.expect(3);
+
+ this.params.res_id = 1;
+ this.params.fieldsInfo = {
+ default: {
+ category: {
+ fieldsInfo: {default: {display_name: {}}},
+ relatedFields: {display_name: {type: "char"}},
+ viewType: 'default',
+ },
+ },
+ };
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ });
+
+ model.load(this.params).then(function (resultID) {
+ var record = model.get(resultID);
+ assert.strictEqual(record.data.category.data[0].res_id, 12,
+ "should have loaded many2many res_ids");
+ assert.strictEqual(record.data.category.data[0].data.display_name, "gold",
+ "should have loaded many2many display_name");
+ record = model.get(resultID, {raw: true});
+ assert.deepEqual(record.data.category, [12],
+ "with option raw, category should only return ids");
+ });
+ model.destroy();
+ });
+
+ QUnit.test('can use command add and get many2many value with date field', function (assert) {
+ assert.expect(2);
+
+ this.params.fieldsInfo = {
+ default: {
+ category: {
+ fieldsInfo: {default: {date: {}}},
+ relatedFields: {date: {type: "date"}},
+ viewType: 'default',
+ },
+ },
+ };
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ });
+
+ model.load(this.params).then(function (resultID) {
+ var changes = {
+ category: {operation: 'ADD_M2M', ids: [{id: 12}]}
+ };
+ model.notifyChanges(resultID, changes).then(function () {
+ var record = model.get(resultID);
+ assert.strictEqual(record.data.category.data.length, 1, "should have added one category");
+ assert.strictEqual(record.data.category.data[0].data.date instanceof moment,
+ true, "should have a date parsed in a moment object");
+ });
+ });
+ model.destroy();
+ });
+
+ QUnit.test('can fetch a list', function (assert) {
+ assert.expect(4);
+
+ this.params.fieldNames = ['foo'];
+ this.params.domain = [];
+ this.params.groupedBy = [];
+ this.params.res_id = undefined;
+
+ var rpcCount = 0;
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ mockRPC: function (route, args) {
+ rpcCount++;
+ return this._super(route, args);
+ },
+ });
+
+ model.load(this.params).then(function (resultID) {
+ var record = model.get(resultID);
+
+ assert.strictEqual(record.type, 'list', "record fetched should be a list");
+ assert.strictEqual(record.data.length, 2, "should have fetched 2 records");
+ assert.strictEqual(record.data[0].data.foo, 'blip', "first record should have 'blip' in foo field");
+ assert.strictEqual(rpcCount, 1, "should have done 1 rpc (searchread)");
+ });
+ model.destroy();
+ });
+
+ QUnit.test('fetch x2manys in list, with not too many rpcs', function (assert) {
+ assert.expect(2);
+
+ this.data.partner.records[0].category = [12, 15];
+ this.data.partner.records[1].category = [12, 14];
+
+ this.params.fieldNames = ['category'];
+ this.params.domain = [];
+ this.params.groupedBy = [];
+ this.params.res_id = undefined;
+
+ var rpcCount = 0;
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ mockRPC: function (route, args) {
+ rpcCount++;
+ return this._super(route, args);
+ },
+ });
+
+ model.load(this.params).then(function (resultID) {
+ var record = model.get(resultID);
+
+ assert.strictEqual(record.data[0].data.category.data.length, 2,
+ "first record should have 2 categories loaded");
+ assert.strictEqual(rpcCount, 2, "should have done 2 rpc (searchread and read category)");
+ });
+ model.destroy();
+ });
+
+ QUnit.test('can make a default_record, no onchange', function (assert) {
+ assert.expect(3);
+
+ this.params.context = {};
+ this.params.fieldNames = ['product_id', 'category', 'product_ids'];
+ this.params.res_id = undefined;
+ this.params.type = 'record';
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ });
+
+ model.load(this.params).then(function (resultID) {
+ var record = model.get(resultID);
+ assert.strictEqual(record.data.product_id, false, "m2o default value should be false");
+ assert.deepEqual(record.data.product_ids.data, [], "o2m default should be []");
+ assert.deepEqual(record.data.category.data, [], "m2m default should be []");
+ });
+ model.destroy();
+ });
+
+ QUnit.test('default_record, with onchange on many2one', function (assert) {
+ assert.expect(1);
+
+ // the onchange is done by the mockRPC because we want to return a value
+ // of 'false', which does not work with the mockserver mockOnChange method.
+ this.data.partner.onchanges.product_id = true;
+
+ this.params.context = {};
+ this.params.fieldNames = ['product_id'];
+ this.params.res_id = undefined;
+ this.params.type = 'record';
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ mockRPC: function (route, args) {
+ if (args.method === 'onchange') {
+ return $.when({value: { product_id: false }});
+ }
+ return this._super(route, args);
+ },
+ });
+
+ model.load(this.params).then(function (resultID) {
+ var record = model.get(resultID);
+ assert.strictEqual(record.data.product_id, false, "m2o default value should be false");
+ });
+ model.destroy();
+ });
+
+ QUnit.test('default record: batch namegets on same model and res_id', function (assert) {
+ assert.expect(3);
+
+ var rpcCount = 0;
+ var fields = this.data.partner.fields;
+ fields.other_product_id = _.extend({}, fields.product_id);
+ fields.product_id.default = 37;
+ fields.other_product_id.default = 41;
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ mockRPC: function (route, args) {
+ rpcCount++;
+ return this._super(route, args);
+ },
+ });
+
+ var params = {
+ context: {},
+ fieldNames: ['other_product_id', 'product_id'],
+ fields: fields,
+ modelName: 'partner',
+ type: 'record',
+ };
+
+ model.load(params).then(function (resultID) {
+ var record = model.get(resultID);
+ assert.strictEqual(record.data.product_id.data.display_name, "xphone",
+ "should have fetched correct name");
+ assert.strictEqual(record.data.other_product_id.data.display_name, "xpad",
+ "should have fetched correct name");
+ assert.strictEqual(rpcCount, 2, "should have done 2 rpcs: default_get and 1 name_get");
+ });
+ model.destroy();
+ });
+
+ QUnit.test('undoing a change makes the record not dirty', function (assert) {
+ assert.expect(4);
+
+ this.params.fieldNames = ['foo'];
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ });
+
+ model.load(this.params).then(function (resultID) {
+ var record = model.get(resultID);
+ assert.strictEqual(record.data.foo, "gnap", "foo field should properly be set");
+ assert.ok(!model.isDirty(resultID), "record should not be dirty");
+ model.notifyChanges(resultID, {foo: "hello"});
+ assert.ok(model.isDirty(resultID), "record should be dirty");
+ model.notifyChanges(resultID, {foo: "gnap"});
+ assert.ok(!model.isDirty(resultID), "record should not be dirty");
+ });
+ model.destroy();
+ });
+
+ QUnit.test('isDirty works correctly on list made empty', function (assert) {
+ assert.expect(3);
+
+ this.params.fieldNames = ['category'];
+ this.params.res_id = 1;
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ });
+
+ model.load(this.params).then(function (resultID) {
+ var record = model.get(resultID);
+ var category_value = record.data.category;
+ assert.ok(_.isObject(category_value), "category field should have been fetched");
+ assert.strictEqual(category_value.data.length, 1, "category field should contain one record");
+ model.notifyChanges(resultID, {category: {
+ operation: 'REMOVE',
+ id: category_value.data[0].id
+ }});
+ assert.ok(model.isDirty(resultID), "record should be considered dirty");
+ });
+ model.destroy();
+ });
+
+ QUnit.test('can duplicate a record', function (assert) {
+ assert.expect(4);
+
+ this.params.fieldNames = ['foo'];
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ });
+
+ model.load(this.params).then(function (resultID) {
+ var record = model.get(resultID);
+ assert.strictEqual(record.data.display_name, "second partner",
+ "record should have correct display name");
+ assert.strictEqual(record.data.foo, "gnap", "foo should be set to correct value");
+ model.duplicateRecord(resultID).then(function (duplicateID) {
+ var duplicate = model.get(duplicateID);
+ assert.strictEqual(duplicate.data.display_name, "second partner (copy)",
+ "record should have been duplicated");
+ assert.strictEqual(duplicate.data.foo, "gnap", "foo should be set to correct value");
+ });
+ });
+ model.destroy();
+ });
+
+ QUnit.test('record with many2one set to some value, then set it to none', function (assert) {
+ assert.expect(3);
+
+ this.params.fieldNames = ['product_id'];
+
+ var self = this;
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ });
+
+ model.load(this.params).then(function (resultID) {
+ var record = model.get(resultID);
+ assert.strictEqual(record.data.product_id.data.display_name, 'xpad', "product_id should be set");
+ model.notifyChanges(resultID, {product_id: false});
+
+ record = model.get(resultID);
+ assert.strictEqual(record.data.product_id, false, "product_id should not be set");
+
+ model.save(resultID);
+
+ assert.strictEqual(self.data.partner.records[1].product_id, false,
+ "should have saved the new product_id value");
+ });
+ model.destroy();
+ });
+
+ QUnit.test('internal state of groups remains when reloading', function (assert) {
+ assert.expect(9);
+
+ this.params.fieldNames = ['foo'];
+ this.params.domain = [];
+ this.params.limit = 80;
+ this.params.groupedBy = ['product_id'];
+ this.params.res_id = undefined;
+
+ var filterEnabled = false;
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ mockRPC: function (route, args) {
+ if (args.method === 'read_group' && filterEnabled) {
+ // as this is not yet supported by the MockServer, simulates
+ // a read_group that returns empty groups
+ // this is the case for several models (e.g. project.task
+ // grouped by stage_id)
+ return this._super.apply(this, arguments).then(function (result) {
+ // artificially filter out records of first group
+ result[0].product_id_count = 0;
+ return result;
+ });
+ }
+ return this._super.apply(this, arguments);
+ },
+ });
+
+ model.load(this.params).then(function (resultID) {
+ var record = model.get(resultID);
+ assert.strictEqual(record.data.length, 2, "should have 2 groups");
+ var groupID = record.data[0].id;
+ model.toggleGroup(groupID);
+
+ record = model.get(resultID);
+ assert.ok(record.data[0].isOpen, "first group should be open");
+ assert.strictEqual(record.data[0].data.length, 1,
+ "first group should have one record");
+ assert.strictEqual(record.data[0].limit, 80,
+ "limit should be 80 by default");
+
+ // change the limit and offset of the first group
+ model.localData[record.data[0].id].limit = 10;
+
+ model.reload(resultID);
+ record = model.get(resultID);
+ assert.ok(record.data[0].isOpen, "first group should still be open");
+ assert.strictEqual(record.data[0].data.length, 1,
+ "first group should still have one record");
+ assert.strictEqual(record.data[0].limit, 10,
+ "new limit should have been kept");
+
+ // filter some records out: the open group should stay open but now
+ // be empty
+ filterEnabled = true;
+ model.reload(resultID);
+ record = model.get(resultID);
+ assert.strictEqual(record.data[0].count, 0,
+ "first group's count should be 0");
+ assert.strictEqual(record.data[0].data.length, 0,
+ "first group's data should be empty'");
+ });
+ model.destroy();
+ });
+
+ QUnit.test('create record, then save', function (assert) {
+ assert.expect(4);
+
+ this.params.fieldNames = ['product_ids'];
+ this.params.res_id = undefined;
+ this.params.type = 'record';
+
+ var id;
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ mockRPC: function (route, args) {
+ if (args.method === 'create') {
+ // has to be done before the call to _super
+ assert.notOk('product_ids' in args.args[0], "should not have any value");
+ assert.notOk('category' in args.args[0], "should not have other fields");
+ }
+ var result = this._super(route, args);
+ if (args.method === 'create') {
+ result.then(function (res) {
+ id = res;
+ });
+ }
+ return result;
+ },
+ });
+
+ model.load(this.params).then(function (resultID) {
+ var record = model.get(resultID);
+ model.save(record.id, {reload: false});
+ record = model.get(resultID);
+ assert.strictEqual(record.res_id, id, "should have correct id from server");
+ assert.strictEqual(record.data.id, id, "should have correct id from server");
+ });
+ model.destroy();
+ });
+
+ QUnit.test('write commands on a one2many', function (assert) {
+ assert.expect(4);
+
+ this.data.partner.records[1].product_ids = [37];
+
+ this.params.fieldNames = ['product_ids'];
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ mockRPC: function (route, args) {
+ if (args.method === 'write') {
+ assert.deepEqual(args.args[0], [2], "should write on res_id = 2");
+ var commands = args.args[1].product_ids;
+ assert.deepEqual(commands[0], [4, 37, false], "first command should be a 4");
+ // TO DO: uncomment next line
+ // assert.strictEqual(commands[1], [0, false, {name: "toy"}], "second command should be a 0");
+ assert.strictEqual(commands[1][0], 0, "second command should be a 0");
+ }
+ return this._super(route, args);
+ },
+ });
+
+ model.load(this.params).then(function (resultID) {
+ var record = model.get(resultID, {raw: true});
+ assert.deepEqual(record.data.product_ids, [37], "should have correct initial value");
+
+ model.makeRecord('product', [{
+ name: 'name',
+ string: "Product Name",
+ type: "char",
+ value: "xpod"
+ }
+ ]).then(function (relatedRecordID) {
+ model.notifyChanges(record.id, {
+ product_ids: {operation: "ADD", id: relatedRecordID}
+ });
+ model.save(record.id);
+ });
+ });
+ model.destroy();
+ });
+
+ QUnit.test('onchange with a one2many on a new record', function (assert) {
+ assert.expect(4);
+
+ this.data.partner.fields.total.default = 50;
+ this.data.partner.onchanges.product_ids = function (obj) {
+ obj.total += 100;
+ };
+
+ this.params.fieldNames = ['total', 'product_ids'];
+ this.params.res_id = undefined;
+ this.params.type = 'record';
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ mockRPC: function (route, args) {
+ if (args.method === 'onchange' && args.args[1].total === 150) {
+ assert.deepEqual(args.args[1].product_ids, [[0, false, {name: "xpod"}]],
+ "Should have sent the create command in the onchange");
+ }
+ return this._super(route, args);
+ },
+ });
+
+ model.load(this.params).then(function (resultID) {
+
+ var record = model.get(resultID);
+ assert.strictEqual(record.data.product_ids.data.length, 0,
+ "one2many should start with a list of length 0");
+
+ model.makeRecord('product', [{
+ name: 'name',
+ string: "Product Name",
+ type: "char",
+ value: "xpod"
+ }
+ ]).then(function (relatedRecordID) {
+ model.notifyChanges(resultID, {
+ product_ids: {operation: "ADD", id: relatedRecordID}
+ });
+
+ record = model.get(resultID);
+ assert.strictEqual(record.data.product_ids.data.length, 1,
+ "one2many should be a list of length 1");
+ assert.strictEqual(record.data.product_ids.data[0].data.name, "xpod",
+ "one2many should have correct data");
+ });
+ });
+ model.destroy();
+ });
+
+ QUnit.test('dates are properly loaded and parsed (record)', function (assert) {
+ assert.expect(2);
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ });
+
+ var params = {
+ fieldNames: ['date'],
+ fields: this.data.partner.fields,
+ modelName: 'partner',
+ res_id: 1,
+ };
+
+ model.load(params).then(function (resultID) {
+ var record = model.get(resultID);
+ assert.ok(record.data.date instanceof moment,
+ "fetched date field should have been formatted");
+ });
+
+ params.res_id = 2;
+
+ model.load(params).then(function (resultID) {
+ var record = model.get(resultID);
+ assert.strictEqual(record.data.date, false,
+ "unset date field should be false");
+ });
+ model.destroy();
+ });
+
+ QUnit.test('dates are properly loaded and parsed (list)', function (assert) {
+ assert.expect(2);
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ });
+
+ var params = {
+ fieldNames: ['date'],
+ fields: this.data.partner.fields,
+ modelName: 'partner',
+ type: 'list',
+ };
+
+ model.load(params).then(function (resultID) {
+ var record = model.get(resultID);
+ var firstRecord = record.data[0];
+ var secondRecord = record.data[1];
+ assert.ok(firstRecord.data.date instanceof moment,
+ "fetched date field should have been formatted");
+ assert.strictEqual(secondRecord.data.date, false,
+ "if date is not set, it should be false");
+ });
+ model.destroy();
+ });
+
+ QUnit.test('dates are properly loaded and parsed (default_get)', function (assert) {
+ assert.expect(1);
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ });
+
+ var params = {
+ fieldNames: ['date'],
+ fields: this.data.partner.fields,
+ modelName: 'partner',
+ type: 'record',
+ };
+
+ model.load(params).then(function (resultID) {
+ var record = model.get(resultID);
+ assert.strictEqual(record.data.date, false, "date default value should be false");
+ });
+ model.destroy();
+ });
+
+ QUnit.test('default_get on x2many may return a list of ids', function (assert) {
+ assert.expect(1);
+
+ this.data.partner.fields.category.default = [12, 14];
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ });
+
+ var params = {
+ fieldNames: ['category'],
+ fields: this.data.partner.fields,
+ modelName: 'partner',
+ type: 'record',
+ };
+
+ model.load(params).then(function (resultID) {
+ var record = model.get(resultID);
+ assert.ok(_.isEqual(record.data.category.res_ids, [12, 14]),
+ "category field should have correct default value");
+ });
+
+ model.destroy();
+ });
+
+ QUnit.test('contexts and domains can be properly fetched', function (assert) {
+ assert.expect(8);
+
+ this.data.partner.fields.product_id.context = "{'hello': 'world', 'test': foo}";
+ this.data.partner.fields.product_id.domain = "[['hello', 'like', 'world'], ['test', 'like', foo]]";
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ });
+
+ this.params.fieldNames = ['product_id', 'foo'];
+
+ model.load(this.params).then(function (resultID) {
+ var recordPartner = model.get(resultID);
+ assert.strictEqual(typeof recordPartner.getContext, "function",
+ "partner record should have a getContext function");
+ assert.strictEqual(typeof recordPartner.getDomain, "function",
+ "partner record should have a getDomain function");
+ assert.deepEqual(recordPartner.getContext(), {},
+ "asking for a context without a field name should fetch the session/user/view context");
+ assert.deepEqual(recordPartner.getDomain(), [],
+ "asking for a domain without a field name should fetch the session/user/view domain");
+ assert.deepEqual(
+ recordPartner.getContext({fieldName: "product_id"}),
+ {hello: "world", test: "gnap"},
+ "asking for a context with a field name should fetch the field context (evaluated)");
+ assert.deepEqual(
+ recordPartner.getDomain({fieldName: "product_id"}),
+ [["hello", "like", "world"], ["test", "like", "gnap"]],
+ "asking for a domain with a field name should fetch the field domain (evaluated)");
+ });
+ model.destroy();
+
+ // Try again with xml override of field domain and context
+ model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ });
+
+ this.params.fieldsInfo = {
+ default: {
+ foo: {},
+ product_id: {
+ context: "{'hello2': 'world', 'test2': foo}",
+ domain: "[['hello2', 'like', 'world'], ['test2', 'like', foo]]",
+ },
+ }
+ };
+
+ model.load(this.params).then(function (resultID) {
+ var recordPartner = model.get(resultID);
+ assert.deepEqual(
+ recordPartner.getContext({fieldName: "product_id"}),
+ {hello2: "world", test2: "gnap"},
+ "field context should have been overriden by xml attribute");
+ assert.deepEqual(
+ recordPartner.getDomain({fieldName: "product_id"}),
+ [["hello2", "like", "world"], ["test2", "like", "gnap"]],
+ "field domain should have been overriden by xml attribute");
+ });
+ model.destroy();
+ });
+
+ QUnit.test('dont write on readonly fields (except if create)', function (assert) {
+ assert.expect(6);
+
+ this.params.fieldNames = ['foo', 'bar'];
+ this.data.partner.onchanges.foo = function (obj) {
+ obj.bar = obj.foo.length;
+ };
+ this.data.partner.fields.bar.readonly = true;
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ mockRPC: function (route, args) {
+ if (args.method === 'write') {
+ assert.deepEqual(args.args[1], {foo: "verylongstring"},
+ "should only save foo field");
+ }
+ if (args.method === 'create') {
+ assert.deepEqual(args.args[0], {
+ foo: "anotherverylongstring",
+ bar: 21,
+ }, "should also save bar field in create mode");
+ }
+ return this._super(route, args);
+ },
+ });
+
+ model.load(this.params).then(function (resultID) {
+ var record = model.get(resultID);
+ assert.strictEqual(record.data.bar, 2,
+ "should be initialized with correct value");
+
+ model.notifyChanges(resultID, {foo: "verylongstring"});
+
+ record = model.get(resultID);
+ assert.strictEqual(record.data.bar, 14,
+ "should be changed with correct value");
+
+ model.save(resultID);
+ });
+
+ // start again, but with a new record
+ delete this.params.res_id;
+ model.load(this.params).then(function (resultID) {
+ var record = model.get(resultID);
+ assert.strictEqual(record.data.bar, false,
+ "should be initialized with correct value");
+
+ model.notifyChanges(resultID, {foo: "anotherverylongstring"});
+
+ record = model.get(resultID);
+ assert.strictEqual(record.data.bar, 21,
+ "should be changed with correct value");
+
+ model.save(resultID);
+ });
+ model.destroy();
+ });
+
+ QUnit.test('default_get with one2many values', function (assert) {
+ assert.expect(1);
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ mockRPC: function (route, args) {
+ if (args.method === 'default_get') {
+ return $.when({
+ product_ids: [[0, 0, {"name": "xdroid"}]]
+ });
+ }
+ return this._super(route, args);
+ },
+ });
+ var params = {
+ fieldNames: ['product_ids'],
+ fields: this.data.partner.fields,
+ modelName: 'partner',
+ type: 'record',
+ };
+ model.load(params).then(function (resultID) {
+ assert.strictEqual(typeof resultID, 'string', "result should be a valid id");
+ });
+ model.destroy();
+ });
+
+ QUnit.test('get method works on unset x2many, after save without reload', function (assert) {
+ assert.expect(3);
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ });
+
+ var params = {
+ fieldNames: ['product_ids'],
+ fields: this.data.partner.fields,
+ modelName: 'partner',
+ type: 'record',
+ };
+
+ model.load(params).then(function (resultID) {
+ assert.ok(model.isDirty(resultID), "datapoint should be dirty");
+ model.save(resultID, {reload: false});
+ var record = model.get(resultID, {raw: true});
+ assert.deepEqual(record.data.product_ids, [],
+ "should have correct value for product_ids field");
+ assert.notOk(model.isDirty(resultID), "datapoint should not be dirty");
+ });
+ model.destroy();
+ });
+
+ QUnit.test('call makeRecord with a pre-fetched many2one field', function (assert) {
+ assert.expect(3);
+ var rpcCount = 0;
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ mockRPC: function (route, args) {
+ rpcCount++;
+ return this._super(route, args);
+ },
+ });
+
+ model.makeRecord('coucou', [{
+ name: 'partner_id',
+ relation: 'partner',
+ type: 'many2one',
+ value: [1, 'first partner'],
+ }], {
+ partner_id: {
+ options: {
+ no_open: true,
+ },
+ },
+ }).then(function (recordID) {
+ var record = model.get(recordID);
+ assert.deepEqual(record.fieldsInfo.default.partner_id, {options: {no_open: true}},
+ "makeRecord should have generated the fieldsInfo");
+ assert.deepEqual(record.data.partner_id.data, {id: 1, display_name: 'first partner'},
+ "many2one should contain the partner with id 1");
+ assert.strictEqual(rpcCount, 0, "makeRecord should not have done any rpc");
+ });
+ model.destroy();
+ });
+
+ QUnit.test('call makeRecord with a many2many field', function (assert) {
+ assert.expect(5);
+ var rpcCount = 0;
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ mockRPC: function (route, args) {
+ rpcCount++;
+ return this._super(route, args);
+ },
+ });
+
+ model.makeRecord('coucou', [{
+ name: 'partner_ids',
+ fields: [{
+ name: 'id',
+ type: 'integer',
+ }, {
+ name: 'display_name',
+ type: 'char',
+ }],
+ relation: 'partner',
+ type: 'many2many',
+ value: [1, 2],
+ }]).then(function (recordID) {
+ var record = model.get(recordID);
+ assert.deepEqual(record.fieldsInfo.default.partner_ids, {},
+ "makeRecord should have generated the fieldsInfo");
+ assert.strictEqual(record.data.partner_ids.count, 2,
+ "there should be 2 elements in the many2many");
+ assert.strictEqual(record.data.partner_ids.data.length, 2,
+ "many2many should be a list of length 2");
+ assert.deepEqual(record.data.partner_ids.data[0].data, {id: 1, display_name: 'first partner'},
+ "many2many should contain the partner with id 1");
+ assert.strictEqual(rpcCount, 1, "makeRecord should have done 1 rpc");
+ });
+ model.destroy();
+ });
+
+ QUnit.test('call makeRecord with a pre-fetched many2many field', function (assert) {
+ assert.expect(5);
+ var rpcCount = 0;
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ mockRPC: function (route, args) {
+ rpcCount++;
+ return this._super(route, args);
+ },
+ });
+
+ model.makeRecord('coucou', [{
+ name: 'partner_ids',
+ fields: [{
+ name: 'id',
+ type: 'integer',
+ }, {
+ name: 'display_name',
+ type: 'char',
+ }],
+ relation: 'partner',
+ type: 'many2many',
+ value: [{
+ id: 1,
+ display_name: "first partner",
+ }, {
+ id: 2,
+ display_name: "second partner",
+ }],
+ }]).then(function (recordID) {
+ var record = model.get(recordID);
+ assert.deepEqual(record.fieldsInfo.default.partner_ids, {},
+ "makeRecord should have generated the fieldsInfo");
+ assert.strictEqual(record.data.partner_ids.count, 2,
+ "there should be 2 elements in the many2many");
+ assert.strictEqual(record.data.partner_ids.data.length, 2,
+ "many2many should be a list of length 2");
+ assert.deepEqual(record.data.partner_ids.data[0].data, {id: 1, display_name: 'first partner'},
+ "many2many should contain the partner with id 1");
+ assert.strictEqual(rpcCount, 0, "makeRecord should not have done any rpc");
+ });
+ model.destroy();
+ });
+
+});});