Issue #23593:correctly handle nulls in address
[xtuple] / test / lib / runner_engine.js
1 /*jshint trailing:true, white:true, indent:2, strict:true, curly:true,
2   immed:true, eqeqeq:true, forin:true, latedef:true,
3   newcap:true, noarg:true, undef:true */
4 /*global XT:true, XM:true, XV:true, exports:true, describe:true, it:true,
5 require:true, __dirname:true, console:true */
6
7 // TODO: test "used"
8 // i.e.: A XM.ShipVia object can not be deleted if it has been assigned as the default customer ship via in sales settings.
9
10 // TODO: test defaults
11
12 (function () {
13   "use strict";
14
15   var crud = require('./crud'),
16     smoke = require('./smoke'),
17     _ = require("underscore"),
18     path = require('path'),
19     assert = require("chai").assert,
20     zombieAuth = require("./zombie_auth");
21
22   var runSpec = function (specContents) {
23     if (!specContents) {
24       return;
25     }
26
27     var spec = specContents.spec;
28
29     describe(spec.recordType + " test", function () {
30       this.pending = spec.skipAll;
31
32       if (_.isString(spec.updatableField)) {
33         spec.updateHash = {};
34         spec.updateHash[spec.updatableField] = "Test" + Math.random();
35       }
36
37
38       if (spec.skipBoilerplateTests && specContents.additionalTests) {
39         specContents.additionalTests();
40         return;
41       } else if (spec.skipBoilerplateTests) {
42         return;
43       }
44
45       //
46       // Run CRUD model tests
47       //
48       if (!spec.skipCrud) {
49         crud.runAllCrud(spec);
50       } else {
51         // even if we skip CRUD we have to create a model
52         it('can be loaded with a zombie session', function (done) {
53           this.timeout(40 * 1000);
54           zombieAuth.loadApp({callback: done, verbose: spec.verbose,
55             loginDataPath: spec.loginDataPath});
56         });
57         it('can be created', function () {
58           spec.model = new XM[spec.recordType.substring(3)]();
59         });
60       }
61
62       //
63       // Smoke Crud
64       //
65       if (!spec.skipSmoke) {
66         smoke.runUICrud(spec);
67       }
68
69       if (!spec.skipModelConfig) {
70         //
71         // Verify required fields
72         //
73         if (spec.requiredAttributes) {
74           _.each(spec.requiredAttributes, function (attr) {
75             it("the " + attr + " attribute is required", function () {
76               assert.include(spec.model.requiredAttributes, attr);
77             });
78           });
79         }
80
81         //
82         // Verify lockability
83         //
84         it(spec.isLockable ? "is lockable" : "is not lockable", function () {
85           assert.equal(spec.isLockable, spec.model.lockable);
86         });
87
88         //
89         // Verify inheritance
90         //
91         if (spec.instanceOf === "XM.Document") {
92           it("inherits from XM.Document", function () {
93             assert.isTrue(spec.model instanceof XM.Document);
94           });
95         } else if (spec.instanceOf === "XM.Model") {
96           it("inherits from XM.Model but not XM.Document", function () {
97             assert.isTrue(spec.model instanceof XM.Model);
98             assert.isFalse(spec.model instanceof XM.Document);
99           });
100         } else {
101           it("has its inheritance defined in the test spec", function () {
102             assert.fail();
103           });
104         }
105
106         //
107         // Verify ID attribute
108         //
109         if (spec.idAttribute) {
110           it("has " + spec.idAttribute + " as its idAttribute", function () {
111             assert.equal(spec.idAttribute, spec.model.idAttribute);
112           });
113         } else {
114           it("has its id attribute defined in the test spec", function () {
115             assert.fail();
116           });
117         }
118
119         //
120         // Verify Document Key
121         //
122         if (spec.documentKey) {
123           it("has " + spec.documentKey + " as its documentKey", function () {
124             assert.equal(spec.documentKey, spec.model.documentKey);
125           });
126         }
127
128         //
129         // Make sure we're testing the enforceUpperCase (the asserts themselves are in CRUD)
130         //
131         if (spec.enforceUpperKey) {
132           it((spec.enforceUpperKey ? "Enforces" : "Does not enforce") + " uppercasing the key", function () {
133             assert.equal(spec.model.enforceUpperKey, spec.enforceUpperKey);
134           });
135           if (!_.isBoolean(spec.enforceUpperKey)) {
136             it("has its enforceUpperKey convention defined in the test spec", function () {
137               assert.fail();
138             });
139           }
140         }
141
142         //
143         // Verify attributes
144         //
145         _.each(spec.attributes, function (attr) {
146           it("contains the " + attr + " attribute", function () {
147             assert.include(spec.model.getAttributeNames(), attr);
148           });
149         });
150         if (!spec.attributes || spec.attributes.length === 0) {
151           it("has some attributes defined in the test spec", function () {
152             assert.fail();
153           });
154         }
155
156         //
157         // Verify privileges are declared correctly by the extensions
158         //
159         _.each(spec.privileges, function (priv) {
160           if (typeof priv === 'string') {
161             _.each(spec.extensions, function (extension) {
162               it("has privilege " + priv + " declared by the " + extension + " extension", function () {
163                 assert.isDefined(_.findWhere(XT.session.relevantPrivileges,
164                   {privilege: priv, module: spec.relevantPrivilegeModule || extension}));
165               });
166             });
167             /*
168             XXX this could get tripped up by non-core extensions
169             it("has privilege " + priv + " not declared by any other extensions", function () {
170               var matchedPriv = _.filter(XT.session.relevantPrivileges, function (sessionPriv) {
171                 return sessionPriv.privilege === priv && !_.contains(spec.extensions, sessionPriv.module);
172               });
173               assert.equal(0, matchedPriv.length);
174             });
175             */
176             //
177             // Make sure the privilege is translated
178             //
179             it("has privilege " + priv + " that is translated in the strings file", function () {
180               var privLoc = "_" + XT.String.camelize(priv);
181               assert.notEqual(XT.String.loc(privLoc), privLoc);
182             });
183           }
184         });
185
186         //
187         // Verify Privileges
188         //
189         _.each(spec.privileges, function (priv, key) {
190           var methodMap = {
191               createReadUpdateDelete: ["canCreate", "canRead", "canUpdate", "canDelete"],
192               createUpdateDelete: ["canCreate", "canUpdate", "canDelete"],
193               createUpdate: ["canCreate", "canUpdate"],
194               create: ["canCreate"],
195               read: ["canRead"],
196               update: ["canUpdate"],
197               delete: ["canDelete"]
198             },
199             pertinentMethods = methodMap[key],
200             updatePriv = spec.privileges.update ||
201               spec.privileges.createUpdate ||
202               spec.privileges.createUpdateDelete;
203
204           it("needs " + priv + " privilege to perform action " + key, function () {
205             var schema = XT.session.schemas.XM.get(XT.String.suffix(spec.recordType)),
206               personalPrivs = schema.privileges.personal,
207               Klass = XT.getObjectByName(spec.recordType);
208
209             if (personalPrivs) {
210               //console.log("skipping personal");
211               // TODO: don't let personal privs mess us up. Find a way to test them.
212             } else if (_.isString(priv)) {
213               assert.isDefined(pertinentMethods); // make sure we're testing for the priv
214               XT.session.privileges.attributes[priv] = false;
215               if (key === "read" && updatePriv) {
216                 // update privs are sufficient for read, so we have to toggle those too
217                 XT.session.privileges.attributes[updatePriv] = false;
218               }
219               _.each(pertinentMethods, function (pertinentMethod) {
220                 assert.isFalse(Klass[pertinentMethod]());
221               });
222               XT.session.privileges.attributes[priv] = true;
223               if (key === "read" && updatePriv) {
224                 // update privs are sufficient for read, so we have to toggle those too
225                 XT.session.privileges.attributes[updatePriv] = true;
226               }
227               _.each(pertinentMethods, function (pertinentMethod) {
228                 assert.isTrue(Klass[pertinentMethod]());
229               });
230
231             } else if (_.isBoolean(priv)) {
232               _.each(pertinentMethods, function (pertinentMethod) {
233                 assert.equal(Klass[pertinentMethod](), priv);
234               });
235
236             } else {
237               it("has privilege " + priv + " that's a string or boolean in the test spec", function () {
238                 assert.fail();
239               });
240             }
241           });
242         });
243
244         //
245         // Test that the collection exists
246         //
247         if (spec.collectionType) {
248           it("backs the " + spec.collectionType + " collection", function () {
249             var Collection = XT.getObjectByName(spec.collectionType),
250               modelPrototype = Collection.prototype.model.prototype,
251               editableModel = modelPrototype.editableModel || modelPrototype.recordType;
252
253             assert.isFunction(Collection);
254             assert.equal(editableModel, spec.recordType);
255           });
256         } else if (spec.collectionType === null) {
257           // TODO: loop through the existing collections and make sure that
258           // none are backed by spec.recordType
259         } else {
260           it("has no colletion specified in the test spec", function () {
261             assert.fail();
262           });
263         }
264
265         //
266         // Test that the cache exists
267         //
268         if (spec.cacheName) {
269           it("is cached as " + spec.cacheName, function () {
270             var cache = XT.getObjectByName(spec.cacheName);
271             assert.isObject(cache);
272             assert.equal(cache.model.prototype.recordType, spec.recordType);
273           });
274
275         } else if (spec.cacheName === null) {
276           /*
277           TODO: probably the best thing to do is to loop through the caches and make sure
278           that none of them are backed by spec.recordType
279           it("is not cached", function () {
280
281           });
282           */
283         } else {
284           it("has a cache (or null for no cache) specified in the test spec", function () {
285             assert.fail();
286           });
287         }
288       }
289
290       // TODO: verify that the cache is made available by certain extensions and not others
291       // TODO: verify that the list is made available by certain extensions and not others
292
293       if (specContents.additionalTests) {
294         specContents.additionalTests();
295       }
296       if (specContents.extensionTests) {
297         specContents.extensionTests();
298       }
299
300     });
301   };
302
303   exports.runSpec = runSpec;
304 }());