1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | 'use strict';
|
7 |
|
8 | const should = require('./init.js');
|
9 |
|
10 | const juggler = require('../');
|
11 | const ModelBuilder = juggler.ModelBuilder;
|
12 | const {StrongGlobalize} = require('strong-globalize');
|
13 | const parentRefHelper = require('./helpers/setup-parent-ref');
|
14 |
|
15 | describe('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);
|
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);
|
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']},
|
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 | });
|