applebot 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +38 -0
- data/LICENSE +19 -0
- data/README.md +153 -0
- data/Rakefile +13 -0
- data/applebot.gemspec +27 -0
- data/bin/applebot +73 -0
- data/create_app_id_manifest.js +6 -0
- data/create_manifest.js +24 -0
- data/lib/applebot/command_proxy.rb +44 -0
- data/lib/applebot/commands.rb +74 -0
- data/lib/applebot/error.rb +97 -0
- data/lib/applebot/mkmf.rb +25 -0
- data/lib/applebot/shell.rb +79 -0
- data/lib/applebot/version.rb +5 -0
- data/lib/applebot.rb +109 -0
- data/phantom/_commands.json +318 -0
- data/phantom/applebot.js +656 -0
- data/phantom/create_app.js +355 -0
- data/phantom/create_app_id.js +80 -0
- data/phantom/create_profile.js +134 -0
- data/phantom/delete_app_id.js +57 -0
- data/phantom/download_profile.js +36 -0
- data/phantom/list_app_id.js +66 -0
- data/phantom/list_profile.js +23 -0
- data/phantom/reject_binary_app.js +73 -0
- data/phantom/remove_from_sale_app.js +50 -0
- data/phantom/update_app.js +309 -0
- data/update_app_manifest.js +9 -0
- metadata +143 -0
data/phantom/applebot.js
ADDED
@@ -0,0 +1,656 @@
|
|
1
|
+
/* applebot.js */
|
2
|
+
|
3
|
+
var require = patchRequire(require);
|
4
|
+
var fs = require('fs');
|
5
|
+
var Casper = require('casper');
|
6
|
+
|
7
|
+
var iterate = function(object, callback) {
|
8
|
+
var keys = Object.keys(object);
|
9
|
+
for (var i = keys.length - 1; i >= 0; i--) {
|
10
|
+
var key = keys[i];
|
11
|
+
var value = object[key];
|
12
|
+
callback(key, value);
|
13
|
+
}
|
14
|
+
};
|
15
|
+
|
16
|
+
var merge = function(obj1,obj2) {
|
17
|
+
var obj3 = {};
|
18
|
+
for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; }
|
19
|
+
for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; }
|
20
|
+
return obj3;
|
21
|
+
};
|
22
|
+
|
23
|
+
var isFunction = function(obj) {
|
24
|
+
return typeof(obj) === 'function';
|
25
|
+
};
|
26
|
+
|
27
|
+
var isUndefined = function(obj) {
|
28
|
+
return obj === void(0);
|
29
|
+
};
|
30
|
+
|
31
|
+
var $outputFormat;
|
32
|
+
var jsonLog = function(obj) {
|
33
|
+
console.log(JSON.stringify(obj));
|
34
|
+
};
|
35
|
+
console.formatLog = function(string, json) {
|
36
|
+
if ($outputFormat === 'json') {
|
37
|
+
jsonLog(merge({normal_output: string}, json));
|
38
|
+
}
|
39
|
+
else if (string.length > 0) {
|
40
|
+
console.log(string);
|
41
|
+
}
|
42
|
+
};
|
43
|
+
|
44
|
+
var die = function(page, message, metadata) {
|
45
|
+
if (isUndefined(metadata)) {
|
46
|
+
metadata = {};
|
47
|
+
}
|
48
|
+
console.formatLog("", {event: "debug_html", html: page.getHTML()});
|
49
|
+
//var error = merge({event: "error", message: message}, metadata);
|
50
|
+
//console.formatLog("☠ " + message, error);
|
51
|
+
page.die("☠ " + message, 1);
|
52
|
+
};
|
53
|
+
|
54
|
+
var dieFromException = function(page, ex, metadata) {
|
55
|
+
if (isUndefined(metadata)) {
|
56
|
+
metadata = {};
|
57
|
+
}
|
58
|
+
metadata.stack = ex.stack;
|
59
|
+
die(page, ex.message, metadata);
|
60
|
+
};
|
61
|
+
|
62
|
+
var Shortcuts = {
|
63
|
+
findHrefSelector: function(innerHTML, page, userQueryScope, options) {
|
64
|
+
var queryScope = 'a'
|
65
|
+
if (userQueryScope) {
|
66
|
+
queryScope = userQueryScope + ' ' + queryScope;
|
67
|
+
}
|
68
|
+
if (!options) {
|
69
|
+
options = {};
|
70
|
+
}
|
71
|
+
var href = page.evaluate(function(innerHTML, queryScope, options) {
|
72
|
+
var links = document.querySelectorAll(queryScope);
|
73
|
+
var href = false;
|
74
|
+
for(var i = 0; i < links.length; i++) {
|
75
|
+
var link = links[i];
|
76
|
+
if (options.fuzzy) {
|
77
|
+
if (link.innerHTML.toLowerCase().indexOf(innerHTML.toLowerCase()) !== -1) {
|
78
|
+
href = link.href;
|
79
|
+
}
|
80
|
+
}
|
81
|
+
else if (link.innerHTML === innerHTML) {
|
82
|
+
href = link.href;
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
return href;
|
87
|
+
}, innerHTML, queryScope, options);
|
88
|
+
|
89
|
+
var hrefSelector = false;
|
90
|
+
if (href) {
|
91
|
+
hrefSelector = queryScope +'[href="' + href.replace("https://itunesconnect.apple.com", "") +'"]';
|
92
|
+
}
|
93
|
+
|
94
|
+
if (href === false || !page.exists(hrefSelector)) {
|
95
|
+
page.debugHTML();
|
96
|
+
throw ("Error: could not find " + innerHTML + " link: " + hrefSelector);
|
97
|
+
}
|
98
|
+
|
99
|
+
return hrefSelector;
|
100
|
+
},
|
101
|
+
findFieldName: function(page, fieldLabel, baseSelector) {
|
102
|
+
var fieldName = page.evaluate(function(fieldLabel, baseSelector) {
|
103
|
+
var fieldContainers = document.querySelectorAll(baseSelector);
|
104
|
+
var fieldName = false;
|
105
|
+
for (var i = 0; i < fieldContainers.length; i++) {
|
106
|
+
var parent = fieldContainers[i];
|
107
|
+
var children = parent.children;
|
108
|
+
for (var j = 0; j < children.length; j++) {
|
109
|
+
var child = children[j];
|
110
|
+
if (child.innerHTML === fieldLabel) {
|
111
|
+
fieldName = parent.querySelector('input').name;
|
112
|
+
break;
|
113
|
+
}
|
114
|
+
}
|
115
|
+
if (fieldName !== false) {
|
116
|
+
break;
|
117
|
+
}
|
118
|
+
}
|
119
|
+
|
120
|
+
return fieldName;
|
121
|
+
}, fieldLabel, baseSelector);
|
122
|
+
if (fieldName === false) {
|
123
|
+
die(page, "Error: could not find " + fieldLabel + " field");
|
124
|
+
}
|
125
|
+
return fieldName;
|
126
|
+
},
|
127
|
+
openManageApps: function(page) {
|
128
|
+
var hrefSelector = Shortcuts.findHrefSelector("Manage Your Apps", page);
|
129
|
+
page.click(hrefSelector);
|
130
|
+
},
|
131
|
+
getScreenshotPaths: function(userOptions, optionsKey) {
|
132
|
+
var screenshotsOption = userOptions[optionsKey];
|
133
|
+
var screenshotPaths = [];
|
134
|
+
|
135
|
+
if (screenshotsOption.charAt(0) === "[") {
|
136
|
+
// using JSON
|
137
|
+
screenshotPaths = JSON.parse(screenshotsOption);
|
138
|
+
}
|
139
|
+
else {
|
140
|
+
screenshotPaths = [screenshotsOption];
|
141
|
+
}
|
142
|
+
|
143
|
+
return screenshotPaths;
|
144
|
+
},
|
145
|
+
popScreenshotPaths: function(userOptions, optionsKey) {
|
146
|
+
var screenshotPaths = Shortcuts.getScreenshotPaths(userOptions, optionsKey);
|
147
|
+
|
148
|
+
var value = screenshotPaths.pop();
|
149
|
+
userOptions[optionsKey] = JSON.stringify(screenshotPaths);
|
150
|
+
return value;
|
151
|
+
},
|
152
|
+
addStepsForScreenshotGroup: function(applebot, page, userOptions, screenshotStepOption) {
|
153
|
+
var key_path = screenshotStepOption.key_path;
|
154
|
+
var selector = screenshotStepOption.selector;
|
155
|
+
var description = screenshotStepOption.description;
|
156
|
+
var next_step = screenshotStepOption.next_step;
|
157
|
+
var while_step = screenshotStepOption.while_step;
|
158
|
+
|
159
|
+
var numScreenshots = Shortcuts.getScreenshotPaths(userOptions, key_path).length;
|
160
|
+
var waitForScreenshotCount = function(screenshotSelector, count) {
|
161
|
+
var wait = function() {
|
162
|
+
var numberOfScreenshots = page.evaluate(function(screenshotSelector) {
|
163
|
+
return document.querySelectorAll(screenshotSelector).length;
|
164
|
+
}, screenshotSelector);
|
165
|
+
return numberOfScreenshots === count;
|
166
|
+
}
|
167
|
+
wait.timeout = 8000 * numScreenshots;
|
168
|
+
return wait;
|
169
|
+
};
|
170
|
+
|
171
|
+
for (var i = 1; i <= numScreenshots; i++) {
|
172
|
+
var numUploaded = 0;
|
173
|
+
applebot.step("Wait for " + description + " screenshot #" + i + " to upload", 'waitFor', waitForScreenshotCount(selector, i), function() {
|
174
|
+
numUploaded += 1;
|
175
|
+
if (numUploaded >= numScreenshots) {
|
176
|
+
applebot.action(next_step);
|
177
|
+
}
|
178
|
+
else {
|
179
|
+
applebot.action(while_step);
|
180
|
+
}
|
181
|
+
});
|
182
|
+
}
|
183
|
+
},
|
184
|
+
waitToParseProfiles: function(applebot, page, profileType, callback) {
|
185
|
+
page.evaluate(function(profileType) {
|
186
|
+
window.profileType = profileType;
|
187
|
+
}, profileType);
|
188
|
+
|
189
|
+
if (!page.evaluate(function() { return window.profileDataURL; })) {
|
190
|
+
throw "Invalid profile list page, missing window.profileDataURL";
|
191
|
+
}
|
192
|
+
|
193
|
+
applebot.action("Grab profile data", function() {
|
194
|
+
var parsedProfiles = page.evaluate(function() {
|
195
|
+
return window.parsedProfiles;
|
196
|
+
});
|
197
|
+
callback(parsedProfiles);
|
198
|
+
});
|
199
|
+
|
200
|
+
var waitFor = function() {
|
201
|
+
var result = page.evaluate(function() {
|
202
|
+
if (window.parseFinished) {
|
203
|
+
return true;
|
204
|
+
}
|
205
|
+
if (window.parseRequest) {
|
206
|
+
return false;
|
207
|
+
}
|
208
|
+
window.parseStarted = true;
|
209
|
+
var url = window.profileDataURL + "&type=" + window.profileType;
|
210
|
+
window.parseRequest = $.post(url, function(remoteData, success) {
|
211
|
+
var profiles = {};
|
212
|
+
remoteData.provisioningProfiles.forEach(function(profile) {
|
213
|
+
var appId = profile.appId.identifier;
|
214
|
+
profiles[appId] = {
|
215
|
+
id: profile.provisioningProfileId,
|
216
|
+
download_url: "https://developer.apple.com/account/ios/profile/profileContentDownload.action?displayId=" + profile.provisioningProfileId
|
217
|
+
};
|
218
|
+
});
|
219
|
+
window.parsedProfiles = profiles;
|
220
|
+
window.parseFinished = true;
|
221
|
+
});
|
222
|
+
return false;
|
223
|
+
});
|
224
|
+
return result;
|
225
|
+
};
|
226
|
+
applebot.step("Wait for profiles to parse", 'waitFor', waitFor, function() {
|
227
|
+
applebot.action("Grab profile data");
|
228
|
+
}, {timeout: 15 * 1000});
|
229
|
+
},
|
230
|
+
safeFillSelectors: function(page, formSelector, fillOptions, doSubmission) {
|
231
|
+
if (doSubmission === undefined) {
|
232
|
+
doSubmission = true;
|
233
|
+
}
|
234
|
+
var safeOptions = {};
|
235
|
+
|
236
|
+
iterate(fillOptions, function(selector, value) {
|
237
|
+
if (page.exists(selector)) {
|
238
|
+
safeOptions[selector] = value;
|
239
|
+
}
|
240
|
+
});
|
241
|
+
|
242
|
+
page.fillSelectors(formSelector, safeOptions, doSubmission);
|
243
|
+
},
|
244
|
+
safeFill: function(page, formSelector, fillOptions, doSubmission) {
|
245
|
+
var transformedFillOptions = {};
|
246
|
+
|
247
|
+
|
248
|
+
iterate(fillOptions, function(fieldName, value) {
|
249
|
+
transformedFillOptions['[name="' + fieldName + '"]'] = value;
|
250
|
+
});
|
251
|
+
|
252
|
+
|
253
|
+
Shortcuts.safeFillSelectors(page, formSelector, transformedFillOptions, doSubmission);
|
254
|
+
},
|
255
|
+
filterBundleIdsFromItunesConnect: function(page, toTitle) {
|
256
|
+
var bundleIdMap = {};
|
257
|
+
var bundleIdFieldInfo = page.evaluate(function() {
|
258
|
+
var bundleIdField = document.querySelector('.bundleIdentifierBox select');
|
259
|
+
if (!bundleIdField) {
|
260
|
+
return false;
|
261
|
+
}
|
262
|
+
var bundleIdFieldName = bundleIdField.name;
|
263
|
+
var bundleIdFieldOptions = [];
|
264
|
+
for (var i = 0; i < bundleIdField.options.length; i++) {
|
265
|
+
var option = bundleIdField.options[i];
|
266
|
+
if (option.innerHTML === 'Select') {
|
267
|
+
continue;
|
268
|
+
}
|
269
|
+
bundleIdFieldOptions.push([option.innerHTML, option.value]);
|
270
|
+
}
|
271
|
+
|
272
|
+
return {
|
273
|
+
name: bundleIdFieldName,
|
274
|
+
options: bundleIdFieldOptions
|
275
|
+
};
|
276
|
+
});
|
277
|
+
if (bundleIdFieldInfo === false) {
|
278
|
+
die(page, "Error: could not find Bundle ID field");
|
279
|
+
}
|
280
|
+
var bundleIdOptions = bundleIdFieldInfo.options;
|
281
|
+
var i = bundleIdOptions.length; //or 10
|
282
|
+
while (i--) {
|
283
|
+
var option = bundleIdFieldInfo.options[i];
|
284
|
+
var nameString = option[0];
|
285
|
+
var formValue = option[1];
|
286
|
+
var nameAndBundle = nameString.split("-").map(function(s) { return s.trim() });
|
287
|
+
if (toTitle) {
|
288
|
+
bundleIdMap[nameAndBundle[1]] = nameAndBundle[0];
|
289
|
+
}
|
290
|
+
else {
|
291
|
+
bundleIdMap[nameAndBundle[1]] = formValue;
|
292
|
+
}
|
293
|
+
}
|
294
|
+
|
295
|
+
return bundleIdMap;
|
296
|
+
},
|
297
|
+
searchITCApps: function(page, searchParams) {
|
298
|
+
var formOptions = {};
|
299
|
+
if (searchParams.title) {
|
300
|
+
formOptions['.search-param-value-name input'] = searchParams.title;
|
301
|
+
}
|
302
|
+
if (searchParams.apple_id) {
|
303
|
+
formOptions['.search-param-value-appleId input'] = searchParams.apple_id;
|
304
|
+
}
|
305
|
+
if (searchParams.sku) {
|
306
|
+
formOptions['.search-param-value-sku input'] = searchParams.sku;
|
307
|
+
}
|
308
|
+
Shortcuts.safeFillSelectors(page, 'form[name="mainForm"]', formOptions, false);
|
309
|
+
page.click('.searchfield input');
|
310
|
+
},
|
311
|
+
addStepToWaitForListOfRecentApps: function(applebot, callback) {
|
312
|
+
applebot.step("Wait for the list of recent apps", 'waitForResource', 'btn-blue-add-new-app.png', function() {
|
313
|
+
callback();
|
314
|
+
});
|
315
|
+
},
|
316
|
+
addStepsToSearchAndClickAppTitle: function(applebot, page, title, callback) {
|
317
|
+
applebot.action("Fill the apps search box", function() {
|
318
|
+
applebot.shortcuts.searchITCApps(page, {title: title});
|
319
|
+
});
|
320
|
+
|
321
|
+
applebot.action('Find the app link and click it', function() {
|
322
|
+
var hrefSelector = applebot.shortcuts.findHrefSelector(title, page, undefined, { fuzzy: true});
|
323
|
+
page.click(hrefSelector);
|
324
|
+
});
|
325
|
+
|
326
|
+
applebot.shortcuts.addStepToWaitForListOfRecentApps(applebot, function() {
|
327
|
+
applebot.action("Fill the apps search box");
|
328
|
+
});
|
329
|
+
|
330
|
+
applebot.step("Wait for the search results", 'waitForText', 'Search Results', function() {
|
331
|
+
applebot.action('Find the app link and click it');
|
332
|
+
});
|
333
|
+
|
334
|
+
applebot.step("Wait for the app info screen", 'waitForText', 'App Information', function() {
|
335
|
+
callback();
|
336
|
+
});
|
337
|
+
}
|
338
|
+
}
|
339
|
+
|
340
|
+
function AppleBot(options) {
|
341
|
+
var _username, _password;
|
342
|
+
var applebot = this;
|
343
|
+
var _currentPage;
|
344
|
+
var _actions = {};
|
345
|
+
var _steps = [];
|
346
|
+
|
347
|
+
if (!isUndefined(options.username)) {
|
348
|
+
_username = options.username;
|
349
|
+
}
|
350
|
+
if (!isUndefined(options.password)) {
|
351
|
+
_password = options.password;
|
352
|
+
}
|
353
|
+
if (!isUndefined(options.output_format)) {
|
354
|
+
$outputFormat = options.output_format;
|
355
|
+
}
|
356
|
+
|
357
|
+
var logStepStart = function(stepName) {
|
358
|
+
console.formatLog("- " + stepName, {event: "step_start", name: stepName});
|
359
|
+
};
|
360
|
+
var logStepComplete = function(stepName) {
|
361
|
+
console.formatLog("✔ " + stepName, {event: "step_complete", name: stepName});
|
362
|
+
};
|
363
|
+
var logActionStart = function(actionName) {
|
364
|
+
console.formatLog("- " + actionName, {event: "action_start", name: actionName});
|
365
|
+
};
|
366
|
+
var logActionComplete = function(actionName) {
|
367
|
+
console.formatLog("✔ " + actionName, {event: "action_complete", name: actionName});
|
368
|
+
};
|
369
|
+
var logStepFail = function(stepName) {
|
370
|
+
console.formatLog("! " + actionName, {event: "step_fail", name: stepName});
|
371
|
+
};
|
372
|
+
var logPageError = function(error) {
|
373
|
+
console.formatLog("X - " + errors[i], {event: "page_error", error: error});
|
374
|
+
};
|
375
|
+
|
376
|
+
this.result = function(result) {
|
377
|
+
jsonLog({'result': result});
|
378
|
+
};
|
379
|
+
|
380
|
+
var createPage = function() {
|
381
|
+
var page = Casper.create({
|
382
|
+
verbose: true,
|
383
|
+
logLevel: 'warning',
|
384
|
+
//logLevel: 'info',
|
385
|
+
waitTimeout: 10000
|
386
|
+
});
|
387
|
+
page.echo = function(string, style) {
|
388
|
+
console.formatLog(string, {message: string, event: "echo"});
|
389
|
+
return page;
|
390
|
+
};
|
391
|
+
|
392
|
+
phantom.cookiesEnabled = true;
|
393
|
+
page.userAgent('Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.517.41 Safari/534.7');
|
394
|
+
return page;
|
395
|
+
};
|
396
|
+
|
397
|
+
// Works for both ADC and ITC
|
398
|
+
this.openPage = function(url, callback) {
|
399
|
+
var page = createPage();
|
400
|
+
|
401
|
+
logStepStart("Wait for the login URL");
|
402
|
+
page.start(url, function() {
|
403
|
+
logStepComplete("Wait for the login URL");
|
404
|
+
logActionStart("Fill the login form");
|
405
|
+
try {
|
406
|
+
Shortcuts.safeFill(page, 'form', {
|
407
|
+
'theAccountName': _username,
|
408
|
+
'theAccountPW': _password,
|
409
|
+
'appleId': _username,
|
410
|
+
'accountPassword': _password
|
411
|
+
}, true);
|
412
|
+
}
|
413
|
+
catch (ex) {
|
414
|
+
dieFromException(page, ex);
|
415
|
+
}
|
416
|
+
});
|
417
|
+
page.then(function(response) {
|
418
|
+
logActionComplete("Fill the login form");
|
419
|
+
|
420
|
+
logStepStart("Wait for the login to process");
|
421
|
+
if (page.exists('.dserror')) {
|
422
|
+
die(page, "Login error: " + page.getHTML('.dserror'));
|
423
|
+
}
|
424
|
+
logStepComplete("Wait for the login to process");
|
425
|
+
logActionStart("Open the action URL (" + url + ")");
|
426
|
+
});
|
427
|
+
page.thenOpen(url, function() {
|
428
|
+
logActionComplete("Wait for the action URL to load");
|
429
|
+
callback(page);
|
430
|
+
});
|
431
|
+
|
432
|
+
page.run();
|
433
|
+
|
434
|
+
this.setPage(page);
|
435
|
+
};
|
436
|
+
|
437
|
+
this.shortcuts = Shortcuts;
|
438
|
+
|
439
|
+
var addAction = function(actionName, actionImpl) {
|
440
|
+
_actions[actionName] = actionImpl;
|
441
|
+
};
|
442
|
+
|
443
|
+
var runAction = function(actionName) {
|
444
|
+
var action = _actions[actionName];
|
445
|
+
if (action) {
|
446
|
+
logActionStart(actionName);
|
447
|
+
try {
|
448
|
+
action();
|
449
|
+
logActionComplete(actionName);
|
450
|
+
} catch (ex) {
|
451
|
+
dieFromException(_currentPage, ex, {action: actionName});
|
452
|
+
}
|
453
|
+
}
|
454
|
+
else {
|
455
|
+
die(_currentPage, "Could not find registered action for '" + actionName + "'");
|
456
|
+
}
|
457
|
+
};
|
458
|
+
|
459
|
+
this.action = function(actionName, actionImpl) {
|
460
|
+
if (isUndefined(actionImpl)) {
|
461
|
+
runAction(actionName);
|
462
|
+
}
|
463
|
+
else {
|
464
|
+
addAction(actionName, actionImpl);
|
465
|
+
}
|
466
|
+
};
|
467
|
+
|
468
|
+
var logPageErrors = function() {
|
469
|
+
_currentPage.debugHTML();
|
470
|
+
|
471
|
+
var errors = _currentPage.evaluate(function() {
|
472
|
+
var errors = [];
|
473
|
+
var errorEls = document.querySelectorAll(".global-message.error li span");
|
474
|
+
for (var i = 0; i < errorEls.length; i++) {
|
475
|
+
errors.push(errorEls[i].innerHTML);
|
476
|
+
}
|
477
|
+
return errors;
|
478
|
+
});
|
479
|
+
for (var i = 0; i < errors.length; i++) {
|
480
|
+
logPageError(errors[i]);
|
481
|
+
}
|
482
|
+
};
|
483
|
+
|
484
|
+
this.step = function(stepName, methodName, methodArg, callback, options) {
|
485
|
+
var _step = {
|
486
|
+
name: stepName,
|
487
|
+
methodName: methodName,
|
488
|
+
methodArg: methodArg,
|
489
|
+
methodDescription: function() {
|
490
|
+
var argDescription = "'" + _step.methodArg + "'";
|
491
|
+
if (isFunction(_step.methodArg)) {
|
492
|
+
argDescription = "[function]";
|
493
|
+
}
|
494
|
+
return _step.name + " ( " + _step.methodName + "(" + argDescription + ")" + " )";
|
495
|
+
},
|
496
|
+
callback: callback,
|
497
|
+
options: options || {}
|
498
|
+
}
|
499
|
+
var method = _currentPage[_step.methodName];
|
500
|
+
var timeout = undefined;
|
501
|
+
if (_step.options.timeout) {
|
502
|
+
timeout = _step.options.timeout;
|
503
|
+
}
|
504
|
+
method.bind(_currentPage)(_step.methodArg, function() {
|
505
|
+
logStepComplete(_step.name);
|
506
|
+
if (isFunction(_step.callback)) {
|
507
|
+
_step.callback();
|
508
|
+
}
|
509
|
+
}, function() {
|
510
|
+
logStepFail(_step.name);
|
511
|
+
var userDieMethod = _step.options.onFail;
|
512
|
+
if (isFunction(_step.options)) {
|
513
|
+
userDieMethod = _step.options;
|
514
|
+
}
|
515
|
+
var shouldDie = true;
|
516
|
+
if (userDieMethod) {
|
517
|
+
shouldDie = (userDieMethod() !== false);
|
518
|
+
}
|
519
|
+
if (shouldDie === true) {
|
520
|
+
logPageErrors();
|
521
|
+
die(_currentPage, "Failed @ " + _step.methodDescription(), {step: _step.name});
|
522
|
+
}
|
523
|
+
}, timeout);
|
524
|
+
}
|
525
|
+
|
526
|
+
this.setPage = function(page) {
|
527
|
+
_currentPage = page;
|
528
|
+
}
|
529
|
+
};
|
530
|
+
|
531
|
+
var getOptionsWithManifest = function() {
|
532
|
+
var casper = require("casper").create();
|
533
|
+
var options = casper.cli.options;
|
534
|
+
|
535
|
+
var manifest = options.manifest;
|
536
|
+
if (manifest) {
|
537
|
+
var f = fs.open(manifest, "r");
|
538
|
+
options = JSON.parse(f.read());
|
539
|
+
}
|
540
|
+
|
541
|
+
return options;
|
542
|
+
}
|
543
|
+
|
544
|
+
var _commandConfigs = null;
|
545
|
+
var CommandConfigs = function() {
|
546
|
+
if (_commandConfigs === null) {
|
547
|
+
var options = getOptionsWithManifest();
|
548
|
+
// no __FILE__ equivalent in phantomjs, unfortunately
|
549
|
+
var applebotRootPath = options.applebot_root_path;
|
550
|
+
var _commandsFilePath = applebotRootPath + fs.separator + 'phantom' + fs.separator + '_commands.json';
|
551
|
+
_commandConfigs = JSON.parse(fs.open(_commandsFilePath, "r").read());
|
552
|
+
}
|
553
|
+
return _commandConfigs;
|
554
|
+
}
|
555
|
+
|
556
|
+
function CommandHandler(fileName) {
|
557
|
+
|
558
|
+
var _fullOptions = CommandConfigs()[fileName];
|
559
|
+
var _options = {};
|
560
|
+
_options.options = {
|
561
|
+
required: [],
|
562
|
+
optional: []
|
563
|
+
};
|
564
|
+
|
565
|
+
var optionToString = function(optionData) {
|
566
|
+
return optionData.key;
|
567
|
+
};
|
568
|
+
var parseOption = function(optionData) {
|
569
|
+
if (optionData.batch) {
|
570
|
+
return optionData.keys;
|
571
|
+
}
|
572
|
+
else {
|
573
|
+
return optionToString(optionData);
|
574
|
+
}
|
575
|
+
};
|
576
|
+
var parseOptions = function(optionList) {
|
577
|
+
var _optionList = [];
|
578
|
+
for (var i = optionList.length - 1; i >= 0; i--) {
|
579
|
+
var parsed = parseOption(optionList[i]);
|
580
|
+
if (parsed instanceof Array) {
|
581
|
+
_optionList = _optionList.concat(parsed);
|
582
|
+
}
|
583
|
+
else {
|
584
|
+
_optionList.push(parsed);
|
585
|
+
}
|
586
|
+
};
|
587
|
+
return _optionList;
|
588
|
+
};
|
589
|
+
|
590
|
+
if (_fullOptions.options.required) {
|
591
|
+
_options.options.required = parseOptions(_fullOptions.options.required);
|
592
|
+
}
|
593
|
+
if (_fullOptions.options.optional) {
|
594
|
+
_options.options.optional = parseOptions(_fullOptions.options.optional);
|
595
|
+
}
|
596
|
+
|
597
|
+
var _description = _options.description;
|
598
|
+
var _requiredCommands = _options.options.required;
|
599
|
+
var _optionalCommands = _options.options.optional;
|
600
|
+
var ARG_OFFSET = 1;
|
601
|
+
|
602
|
+
this.description = function() {
|
603
|
+
var requiredString = _requiredCommands.map(function(c) {
|
604
|
+
return "<" + c + ">";
|
605
|
+
}).join(" ");
|
606
|
+
var optionalString = _optionalCommands.map(function(c) {
|
607
|
+
return "--" + c + "=<" + c + ">";
|
608
|
+
}).join(" ");
|
609
|
+
return _description + ": " + requiredString + " " + optionalString;
|
610
|
+
}
|
611
|
+
|
612
|
+
this.parseArgs = function() {
|
613
|
+
var casper = require("casper").create();
|
614
|
+
|
615
|
+
var givenOptions = {};
|
616
|
+
var requiredValues = casper.cli.args || [];
|
617
|
+
var allButRequired = getOptionsWithManifest();
|
618
|
+
|
619
|
+
for (var i = 0; i < _requiredCommands.length; i++) {
|
620
|
+
var key = _requiredCommands[i];
|
621
|
+
if (allButRequired[key]) {
|
622
|
+
requiredValues.push(allButRequired[key]);
|
623
|
+
}
|
624
|
+
}
|
625
|
+
|
626
|
+
if (requiredValues.length != _requiredCommands.length) {
|
627
|
+
var message = "Incomplete arguments - need to include " + _requiredCommands;
|
628
|
+
throw message;
|
629
|
+
}
|
630
|
+
|
631
|
+
for (var i = 0; i < _requiredCommands.length; i++) {
|
632
|
+
var key = _requiredCommands[i];
|
633
|
+
givenOptions[key] = requiredValues[i];
|
634
|
+
}
|
635
|
+
|
636
|
+
for (var i = 0; i < _optionalCommands.length; i++) {
|
637
|
+
var key = _optionalCommands[i];
|
638
|
+
givenOptions[key] = allButRequired[key];
|
639
|
+
}
|
640
|
+
|
641
|
+
return givenOptions;
|
642
|
+
};
|
643
|
+
|
644
|
+
this.getAuthFromArgs = function() {
|
645
|
+
var options = getOptionsWithManifest();
|
646
|
+
|
647
|
+
return {
|
648
|
+
username: options.username,
|
649
|
+
password: options.password,
|
650
|
+
output_format: options.output_format
|
651
|
+
};
|
652
|
+
}
|
653
|
+
}
|
654
|
+
|
655
|
+
exports.AppleBot = AppleBot;
|
656
|
+
exports.CommandHandler = CommandHandler;
|