UNPKG

7.3 kBJavaScriptView Raw
1// Copyright IBM Corp. 2018,2019. All Rights Reserved.
2// Node module: loopback-datasource-juggler
3// This file is licensed under the MIT License.
4// License text available at https://opensource.org/licenses/MIT
5
6'use strict';
7
8const should = require('./init.js');
9
10const juggler = require('../');
11const ModelBuilder = juggler.ModelBuilder;
12const {StrongGlobalize} = require('strong-globalize');
13const parentRefHelper = require('./helpers/setup-parent-ref');
14
15describe('ModelBuilder', () => {
16 describe('define()', () => {
17 let builder;
18
19 beforeEach(givenModelBuilderInstance);
20
21 it('sets correct "modelName" property', () => {
22 const MyModel = builder.define('MyModel');
23 MyModel.should.have.property('modelName', 'MyModel');
24 });
25
26 it('sets correct "name" property on model constructor', () => {
27 const MyModel = builder.define('MyModel');
28 MyModel.should.have.property('name', 'MyModel');
29 });
30
31 describe('model class name sanitization', () => {
32 it('converts "-" to "_"', () => {
33 const MyModel = builder.define('Grand-child');
34 MyModel.should.have.property('name', 'Grand_child');
35 });
36
37 it('converts "." to "_"', () => {
38 const MyModel = builder.define('Grand.child');
39 MyModel.should.have.property('name', 'Grand_child');
40 });
41
42 it('converts ":" to "_"', () => {
43 const MyModel = builder.define('local:User');
44 MyModel.should.have.property('name', 'local_User');
45 });
46
47 it('falls back to legacy "ModelConstructor" in other cases', () => {
48 const MyModel = builder.define('Grand\tchild');
49 MyModel.should.have.property('name', 'ModelConstructor');
50 });
51 });
52
53 describe('model with nested properties as function', () => {
54 const Role = function(roleName) {};
55 it('sets correct nested properties', () => {
56 const User = builder.define('User', {
57 role: {
58 type: typeof Role,
59 default: null,
60 },
61 });
62 should.equal(User.getPropertyType('role'), 'ModelConstructor');
63 });
64 });
65
66 describe('model with nested properties as class', () => {
67 class Role {
68 constructor(roleName) {}
69 }
70 it('sets correct nested properties', () => {
71 const User = builder.define('UserWithClass', {
72 role: {
73 type: Role,
74 default: null,
75 },
76 });
77 User.registerProperty('role');
78 should.equal(User.getPropertyType('role'), 'Role');
79 });
80 });
81
82 describe('model with nested properties as embedded model', () => {
83 let Address, Person;
84 const originalWarn = StrongGlobalize.prototype.warn;
85 parentRefHelper(() => builder);
86 before('create stub for warning check', () => {
87 StrongGlobalize.prototype.warn = function gWarnWrapper(...args) {
88 StrongGlobalize.prototype.warn.called++;
89 return originalWarn.apply(this, args);
90 };
91 StrongGlobalize.prototype.warn.called = 0;
92 });
93 beforeEach('Define models', () => {
94 Address = builder.define('Address', {
95 street: {type: 'string'},
96 number: {type: 'number'},
97 });
98 Person = builder.define('Person', {
99 name: {type: 'string'},
100 address: {type: 'Address'},
101 other: {type: 'object'},
102 });
103 });
104 after('restore warning stub', () => {
105 StrongGlobalize.prototype.warn = originalWarn;
106 });
107 it('should properly add the __parent relationship when instantiating parent model', () => {
108 const person = new Person({
109 name: 'Mitsos',
110 address: {street: 'kopria', number: 11},
111 });
112 person.should.have.propertyByPath('address', '__parent').which.equals(person);
113 });
114 it('should add _parent property when setting embedded model after instantiation', () => {
115 const person = new Person({
116 name: 'Mitsos',
117 });
118 person.address = {street: 'kopria', number: 11};
119 person.should.have.propertyByPath('address', '__parent').which.equals(person);
120 });
121 it('should handle nullish embedded property values', () => {
122 const person = new Person({
123 name: 'Mitsos',
124 address: null,
125 });
126 person.should.have.property('address').which.equals(null);
127 });
128 it('should change __parent reference and WARN when moving a child instance to an other parent', () => {
129 const person1 = new Person({
130 name: 'Mitsos',
131 address: {street: 'kopria', number: 11},
132 });
133 const {address} = person1;
134 address.should.be.instanceof(Address).and.have.property('__parent').which.equals(person1);
135 StrongGlobalize.prototype.warn.should.have.property('called', 0); // check that no warn yet
136 const person2 = new Person({
137 name: 'Allos',
138 address,
139 });
140 address.should.have.property('__parent').which.equals(person2);
141 StrongGlobalize.prototype.warn.should.have.property('called', 1); // check we had a warning
142 });
143 it('should NOT provide the __parent property to any serialization of the instance', () => {
144 const person = new Person({
145 name: 'Mitsos',
146 address: {street: 'kopria', number: 11},
147 });
148 person.toJSON().should.not.have.propertyByPath('address', '__parent');
149 person.toObject().should.not.have.propertyByPath('address', '__parent');
150 });
151 it('should NOT provide __parent property in plain object properties', () => {
152 const person = new Person({
153 name: 'Mitsos',
154 address: {street: 'kopria', number: 11},
155 other: {some: 'object'},
156 });
157 person.should.have.property('other').which.eql({some: 'object'}).and.not.has
158 .property('__parent');
159 });
160 });
161
162 describe('Model with properties as list of embedded models', () => {
163 let Person, Address;
164 beforeEach('Define models', () => {
165 Address = builder.define('Address', {
166 street: {type: 'string'},
167 number: {type: 'number'},
168 });
169 Person = builder.define('Person', {
170 name: {type: 'string'},
171 addresses: {type: ['Address']}, // array of addresses
172 });
173 });
174 it('should pass the container model instance as parent to the list item', () => {
175 const person = new Person({
176 name: 'mitsos',
177 addresses: [{
178 street: 'kapou oraia',
179 number: 100,
180 }],
181 });
182 person.should.have.property('addresses').which.has.property('parent')
183 .which.is.instanceof(Person).and.equals(person);
184 });
185 it('should pass the container model instance as parent to the list, when assigning to ' +
186 'the list property', () => {
187 const person = new Person({
188 name: 'mitsos',
189 });
190 person.addresses = [{
191 street: 'kapou oraia',
192 number: 100,
193 }];
194 person.should.have.property('addresses').which.has.property('parent')
195 .which.is.instanceof(Person).and.equals(person);
196 });
197 });
198
199 function givenModelBuilderInstance() {
200 builder = new ModelBuilder();
201 }
202 });
203});