Merge pull request #1831 from garyhgohoos/24544
[xtuple] / test / lib / zombie_auth.js
1 /*jshint node:true, indent:2, curly:false, eqeqeq:true, immed:true, latedef:true, newcap:true, noarg:true,
2 regexp:true, undef:true, strict:true, trailing:true, white:true */
3 /*global XT:true, XM:true, XV:true, XZ:true, enyo:true, XG:true */
4
5 var _ = require('underscore');
6 global.URL = require('url');
7 var parse = global.URL.parse;
8 var resolve = global.URL.resolve;
9 global.URL.parse = function (url) {
10   "use strict";
11   console.log('URL.parse', url);
12   if (_.isObject(url) && _.isString(url.href)) {
13     return parse(url.href);
14   }
15   else {
16     return parse(url);
17   }
18 };
19 global.URL.resolve = function (from, to) {
20   "use strict";
21   console.log('URL.resolve from', from);
22   console.log('URL.resolve to', to);
23   if (_.isObject(from)) {
24     from = from.href || '/';
25   }
26   if (_.isObject(to)) {
27     to = to.href || '';
28   }
29
30   return resolve(from, to);
31 };
32
33
34 // global objects
35 enyo = {};
36 XT = {};
37 XG = {};
38 XM = {};
39 XV = {};
40 XZ = {}; // xTuple Zombie. Used to help zombie within the context of these tests.
41
42 // https://github.com/mikeal/request/issues/418#issuecomment-17149236
43 process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
44
45 var assert = require('assert'),
46   zombie = require('zombie'),
47   _ = require('underscore');
48
49
50 /**
51 Simplest possible usage:
52
53   var zombieTest = require('./zombie_auth');
54   zombieTest.testLoad();
55 */
56 (function () {
57   "use strict";
58
59   var secondsToWait = 40;
60
61   /**
62     Loads up the xTuple environment and makes the global variables globally available.
63
64     The first three options are optional, but if omitted then the login data should be
65     available in the /test/lib/loginData.js file.
66
67     There is one important limitation to this code at the moment: the client-side
68     app must be built. (Going in through debug.html won't work).
69
70     @param {Object} options
71     @param {String} options.username
72     @param {String} options.password
73     @param {String} options.host
74     @param {Boolean} options.verbose
75     @param {Function} options.callback. This function will be called with the zombie browser
76       as a parameter if the app loads up successfully.
77
78     Supported signatures are:
79     loadApp(callback);
80     loadApp({username: "admin", password: "somenew", callback: callback});
81     loadApp({callback: callback, verbose: true});
82   */
83   var loadApp = exports.loadApp = function (options) {
84     options = options || {};
85
86     var username = options.username,
87       password = options.password,
88       database = options.database,
89       host = options.host,
90       callback = options.callback,
91       verboseMode = options.verbose,
92       loginData;
93
94     //
95     // Handle multiple signatures
96     //
97     if (typeof arguments[0] === 'function') {
98       // if the sole parameter is the callback, then we get the auth data from a file
99       callback = arguments[0];
100     }
101
102     if (!username || !password) {
103       try {
104         loginData = require(options.loginDataPath || './login_data');
105       } catch (err) {
106         console.log("Make sure you put your login credentials in the /test/lib/login_data.js file");
107         process.exit(1);
108       }
109
110       username = loginData.data.username;
111       password = loginData.data.pwd;
112       database = loginData.data.org;
113       host = loginData.data.webaddress;
114     }
115     host = host || "https://localhost:443";
116
117     if (options.refreshLogin) {
118       enyo = {};
119       XT = {};
120       XM = {};
121       XV = {};
122       XZ = {};
123     }
124
125     // when we run all our tests we only want to have to log in for the first one
126     if (XT.app) {
127       if (verboseMode) {
128         console.log("Using pre-existing zombie session");
129       }
130       callback();
131       return;
132     }
133
134
135     zombie.visit(host, {debug: verboseMode, runScripts: false}, function (e, browser) {
136       if (e) {
137         console.log("Zombie visit error: ", e);
138       }
139       //
140       // This is the login screen
141       //
142       browser.runScripts = true;
143       browser
144         .fill('id', username)
145         .fill('password', password)
146         .select('database', database)
147         .pressButton('submit', function () {
148
149           // Note: make sure the app is built
150           // XXX this limitation should be fixed, to allow testing off of debug.html
151           // it's possible that Zombie 2.0 will get this right.
152
153           //
154           // Plan to give up after a set time
155           //
156           var timeout = setTimeout(function () {
157               console.log("App did not fully load");
158               process.exit(1);
159             }, secondsToWait * 1000);
160
161           //
162           // Check frequently to see if the app is loaded, and move forward when it is
163           //
164           var interval = setInterval(function () {
165
166             if (browser.window.XT && browser.window.XT.app && browser.window.XT.app.state === 6) {
167
168               // add the global objects to our global namespace
169               enyo = browser.window.enyo;
170               XG = browser.window.XG;
171               XM = browser.window.XM;
172               XT = browser.window.XT;
173               XV = browser.window.XV;
174               XZ.browser = browser;
175               XZ.host = host;
176               XZ.database = database;
177
178               XT.log = function (message, obj) {
179                 if (message && message.toLowerCase().indexOf("error") === 0) {
180                   // errors from the datasource should cause the test to fail
181                   assert.fail(message + " " + JSON.stringify(obj));
182                 }
183                 // log if verbose mode or if the log is a warning
184                 if (verboseMode || (message && message.code)) {
185                   console.log(JSON.stringify(arguments));
186                 }
187               };
188
189               /*
190               var oldNotify = XT.app.$.postbooks.notify;
191               XT.app.$.postbooks.notify = function (notifySender, notifyObj) {
192                 if (notifyObj && notifyObj.type === XM.Model.CRITICAL) {
193                   assert.fail(JSON.stringify(notifyObj));
194                 } else {
195                   oldNotify(notifySender, notifyObj);
196                 }
197               };
198               */
199               // WIP. Not yet working. Probably need to move it up to earlier app start status.
200               /*
201               var oldLoc = XT.String.loc;
202               XT.String.loc = function (str) {
203                 var localized = XT.localizeString(str);
204                 if (localized === str) {
205                   assert.fail(str + " has no translation");
206                 } else {
207                   oldLoc(str);
208                 }
209               };
210               */
211
212               // these are really annoying
213               browser.window.Backbone.Relational.showWarnings = false;
214
215               // clear out both is interval and the I'm-giving-up timeout
216               // we really want neither to be run again.
217               clearInterval(interval);
218               clearTimeout(timeout);
219
220               // give control back to whoever called us
221               callback();
222             }
223           }, 100); // 100 = check to see if the app is loaded every 0.1 seconds
224         });
225     });
226   };
227
228   /**
229     More of a proof-of-concept than anything else.
230    */
231   var testLoad = exports.testLoad = function () {
232     console.log("Testing loadup of app.");
233
234     loadApp(function () {
235       console.log("App loaded successfully.");
236       process.exit(0);
237     });
238   };
239
240 }());