// Copyright IBM Corp. 2014,2019. All Rights Reserved. // Node module: loopback-datasource-juggler // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT // This test written in mocha+should.js 'use strict'; const should = require('./init.js'); const jdb = require('../'); const ModelBuilder = jdb.ModelBuilder; const DataSource = jdb.DataSource; const Memory = require('../lib/connectors/memory'); const modelBuilder = new ModelBuilder(); const mixins = modelBuilder.mixins; function timestamps(Model, options) { Model.defineProperty('createdAt', {type: Date}); Model.defineProperty('updatedAt', {type: Date}); const originalBeforeSave = Model.beforeSave; Model.beforeSave = function(next, data) { Model.applyTimestamps(data, this.isNewRecord()); if (data.createdAt) { this.createdAt = data.createdAt; } if (data.updatedAt) { this.updatedAt = data.updatedAt; } if (originalBeforeSave) { originalBeforeSave.apply(this, arguments); } else { next(); } }; Model.applyTimestamps = function(data, creation) { data.updatedAt = new Date(); if (creation) { data.createdAt = data.updatedAt; } }; } mixins.define('TimeStamp', timestamps); describe('Model class', function() { it('should define mixins', function() { mixins.define('Example', function(Model, options) { Model.prototype.example = function() { return options; }; }); mixins.define('Demo', function(Model, options) { Model.demoMixin = options.value; }); mixins.define('Multi', function(Model, options) { Model.multiMixin = Model.multiMixin || {}; Model.multiMixin[options.key] = options.value; }); }); it('should apply a mixin class', function() { const Address = modelBuilder.define('Address', { street: {type: 'string', required: true}, city: {type: 'string', required: true}, }); const memory = new DataSource('mem', {connector: Memory}, modelBuilder); const Item = memory.createModel('Item', {name: 'string'}, { mixins: {Address: true}, }); const properties = Item.definition.properties; properties.street.should.eql({type: String, required: true}); properties.city.should.eql({type: String, required: true}); }); it('should fail to apply an undefined mixin class', function() { const memory = new DataSource('mem', {connector: Memory}, modelBuilder); function applyMixin() { memory.createModel('Item', {name: 'string'}, { mixins: {UndefinedMixin: true}, }); } should.throws(applyMixin, 'failed to apply undefined mixin class'); }); it('should apply mixins', function(done) { const memory = new DataSource('mem', {connector: Memory}, modelBuilder); const Item = memory.createModel('Item', {name: 'string'}, { mixins: { TimeStamp: true, Demo: {value: true}, Multi: [ {key: 'foo', value: 'bar'}, {key: 'fox', value: 'baz'}, ], }, }); Item.mixin('Example', {foo: 'bar'}); Item.demoMixin.should.be.true; Item.multiMixin.foo.should.equal('bar'); Item.multiMixin.fox.should.equal('baz'); const properties = Item.definition.properties; properties.createdAt.should.eql({type: Date}); properties.updatedAt.should.eql({type: Date}); Item.create({name: 'Item 1'}, function(err, inst) { inst.createdAt.should.be.a.date; inst.updatedAt.should.be.a.date; inst.example().should.eql({foo: 'bar'}); done(); }); }); it('should fail to apply undefined mixin', function() { const memory = new DataSource('mem', {connector: Memory}, modelBuilder); const Item = memory.createModel('Item', {name: 'string'}); function applyMixin() { Item.mixin('UndefinedMixin', {foo: 'bar'}); } should.throws(applyMixin, 'failed to apply undefined mixin'); }); describe('#mixin()', function() { let Person, Author, Address; beforeEach(function() { Address = modelBuilder.define('Address', { street: {type: 'string', required: true}, city: {type: 'string', required: true}, }); const memory = new DataSource('mem', {connector: Memory}, modelBuilder); Person = memory.createModel('Person', {name: 'string'}); Author = memory.createModel('Author', {name: 'string'}); }); it('should register mixin class into _mixins', function() { Person.mixin(Address); Person._mixins.should.containEql(Address); }); it('should NOT share mixins registry', function() { Person.mixin(Address); Author._mixins.should.not.containEql(Address); }); it('should able to mixin same class', function() { Person.mixin(Address); Author.mixin(Address); Author._mixins.should.containEql(Address); }); }); });