UNPKG

11.7 kBJavaScriptView Raw
1#!/usr/bin/env node
2
3var http = require('http'),
4 localtunnel = require('localtunnel'),
5 parseArgs = require('minimist'),
6 shell = require('shelljs'),
7 fs = require('fs'),
8 request = require('request'),
9 tmp = require('tmp'),
10 path = require('path');
11
12var PORT = 8008;
13var TIMEOUT = 10 * 60 * 1000; // 10 minutes in msec - this will become a param
14
15
16
17function ParamedicRunner(_platformId,_plugins,_callback,bJustBuild,nPort,msTimeout,browserify,bSilent,bVerbose) {
18 this.tunneledUrl = "";
19 this.port = nPort;
20 this.justBuild = bJustBuild;
21 this.plugins = _plugins;
22 this.platformId = _platformId;
23 this.callback = _callback;
24 this.tempFolder = null;
25 this.timeout = msTimeout;
26 this.verbose = bVerbose;
27
28 if(browserify) {
29 this.browserify = "--browserify";
30 } else {
31 this.browserify = '';
32 }
33
34 if(bSilent) {
35 var logOutput = this.logOutput = [];
36 this.logMessage = function(msg) {
37 logOutput.push(msg);
38 };
39 }
40 else {
41 this.logMessage = function(msg) {
42 console.log(msg);
43 };
44 }
45}
46
47ParamedicRunner.prototype = {
48 run: function() {
49 var cordovaResult = shell.exec('cordova --version', {silent:!this.verbose});
50 if(cordovaResult.code) {
51 this.logMessage(cordovaResult.output);
52 // this would be fatal
53 process.exit(cordovaResult.code);
54 }
55
56 // limit runtime to TIMEOUT msecs
57 var self = this;
58 setTimeout(function(){
59 self.logMessage("This test seems to be blocked :: timeout exceeded. Exiting ...");
60 self.cleanUpAndExitWithCode(1);
61 },self.timeout);
62
63 this.createTempProject();
64 this.installPlugins();
65 this.startServer();
66 },
67 createTempProject: function() {
68 this.tempFolder = tmp.dirSync();
69 tmp.setGracefulCleanup();
70 this.logMessage("cordova-paramedic: creating temp project at " + this.tempFolder.name);
71 shell.exec('cordova create ' + this.tempFolder.name,{silent:!this.verbose});
72 shell.cd(this.tempFolder.name);
73 },
74 installSinglePlugin: function(plugin) {
75 this.logMessage("cordova-paramedic: installing " + plugin);
76 var pluginPath = path.resolve(this.storedCWD, plugin);
77 var plugAddCmd = shell.exec('cordova plugin add ' + pluginPath, {silent:!this.verbose});
78 if(plugAddCmd.code !== 0) {
79 this.logMessage('Failed to install plugin : ' + plugin);
80 this.cleanUpAndExitWithCode(1);
81 }
82 },
83 installPlugins: function() {
84 for(var n = 0; n < this.plugins.length; n++) {
85 var plugin = this.plugins[n];
86 this.installSinglePlugin(plugin);
87 if(!this.justBuild) {
88 this.installSinglePlugin(path.join(plugin,"tests"));
89 }
90 }
91
92 if(!this.justBuild) {
93 this.logMessage("cordova-paramedic: installing plugin-test-framework");
94 var plugAddCmd = shell.exec('cordova plugin add https://github.com/apache/cordova-plugin-test-framework',
95 {silent:!this.verbose});
96 if(plugAddCmd.code !== 0) {
97 this.logMessage('cordova-plugin-test-framework');
98 this.cleanUpAndExitWithCode(1);
99 }
100 }
101 },
102 cleanUpAndExitWithCode: function(exitCode,resultsObj) {
103 shell.cd(this.storedCWD);
104 // the TMP_FOLDER.removeCallback() call is throwing an exception, so we explicitly delete it here
105 shell.exec('rm -rf ' + this.tempFolder.name);
106 var logStr = this.logOutput ? this.logOutput.join("\n") : null;
107 this.callback(exitCode,resultsObj,logStr);
108 },
109 writeMedicLogUrl: function(url) {
110 this.logMessage("cordova-paramedic: writing medic log url to project");
111 var obj = {logurl:url};
112 fs.writeFileSync(path.join("www","medic.json"),JSON.stringify(obj));
113 },
114 setConfigStartPage: function() {
115 this.logMessage("cordova-paramedic: setting app start page to test page");
116 var fileName = 'config.xml';
117 var configStr = fs.readFileSync(fileName).toString();
118 if(configStr) {
119 configStr = configStr.replace("src=\"index.html\"","src=\"cdvtests/index.html\"");
120 fs.writeFileSync(fileName, configStr);
121 }
122 else {
123 this.logMessage("Oops, could not find config.xml");
124 }
125 },
126 startServer: function() {
127
128 if(this.justBuild) {
129 this.addAndRunPlatform();
130 return;
131 }
132 /// else ....
133
134 this.logMessage("cordova-paramedic: starting local medic server " + this.platformId);
135 var self = this;
136 var server = http.createServer(this.requestListener.bind(this));
137
138 server.listen(this.port, '127.0.0.1',function onServerConnect() {
139
140 switch(self.platformId) {
141 case "ios" : // intentional fallthrough
142 case "browser" :
143 case "windows" :
144 self.writeMedicLogUrl("http://127.0.0.1:" + self.port);
145 self.addAndRunPlatform();
146 break;
147 case "android" :
148 self.writeMedicLogUrl("http://10.0.2.2:" + self.port);
149 self.addAndRunPlatform();
150 break;
151 case "wp8" :
152 //localtunnel(PORT, tunnelCallback);
153 request.get('http://google.com/', function(e, res, data) {
154 if(e) {
155 self.logMessage("failed to detect ip address");
156 self.cleanUpAndExitWithCode(1);
157 }
158 else {
159 var ip = res.req.connection.localAddress ||
160 res.req.socket.localAddress;
161 self.logMessage("Using ip : " + ip);
162 self.writeMedicLogUrl("http://" + ip + ":" + self.port);
163 self.addAndRunPlatform();
164 }
165 });
166 break;
167 default :
168 self.logMessage("platform is not supported :: " + self.platformId);
169 self.cleanUpAndExitWithCode(1);
170 break;
171 }
172 });
173 },
174 requestListener: function(request, response) {
175 var self = this;
176 if (request.method == 'PUT' || request.method == 'POST') {
177 var body = '';
178 request.on('data', function (data) {
179 body += data;
180 // Too much POST data, kill the connection!
181 if (body.length > 1e6) {
182 req.connection.destroy();
183 }
184 });
185 request.on('end', function (res) {
186 if(body.indexOf("mobilespec") == 2){ // {\"mobilespec\":{...}}
187 try {
188 //logMessage("body = " + body);
189 var results = JSON.parse(body);
190 self.logMessage("Results: ran " +
191 results.mobilespec.specs +
192 " specs with " +
193 results.mobilespec.failures +
194 " failures");
195 if(results.mobilespec.failures > 0) {
196 self.cleanUpAndExitWithCode(1,results);
197 }
198 else {
199 self.cleanUpAndExitWithCode(0,results);
200 }
201
202 }
203 catch(err) {
204 self.logMessage("parse error :: " + err);
205 self.cleanUpAndExitWithCode(1);
206 }
207 }
208 else {
209 self.logMessage("console-log:" + body);
210 }
211 });
212 }
213 else {
214 self.logMessage(request.method);
215 response.writeHead(200, { 'Content-Type': 'text/plain'});
216 response.write("Hello"); // sanity check to make sure server is running
217 response.end();
218 }
219 },
220 addAndRunPlatform: function() {
221 var self = this;
222 if(self.justBuild) {
223 self.logMessage("cordova-paramedic: adding platform");
224 shell.exec('cordova platform add ' + self.platformId,{silent:!this.verbose});
225 shell.exec('cordova prepare '+ self.browserify,{silent:!this.verbose});
226 self.logMessage("building ...");
227
228 shell.exec('cordova build ' + self.platformId.split("@")[0],
229 {async:true,silent:!this.verbose},
230 function(code,output){
231 if(code !== 0) {
232 self.logMessage("Error: cordova build returned error code " + code);
233 self.logMessage("output: " + output);
234 self.cleanUpAndExitWithCode(1);
235 }
236 else {
237 self.cleanUpAndExitWithCode(0);
238 }
239 }
240 );
241 }
242 else {
243 self.setConfigStartPage();
244 self.logMessage("cordova-paramedic: adding platform");
245 shell.exec('cordova platform add ' + self.platformId,{silent:!this.verbose});
246 shell.exec('cordova prepare '+ self.browserify,{silent:!this.verbose});
247
248 shell.exec('cordova emulate ' + self.platformId.split("@")[0] + " --phone",
249 {async:true,silent:!this.verbose},
250 function(code,output){
251 if(code !== 0) {
252 self.logMessage("Error: cordova emulate return error code " + code);
253 self.logMessage("output: " + output);
254 self.cleanUpAndExitWithCode(1);
255 }
256 }
257 );
258 }
259 },
260 tunnelCallback: function(err, tunnel) {
261 if (err){
262 this.logMessage("failed to create tunnel url, check your internet connectivity.");
263 this.cleanUpAndExitWithCode(1);
264 }
265 else {
266 // the assigned public url for your tunnel
267 // i.e. https://abcdefgjhij.localtunnel.me
268 this.tunneledUrl = tunnel.url;
269 this.logMessage("cordova-paramedic: tunneledURL = " + tunneledUrl);
270 this.writeMedicLogUrl(tunneledUrl);
271 this.addAndRunPlatform();
272 }
273 }
274};
275
276var storedCWD = null;
277
278exports.run = function(_platformId,_plugins,_callback,bJustBuild,nPort,msTimeout,bBrowserify,bSilent,bVerbose) {
279
280 storedCWD = storedCWD || process.cwd();
281 if(!_plugins) {
282 _plugins = process.cwd();
283 }
284 if(_platformId && _plugins) {
285
286 // make it an array if it's not
287 var plugins = Array.isArray(_plugins) ? _plugins : [_plugins];
288
289 // if we are passed a callback, we will use it,
290 // otherwise just make a quick and dirty one
291 var callback = ( _callback && _callback.apply ) ? _callback : function(resCode,resObj) {
292 process.exit(resCode);
293 };
294
295 var runner = new ParamedicRunner(_platformId, plugins, callback, !!bJustBuild,
296 nPort || PORT, msTimeout || TIMEOUT, !!bBrowserify, !!bSilent, !!bVerbose);
297
298 runner.storedCWD = storedCWD;
299 return runner.run();
300 }
301 else {
302 console.error("Error : Missing platformId and/or plugins");
303 }
304};