casperjs 1.0.0.RC1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. data/CHANGELOG.md +289 -1
  2. data/CONTRIBUTING.md +93 -0
  3. data/CONTRIBUTORS.md +53 -0
  4. data/README.md +29 -1
  5. data/batchbin/casperjs.bat +5 -0
  6. data/bin/bootstrap.js +164 -114
  7. data/bin/casperjs +84 -0
  8. data/bin/usage.txt +2 -1
  9. data/casperjs.gemspec +7 -4
  10. data/modules/casper.js +661 -239
  11. data/modules/clientutils.js +240 -54
  12. data/modules/colorizer.js +2 -1
  13. data/modules/events.js +13 -1
  14. data/modules/http.js +70 -0
  15. data/modules/mouse.js +1 -2
  16. data/modules/pagestack.js +166 -0
  17. data/modules/tester.js +1013 -659
  18. data/modules/utils.js +519 -367
  19. data/modules/vendors/coffee-script.js +2 -2
  20. data/modules/xunit.js +56 -24
  21. data/package.json +2 -2
  22. data/rpm/casperjs.spec +203 -0
  23. data/rpm/mkrpm.sh +25 -0
  24. data/rubybin/casperjs +9 -4
  25. data/samples/bbcshots.coffee +11 -10
  26. data/samples/bbcshots.js +16 -15
  27. data/samples/cliplay.js +3 -0
  28. data/samples/customevents.js +3 -0
  29. data/samples/customlogging.coffee +17 -19
  30. data/samples/customlogging.js +26 -27
  31. data/samples/download.js +4 -1
  32. data/samples/dynamic.js +3 -0
  33. data/samples/each.js +3 -0
  34. data/samples/events.js +4 -2
  35. data/samples/extends.js +4 -1
  36. data/samples/googlelinks.coffee +4 -1
  37. data/samples/googlelinks.js +10 -3
  38. data/samples/googlematch.coffee +1 -1
  39. data/samples/googlematch.js +5 -2
  40. data/samples/googlepagination.js +4 -1
  41. data/samples/googletesting.js +3 -0
  42. data/samples/logcolor.js +3 -0
  43. data/samples/metaextract.js +3 -0
  44. data/samples/multirun.js +3 -0
  45. data/samples/screenshot.js +4 -1
  46. data/samples/statushandlers.js +4 -1
  47. data/samples/steptimeout.js +3 -0
  48. data/samples/timeout.js +4 -1
  49. data/samples/translate.coffee +23 -0
  50. data/samples/translate.js +30 -0
  51. data/tests/commands/mytest.js +14 -0
  52. data/tests/commands/script.js +3 -0
  53. data/tests/run.js +82 -37
  54. data/tests/sample_modules/config.json +1 -0
  55. data/tests/sample_modules/csmodule.coffee +1 -0
  56. data/tests/sample_modules/jsmodule.js +1 -0
  57. data/tests/selftest.js +57 -0
  58. data/tests/site/dummy.js +1 -0
  59. data/tests/site/field-array.html +14 -0
  60. data/tests/site/frame1.html +10 -0
  61. data/tests/site/frame2.html +11 -0
  62. data/tests/site/frame3.html +11 -0
  63. data/tests/site/frames.html +12 -0
  64. data/tests/site/global.html +6 -1
  65. data/tests/site/includes/include1.js +6 -0
  66. data/tests/site/includes/include2.js +6 -0
  67. data/tests/site/index.html +3 -0
  68. data/tests/site/popup.html +19 -0
  69. data/tests/site/resources.html +7 -8
  70. data/tests/site/urls.html +14 -0
  71. data/tests/suites/casper/agent.js +3 -1
  72. data/tests/suites/casper/alert.js +14 -0
  73. data/tests/suites/casper/auth.js +24 -0
  74. data/tests/suites/casper/capture.js +12 -12
  75. data/tests/suites/casper/click.js +11 -1
  76. data/tests/suites/casper/confirm.js +24 -16
  77. data/tests/suites/casper/debug.js +10 -0
  78. data/tests/suites/casper/elementattribute.js +3 -1
  79. data/tests/suites/casper/encode.js +6 -2
  80. data/tests/suites/casper/evaluate.js +78 -18
  81. data/tests/suites/casper/events.js +3 -1
  82. data/tests/suites/casper/exists.js +3 -1
  83. data/tests/suites/casper/fetchtext.js +3 -1
  84. data/tests/suites/casper/flow.coffee +1 -1
  85. data/tests/suites/casper/formfill.js +39 -4
  86. data/tests/suites/casper/frames.js +43 -0
  87. data/tests/suites/casper/global.js +9 -3
  88. data/tests/suites/casper/headers.js +41 -0
  89. data/tests/suites/casper/history.js +3 -1
  90. data/tests/suites/casper/hooks.js +3 -1
  91. data/tests/suites/casper/keys.js +15 -0
  92. data/tests/suites/casper/location.js +21 -0
  93. data/tests/suites/casper/logging.js +3 -1
  94. data/tests/suites/casper/mouseevents.js +3 -1
  95. data/tests/suites/casper/onerror.js +3 -1
  96. data/tests/suites/casper/open.js +63 -1
  97. data/tests/suites/casper/popup.js +86 -0
  98. data/tests/suites/casper/prompt.js +11 -15
  99. data/tests/suites/casper/request.js +36 -0
  100. data/tests/suites/casper/resources.coffee +5 -5
  101. data/tests/suites/casper/scripts.js +32 -0
  102. data/tests/suites/casper/start.js +3 -1
  103. data/tests/suites/casper/steps.js +4 -2
  104. data/tests/suites/casper/urls.js +21 -0
  105. data/tests/suites/casper/viewport.js +3 -1
  106. data/tests/suites/casper/visible.js +3 -1
  107. data/tests/suites/casper/wait.js +22 -12
  108. data/tests/suites/casper/xpath.js +3 -1
  109. data/tests/suites/cli.js +3 -1
  110. data/tests/suites/clientutils.js +57 -3
  111. data/tests/suites/coffee.coffee +1 -1
  112. data/tests/suites/fs.js +3 -1
  113. data/tests/suites/http_status.js +19 -3
  114. data/tests/suites/popup.js +33 -0
  115. data/tests/suites/require.js +31 -0
  116. data/tests/suites/tester.js +134 -29
  117. data/tests/suites/utils.js +108 -8
  118. data/tests/suites/xunit.js +37 -6
  119. metadata +49 -15
  120. data/modules/injector.js +0 -93
  121. data/tests/suites/injector.js +0 -64
