UNPKG

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