@@ -30,400 +30,552 @@
30
30
 
31
31
  /*global CasperError console exports phantom require*/
32
32
 
33
- (function(exports) {
34
- "use strict";
35
-
36
- /**
37
- * Provides a better typeof operator equivalent, able to retrieve the array
38
- * type.
39
- *
40
- * @param mixed input
41
- * @return String
42
- * @see http://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/
43
- */
44
- function betterTypeOf(input) {
33
+ /**
34
+ * Provides a better typeof operator equivalent, able to retrieve the array
35
+ * type.
36
+ *
37
+ * CAVEAT: this function does not necessarilly map to classical js "type" names,
38
+ * notably a `null` will map to "null" instead of "object".
39
+ *
40
+ * @param mixed input
41
+ * @return String
42
+ * @see http://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/
43
+ */
44
+ function betterTypeOf(input) {
45
+ "use strict";
46
+ switch (input) {
47
+ case undefined:
48
+ return 'undefined';
49
+ case null:
50
+ return 'null';
51
+ default:
45
52
  try {
46
53
  return Object.prototype.toString.call(input).match(/^\[object\s(.*)\]$/)[1].toLowerCase();
47
54
  } catch (e) {
48
55
  return typeof input;
49
56
  }
50
57
  }
51
- exports.betterTypeOf = betterTypeOf;
52
-
53
- /**
54
- * Dumps a JSON representation of passed value to the console. Used for
55
- * debugging purpose only.
56
- *
57
- * @param Mixed value
58
- */
59
- function dump(value) {
60
- console.log(serialize(value, 4));
61
- }
62
- exports.dump = dump;
63
-
64
- /**
65
- * Returns the file extension in lower case.
66
- *
67
- * @param String file File path
68
- * @return string
69
- */
70
- function fileExt(file) {
71
- try {
72
- return file.split('.').pop().toLowerCase().trim();
73
- } catch(e) {
74
- return '';
75
- }
58
+ }
59
+ exports.betterTypeOf = betterTypeOf;
60
+
61
+ /**
62
+ * Cleans a passed URL if it lacks a slash at the end when a sole domain is used.
63
+ *
64
+ * @param String url An HTTP URL
65
+ * @return String
66
+ */
67
+ function cleanUrl(url) {
68
+ "use strict";
69
+ var parts = /(https?):\/\/(.*)/i.exec(url);
70
+ if (!parts) {
71
+ return url;
76
72
  }
77
- exports.fileExt = fileExt;
78
-
79
- /**
80
- * Takes a string and append blanks until the pad value is reached.
81
- *
82
- * @param String text
83
- * @param Number pad Pad value (optional; default: 80)
84
- * @return String
85
- */
86
- function fillBlanks(text, pad) {
87
- pad = pad || 80;
88
- if (text.length < pad) {
89
- text += new Array(pad - text.length + 1).join(' ');
90
- }
91
- return text;
73
+ var protocol = parts[1];
74
+ var subparts = parts[2].split('/');
75
+ if (subparts.length === 1) {
76
+ return format("%s://%s/", protocol, subparts[0]);
92
77
  }
93
- exports.fillBlanks = fillBlanks;
94
-
95
- /**
96
- * Formats a string with passed parameters. Ported from nodejs `util.format()`.
97
- *
98
- * @return String
99
- */
100
- function format(f) {
101
- var i = 1;
102
- var args = arguments;
103
- var len = args.length;
104
- var str = String(f).replace(/%[sdj%]/g, function _replace(x) {
105
- if (i >= len) return x;
106
- switch (x) {
107
- case '%s':
108
- return String(args[i++]);
109
- case '%d':
110
- return Number(args[i++]);
111
- case '%j':
112
- return JSON.stringify(args[i++]);
113
- case '%%':
114
- return '%';
115
- default:
116
- return x;
117
- }
118
- });
119
- for (var x = args[i]; i < len; x = args[++i]) {
120
- if (x === null || typeof x !== 'object') {
121
- str += ' ' + x;
122
- } else {
123
- str += '[obj]';
124
- }
125
- }
126
- return str;
78
+ return url;
79
+ }
80
+ exports.cleanUrl = cleanUrl;
81
+
82
+ /**
83
+ * Clones an object.
84
+ *
85
+ * @param Mixed o
86
+ * @return Mixed
87
+ */
88
+ function clone(o) {
89
+ "use strict";
90
+ return JSON.parse(JSON.stringify(o));
91
+ }
92
+ exports.clone = clone;
93
+
94
+ /**
95
+ * Dumps a JSON representation of passed value to the console. Used for
96
+ * debugging purpose only.
97
+ *
98
+ * @param Mixed value
99
+ */
100
+ function dump(value) {
101
+ "use strict";
102
+ console.log(serialize(value, 4));
103
+ }
104
+ exports.dump = dump;
105
+
106
+ /**
107
+ * Tests equality between the two passed arguments.
108
+ *
109
+ * @param Mixed v1
110
+ * @param Mixed v2
111
+ * @param Boolean
112
+ */
113
+ function equals(v1, v2) {
114
+ "use strict";
115
+ if (isFunction(v1)) {
116
+ return v1.toString() === v2.toString();
127
117
  }
128
- exports.format = format;
129
-
130
- /**
131
- * Retrieves the value of an Object foreign property using a dot-separated
132
- * path string.
133
- *
134
- * Beware, this function doesn't handle object key names containing a dot.
135
- *
136
- * @param Object obj The source object
137
- * @param String path Dot separated path, eg. "x.y.z"
138
- */
139
- function getPropertyPath(obj, path) {
140
- if (!isObject(obj) || !isString(path)) {
141
- return undefined;
118
+ if (v1 instanceof Object) {
119
+ if (Object.keys(v1).length !== Object.keys(v2).length) {
120
+ return false;
142
121
  }
143
- var value = obj;
144
- path.split('.').forEach(function(property) {
145
- if (typeof value === "object" && property in value) {
146
- value = value[property];
147
- } else {
148
- value = undefined;
149
- return;
150
- }
151
- });
152
- return value;
153
- }
154
- exports.getPropertyPath = getPropertyPath;
155
-
156
- /**
157
- * Inherit the prototype methods from one constructor into another.
158
- *
159
- * @param {function} ctor Constructor function which needs to inherit the
160
- * prototype.
161
- * @param {function} superCtor Constructor function to inherit prototype from.
162
- */
163
- function inherits(ctor, superCtor) {
164
- ctor.super_ = ctor.__super__ = superCtor;
165
- ctor.prototype = Object.create(superCtor.prototype, {
166
- constructor: {
167
- value: ctor,
168
- enumerable: false,
169
- writable: true,
170
- configurable: true
122
+ for (var k in v1) {
123
+ if (!equals(v1[k], v2[k])) {
124
+ return false;
171
125
  }
172
- });
173
- }
174
- exports.inherits = inherits;
175
-
176
- /**
177
- * Checks if value is a javascript Array
178
- *
179
- * @param mixed value
180
- * @return Boolean
181
- */
182
- function isArray(value) {
183
- return Array.isArray(value) || isType(value, "array");
184
- }
185
- exports.isArray = isArray;
186
-
187
- /**
188
- * Checks if passed argument is an instance of Capser object.
189
- *
190
- * @param mixed value
191
- * @return Boolean
192
- */
193
- function isCasperObject(value) {
194
- return value instanceof require('casper').Casper;
195
- }
196
- exports.isCasperObject = isCasperObject;
197
-
198
- /**
199
- * Checks if value is a phantomjs clipRect-compatible object
200
- *
201
- * @param mixed value
202
- * @return Boolean
203
- */
204
- function isClipRect(value) {
205
- return isType(value, "cliprect") || (
206
- isObject(value) &&
207
- isNumber(value.top) && isNumber(value.left) &&
208
- isNumber(value.width) && isNumber(value.height)
209
- );
210
- }
211
- exports.isClipRect = isClipRect;
212
-
213
- /**
214
- * Checks if value is a javascript Function
215
- *
216
- * @param mixed value
217
- * @return Boolean
218
- */
219
- function isFunction(value) {
220
- return isType(value, "function");
221
- }
222
- exports.isFunction = isFunction;
223
-
224
- /**
225
- * Checks if a file is apparently javascript compatible (.js or .coffee).
226
- *
227
- * @param String file Path to the file to test
228
- * @return Boolean
229
- */
230
- function isJsFile(file) {
231
- var ext = fileExt(file);
232
- return isString(ext, "string") && ['js', 'coffee'].indexOf(ext) !== -1;
126
+ }
127
+ return true;
233
128
  }
234
- exports.isJsFile = isJsFile;
235
-
236
- /**
237
- * Checks if the provided value is null
238
- *
239
- * @return Boolean
240
- */
241
- function isNull(value) {
242
- return isType(value, "null");
129
+ return v1 === v2;
130
+ }
131
+ exports.equals = equals;
132
+
133
+ /**
134
+ * Returns the file extension in lower case.
135
+ *
136
+ * @param String file File path
137
+ * @return string
138
+ */
139
+ function fileExt(file) {
140
+ "use strict";
141
+ try {
142
+ return file.split('.').pop().toLowerCase().trim();
143
+ } catch(e) {
144
+ return '';
243
145
  }
244
- exports.isNull = isNull;
245
-
246
- /**
247
- * Checks if value is a javascript Number
248
- *
249
- * @param mixed value
250
- * @return Boolean
251
- */
252
- function isNumber(value) {
253
- return isType(value, "number");
146
+ }
147
+ exports.fileExt = fileExt;
148
+
149
+ /**
150
+ * Takes a string and append blanks until the pad value is reached.
151
+ *
152
+ * @param String text
153
+ * @param Number pad Pad value (optional; default: 80)
154
+ * @return String
155
+ */
156
+ function fillBlanks(text, pad) {
157
+ "use strict";
158
+ pad = pad || 80;
159
+ if (text.length < pad) {
160
+ text += new Array(pad - text.length + 1).join(' ');
254
161
  }
255
- exports.isNumber = isNumber;
256
-
257
- /**
258
- * Checks if value is a javascript Object
259
- *
260
- * @param mixed value
261
- * @return Boolean
262
- */
263
- function isObject(value) {
264
- var objectTypes = ["array", "object", "qtruntimeobject"];
265
- return objectTypes.indexOf(betterTypeOf(value)) >= 0;
162
+ return text;
163
+ }
164
+ exports.fillBlanks = fillBlanks;
165
+
166
+ /**
167
+ * Formats a string with passed parameters. Ported from nodejs `util.format()`.
168
+ *
169
+ * @return String
170
+ */
171
+ function format(f) {
172
+ "use strict";
173
+ var i = 1;
174
+ var args = arguments;
175
+ var len = args.length;
176
+ var str = String(f).replace(/%[sdj%]/g, function _replace(x) {
177
+ if (i >= len) return x;
178
+ switch (x) {
179
+ case '%s':
180
+ return String(args[i++]);
181
+ case '%d':
182
+ return Number(args[i++]);
183
+ case '%j':
184
+ return JSON.stringify(args[i++]);
185
+ case '%%':
186
+ return '%';
187
+ default:
188
+ return x;
189
+ }
190
+ });
191
+ for (var x = args[i]; i < len; x = args[++i]) {
192
+ if (x === null || typeof x !== 'object') {
193
+ str += ' ' + x;
194
+ } else {
195
+ str += '[obj]';
196
+ }
266
197
  }
267
- exports.isObject = isObject;
268
-
269
- /**
270
- * Checks if value is a javascript String
271
- *
272
- * @param mixed value
273
- * @return Boolean
274
- */
275
- function isString(value) {
276
- return isType(value, "string");
198
+ return str;
199
+ }
200
+ exports.format = format;
201
+
202
+ /**
203
+ * Retrieves the value of an Object foreign property using a dot-separated
204
+ * path string.
205
+ *
206
+ * Beware, this function doesn't handle object key names containing a dot.
207
+ *
208
+ * @param Object obj The source object
209
+ * @param String path Dot separated path, eg. "x.y.z"
210
+ */
211
+ function getPropertyPath(obj, path) {
212
+ "use strict";
213
+ if (!isObject(obj) || !isString(path)) {
214
+ return undefined;
277
215
  }
278
- exports.isString = isString;
279
-
280
- /**
281
- * Shorthands for checking if a value is of the given type. Can check for
282
- * arrays.
283
- *
284
- * @param mixed what The value to check
285
- * @param String typeName The type name ("string", "number", "function", etc.)
286
- * @return Boolean
287
- */
288
- function isType(what, typeName) {
289
- if (typeof typeName !== "string" || !typeName) {
290
- throw new CasperError("You must pass isType() a typeName string");
216
+ var value = obj;
217
+ path.split('.').forEach(function(property) {
218
+ if (typeof value === "object" && property in value) {
219
+ value = value[property];
220
+ } else {
221
+ value = undefined;
291
222
  }
292
- return betterTypeOf(what).toLowerCase() === typeName.toLowerCase();
293
- }
294
- exports.isType = isType;
295
-
296
- /**
297
- * Checks if the provided value is undefined
298
- *
299
- * @return Boolean
300
- */
301
- function isUndefined(value) {
302
- return isType(value, "undefined");
223
+ });
224
+ return value;
225
+ }
226
+ exports.getPropertyPath = getPropertyPath;
227
+
228
+ /**
229
+ * Inherit the prototype methods from one constructor into another.
230
+ *
231
+ * @param {function} ctor Constructor function which needs to inherit the
232
+ * prototype.
233
+ * @param {function} superCtor Constructor function to inherit prototype from.
234
+ */
235
+ function inherits(ctor, superCtor) {
236
+ "use strict";
237
+ ctor.super_ = ctor.__super__ = superCtor;
238
+ ctor.prototype = Object.create(superCtor.prototype, {
239
+ constructor: {
240
+ value: ctor,
241
+ enumerable: false,
242
+ writable: true,
243
+ configurable: true
244
+ }
245
+ });
246
+ }
247
+ exports.inherits = inherits;
248
+
249
+ /**
250
+ * Checks if value is a javascript Array
251
+ *
252
+ * @param mixed value
253
+ * @return Boolean
254
+ */
255
+ function isArray(value) {
256
+ "use strict";
257
+ return Array.isArray(value) || isType(value, "array");
258
+ }
259
+ exports.isArray = isArray;
260
+
261
+ /**
262
+ * Checks if passed argument is an instance of Capser object.
263
+ *
264
+ * @param mixed value
265
+ * @return Boolean
266
+ */
267
+ function isCasperObject(value) {
268
+ "use strict";
269
+ return value instanceof require('casper').Casper;
270
+ }
271
+ exports.isCasperObject = isCasperObject;
272
+
273
+ /**
274
+ * Checks if value is a phantomjs clipRect-compatible object
275
+ *
276
+ * @param mixed value
277
+ * @return Boolean
278
+ */
279
+ function isClipRect(value) {
280
+ "use strict";
281
+ return isType(value, "cliprect") || (
282
+ isObject(value) &&
283
+ isNumber(value.top) && isNumber(value.left) &&
284
+ isNumber(value.width) && isNumber(value.height)
285
+ );
286
+ }
287
+ exports.isClipRect = isClipRect;
288
+
289
+ /**
290
+ * Checks that the subject is falsy.
291
+ *
292
+ * @param Mixed subject Test subject
293
+ * @return Boolean
294
+ */
295
+ function isFalsy(subject) {
296
+ "use strict";
297
+ /*jshint eqeqeq:false*/
298
+ return !subject;
299
+ }
300
+ exports.isFalsy = isFalsy;
301
+ /**
302
+ * Checks if value is a javascript Function
303
+ *
304
+ * @param mixed value
305
+ * @return Boolean
306
+ */
307
+ function isFunction(value) {
308
+ "use strict";
309
+ return isType(value, "function");
310
+ }
311
+ exports.isFunction = isFunction;
312
+
313
+ /**
314
+ * Checks if passed resource involves an HTTP url.
315
+ *
316
+ * @param Object resource The PhantomJS HTTP resource object
317
+ * @return Boolean
318
+ */
319
+ function isHTTPResource(resource) {
320
+ "use strict";
321
+ return isObject(resource) && /^http/i.test(resource.url);
322
+ }
323
+ exports.isHTTPResource = isHTTPResource;
324
+
325
+ /**
326
+ * Checks if a file is apparently javascript compatible (.js or .coffee).
327
+ *
328
+ * @param String file Path to the file to test
329
+ * @return Boolean
330
+ */
331
+ function isJsFile(file) {
332
+ "use strict";
333
+ var ext = fileExt(file);
334
+ return isString(ext, "string") && ['js', 'coffee'].indexOf(ext) !== -1;
335
+ }
336
+ exports.isJsFile = isJsFile;
337
+
338
+ /**
339
+ * Checks if the provided value is null
340
+ *
341
+ * @return Boolean
342
+ */
343
+ function isNull(value) {
344
+ "use strict";
345
+ return isType(value, "null");
346
+ }
347
+ exports.isNull = isNull;
348
+
349
+ /**
350
+ * Checks if value is a javascript Number
351
+ *
352
+ * @param mixed value
353
+ * @return Boolean
354
+ */
355
+ function isNumber(value) {
356
+ "use strict";
357
+ return isType(value, "number");
358
+ }
359
+ exports.isNumber = isNumber;
360
+
361
+ /**
362
+ * Checks if value is a javascript Object
363
+ *
364
+ * @param mixed value
365
+ * @return Boolean
366
+ */
367
+ function isObject(value) {
368
+ "use strict";
369
+ var objectTypes = ["array", "object", "qtruntimeobject"];
370
+ return objectTypes.indexOf(betterTypeOf(value)) >= 0;
371
+ }
372
+ exports.isObject = isObject;
373
+
374
+ /**
375
+ * Checks if value is a javascript String
376
+ *
377
+ * @param mixed value
378
+ * @return Boolean
379
+ */
380
+ function isString(value) {
381
+ "use strict";
382
+ return isType(value, "string");
383
+ }
384
+ exports.isString = isString;
385
+
386
+ /**
387
+ * Checks that the subject is truthy.
388
+ *
389
+ * @param Mixed subject Test subject
390
+ * @return Boolean
391
+ */
392
+ function isTruthy(subject) {
393
+ "use strict";
394
+ /*jshint eqeqeq:false*/
395
+ return !!subject;
396
+ }
397
+ exports.isTruthy = isTruthy;
398
+
399
+ /**
400
+ * Shorthands for checking if a value is of the given type. Can check for
401
+ * arrays.
402
+ *
403
+ * @param mixed what The value to check
404
+ * @param String typeName The type name ("string", "number", "function", etc.)
405
+ * @return Boolean
406
+ */
407
+ function isType(what, typeName) {
408
+ "use strict";
409
+ if (typeof typeName !== "string" || !typeName) {
410
+ throw new CasperError("You must pass isType() a typeName string");
303
411
  }
304
- exports.isUndefined = isUndefined;
305
-
306
- /**
307
- * Checks if value is a valid selector Object.
308
- *
309
- * @param mixed value
310
- * @return Boolean
311
- */
312
- function isValidSelector(value) {
313
- if (isString(value)) {
314
- try {
315
- // phantomjs env has a working document object, let's use it
316
- document.querySelector(value);
317
- } catch(e) {
318
- if ('name' in e && e.name === 'SYNTAX_ERR') {
319
- return false;
320
- }
321
- }
322
- return true;
323
- } else if (isObject(value)) {
324
- if (!value.hasOwnProperty('type')) {
325
- return false;
326
- }
327
- if (!value.hasOwnProperty('path')) {
328
- return false;
329
- }
330
- if (['css', 'xpath'].indexOf(value.type) === -1) {
412
+ return betterTypeOf(what).toLowerCase() === typeName.toLowerCase();
413
+ }
414
+ exports.isType = isType;
415
+
416
+ /**
417
+ * Checks if the provided value is undefined
418
+ *
419
+ * @return Boolean
420
+ */
421
+ function isUndefined(value) {
422
+ "use strict";
423
+ return isType(value, "undefined");
424
+ }
425
+ exports.isUndefined = isUndefined;
426
+
427
+ /**
428
+ * Checks if value is a valid selector Object.
429
+ *
430
+ * @param mixed value
431
+ * @return Boolean
432
+ */
433
+ function isValidSelector(value) {
434
+ "use strict";
435
+ if (isString(value)) {
436
+ try {
437
+ // phantomjs env has a working document object, let's use it
438
+ document.querySelector(value);
439
+ } catch(e) {
440
+ if ('name' in e && e.name === 'SYNTAX_ERR') {
331
441
  return false;
332
442
  }
333
- return true;
334
443
  }
335
- return false;
336
- }
337
- exports.isValidSelector = isValidSelector;
338
-
339
- /**
340
- * Checks if the provided var is a WebPage instance
341
- *
342
- * @param mixed what
343
- * @return Boolean
344
- */
345
- function isWebPage(what) {
346
- return betterTypeOf(what) === "qtruntimeobject" && what.objectName === 'WebPage';
347
- }
348
- exports.isWebPage = isWebPage;
349
-
350
- /**
351
- * Object recursive merging utility.
352
- *
353
- * @param Object origin the origin object
354
- * @param Object add the object to merge data into origin
355
- * @return Object
356
- */
357
- function mergeObjects(origin, add) {
358
- for (var p in add) {
359
- try {
360
- if (add[p].constructor === Object) {
361
- origin[p] = mergeObjects(origin[p], add[p]);
362
- } else {
363
- origin[p] = add[p];
364
- }
365
- } catch(e) {
366
- origin[p] = add[p];
367
- }
444
+ return true;
445
+ } else if (isObject(value)) {
446
+ if (!value.hasOwnProperty('type')) {
447
+ return false;
368
448
  }
369
- return origin;
449
+ if (!value.hasOwnProperty('path')) {
450
+ return false;
451
+ }
452
+ if (['css', 'xpath'].indexOf(value.type) === -1) {
453
+ return false;
454
+ }
455
+ return true;
370
456
  }
371
- exports.mergeObjects = mergeObjects;
372
-
373
- /**
374
- * Creates an (SG|X)ML node element.
375
- *
376
- * @param String name The node name
377
- * @param Object attributes Optional attributes
378
- * @return HTMLElement
379
- */
380
- function node(name, attributes) {
381
- var _node = document.createElement(name);
382
- for (var attrName in attributes) {
383
- var value = attributes[attrName];
384
- if (attributes.hasOwnProperty(attrName) && isString(attrName)) {
385
- _node.setAttribute(attrName, value);
457
+ return false;
458
+ }
459
+ exports.isValidSelector = isValidSelector;
460
+
461
+ /**
462
+ * Checks if the provided var is a WebPage instance
463
+ *
464
+ * @param mixed what
465
+ * @return Boolean
466
+ */
467
+ function isWebPage(what) {
468
+ "use strict";
469
+ return betterTypeOf(what) === "qtruntimeobject" && what.objectName === 'WebPage';
470
+ }
471
+ exports.isWebPage = isWebPage;
472
+
473
+ /**
474
+ * Object recursive merging utility.
475
+ *
476
+ * @param Object origin the origin object
477
+ * @param Object add the object to merge data into origin
478
+ * @return Object
479
+ */
480
+ function mergeObjects(origin, add) {
481
+ "use strict";
482
+ for (var p in add) {
483
+ try {
484
+ if (add[p].constructor === Object) {
485
+ origin[p] = mergeObjects(origin[p], add[p]);
486
+ } else {
487
+ origin[p] = add[p];
386
488
  }
489
+ } catch(e) {
490
+ origin[p] = add[p];
387
491
  }
388
- return _node;
389
492
  }
390
- exports.node = node;
391
-
392
- /**
393
- * Serializes a value using JSON.
394
- *
395
- * @param Mixed value
396
- * @return String
397
- */
398
- function serialize(value, indent) {
399
- if (isArray(value)) {
400
- value = value.map(function _map(prop) {
401
- return isFunction(prop) ? prop.toString().replace(/\s{2,}/, '') : prop;
402
- });
493
+ return origin;
494
+ }
495
+ exports.mergeObjects = mergeObjects;
496
+
497
+ /**
498
+ * Converts milliseconds to seconds and rounds the results to 3 digits accuracy.
499
+ *
500
+ * @param Number milliseconds
501
+ * @return Number seconds
502
+ */
503
+ function ms2seconds(milliseconds) {
504
+ "use strict";
505
+ return Math.round(milliseconds / 1000 * 1000) / 1000;
506
+ }
507
+ exports.ms2seconds = ms2seconds;
508
+
509
+ /**
510
+ * Creates an (SG|X)ML node element.
511
+ *
512
+ * @param String name The node name
513
+ * @param Object attributes Optional attributes
514
+ * @return HTMLElement
515
+ */
516
+ function node(name, attributes) {
517
+ "use strict";
518
+ var _node = document.createElement(name);
519
+ for (var attrName in attributes) {
520
+ var value = attributes[attrName];
521
+ if (attributes.hasOwnProperty(attrName) && isString(attrName)) {
522
+ _node.setAttribute(attrName, value);
403
523
  }
404
- return JSON.stringify(value, null, indent);
405
524
  }
406
- exports.serialize = serialize;
407
-
408
- /**
409
- * Returns unique values from an array.
410
- *
411
- * Note: ugly code is ugly, but efficient: http://jsperf.com/array-unique2/8
412
- *
413
- * @param Array array
414
- * @return Array
415
- */
416
- function unique(array) {
417
- var o = {},
418
- r = [];
419
- for (var i = 0, len = array.length; i !== len; i++) {
420
- var d = array[i];
421
- if (o[d] !== 1) {
422
- o[d] = 1;
423
- r[r.length] = d;
424
- }
525
+ return _node;
526
+ }
527
+ exports.node = node;
528
+
529
+ /**
530
+ * Maps an object to an array made from its values.
531
+ *
532
+ * @param Object obj
533
+ * @return Array
534
+ */
535
+ function objectValues(obj) {
536
+ "use strict";
537
+ return Object.keys(obj).map(function(arg) {
538
+ return obj[arg];
539
+ });
540
+ }
541
+ exports.objectValues = objectValues;
542
+
543
+ /**
544
+ * Serializes a value using JSON.
545
+ *
546
+ * @param Mixed value
547
+ * @return String
548
+ */
549
+ function serialize(value, indent) {
550
+ "use strict";
551
+ if (isArray(value)) {
552
+ value = value.map(function _map(prop) {
553
+ return isFunction(prop) ? prop.toString().replace(/\s{2,}/, '') : prop;
554
+ });
555
+ }
556
+ return JSON.stringify(value, null, indent);
557
+ }
558
+ exports.serialize = serialize;
559
+
560
+ /**
561
+ * Returns unique values from an array.
562
+ *
563
+ * Note: ugly code is ugly, but efficient: http://jsperf.com/array-unique2/8
564
+ *
565
+ * @param Array array
566
+ * @return Array
567
+ */
568
+ function unique(array) {
569
+ "use strict";
570
+ var o = {},
571
+ r = [];
572
+ for (var i = 0, len = array.length; i !== len; i++) {
573
+ var d = array[i];
574
+ if (o[d] !== 1) {
575
+ o[d] = 1;
576
+ r[r.length] = d;
425
577
  }
426
- return r;
427
578
  }
428
- exports.unique = unique;
429
- })(exports);
579
+ return r;
580
+ }
581
+ exports.unique = unique;