UNPKG

7.94 kBJavaScriptView Raw
1'use strict';
2
3/* eslint no-magic-numbers: 1 */
4
5var test = require('tape');
6var isCallable = require('../');
7var hasToStringTag = require('has-tostringtag/shams')();
8var v = require('es-value-fixtures');
9var forEach = require('for-each');
10var inspect = require('object-inspect');
11var typedArrayNames = require('available-typed-arrays')();
12var generators = require('make-generator-function')();
13var arrows = require('make-arrow-function').list();
14var asyncs = require('make-async-function').list();
15var weirdlyCommentedArrowFn;
16try {
17 /* eslint-disable no-new-func */
18 weirdlyCommentedArrowFn = Function('return cl/*/**/=>/**/ass - 1;')();
19 /* eslint-enable no-new-func */
20} catch (e) { /**/ }
21
22var isIE68 = !(0 in [undefined]);
23var isFirefox = typeof window !== 'undefined' && ('netscape' in window) && (/ rv:/).test(navigator.userAgent);
24var fnToStringCoerces;
25try {
26 Function.prototype.toString.call(v.uncoercibleFnObject);
27 fnToStringCoerces = true;
28} catch (e) {
29 fnToStringCoerces = false;
30}
31
32var noop = function () {};
33var classFake = function classFake() { }; // eslint-disable-line func-name-matching
34var returnClass = function () { return ' class '; };
35var return3 = function () { return 3; };
36/* for coverage */
37noop();
38classFake();
39returnClass();
40return3();
41/* end for coverage */
42
43var proxy;
44if (typeof Proxy === 'function') {
45 try {
46 proxy = new Proxy(function () {}, {});
47 // for coverage
48 proxy();
49 String(proxy);
50 } catch (_) {
51 // Older engines throw a `TypeError` when `Function.prototype.toString` is called on a Proxy object.
52 proxy = null;
53 }
54}
55
56var invokeFunction = function invokeFunctionString(str) {
57 var result;
58 try {
59 /* eslint-disable no-new-func */
60 var fn = Function(str);
61 /* eslint-enable no-new-func */
62 result = fn();
63 } catch (e) {}
64 return result;
65};
66
67var classConstructor = invokeFunction('"use strict"; return class Foo {}');
68var hasDetectableClasses = classConstructor && Function.prototype.toString.call(classConstructor) === 'class Foo {}';
69
70var commentedClass = invokeFunction('"use strict"; return class/*kkk*/\n//blah\n Bar\n//blah\n {}');
71var commentedClassOneLine = invokeFunction('"use strict"; return class/**/A{}');
72var classAnonymous = invokeFunction('"use strict"; return class{}');
73var classAnonymousCommentedOneLine = invokeFunction('"use strict"; return class/*/*/{}');
74
75test('not callables', function (t) {
76 t.notOk(isCallable(), 'implicit undefined is not callable');
77
78 forEach(v.nonFunctions.concat([
79 Object(42),
80 Object('foo'),
81 NaN,
82 [],
83 /a/g,
84 new RegExp('a', 'g'),
85 new Date()
86 ]), function (nonFunction) {
87 if (fnToStringCoerces && nonFunction === v.coercibleFnObject) {
88 t.comment('FF 3.6 has a Function toString that coerces its receiver, so this test is skipped');
89 return;
90 }
91 if (nonFunction != null) { // eslint-disable-line eqeqeq
92 if (isFirefox) {
93 // Firefox 3 throws some kind of *object* here instead of a proper error
94 t['throws'](
95 function () { Function.prototype.toString.call(nonFunction); },
96 inspect(nonFunction) + ' can not be used with Function toString'
97 );
98 } else {
99 t['throws'](
100 function () { Function.prototype.toString.call(nonFunction); },
101 TypeError,
102 inspect(nonFunction) + ' can not be used with Function toString'
103 );
104 }
105 }
106 t.equal(isCallable(nonFunction), false, inspect(nonFunction) + ' is not callable');
107 });
108
109 t.test('non-function with function in its [[Prototype]] chain', function (st) {
110 var Foo = function Bar() {};
111 Foo.prototype = noop;
112 st.equal(isCallable(Foo), true, 'sanity check: Foo is callable');
113 st.equal(isCallable(new Foo()), false, 'instance of Foo is not callable');
114 st.end();
115 });
116
117 t.end();
118});
119
120test('@@toStringTag', { skip: !hasToStringTag }, function (t) {
121 var fakeFunction = {
122 toString: function () { return String(return3); },
123 valueOf: return3
124 };
125 fakeFunction[Symbol.toStringTag] = 'Function';
126 t.equal(String(fakeFunction), String(return3));
127 t.equal(Number(fakeFunction), return3());
128 t.notOk(isCallable(fakeFunction), 'fake Function with @@toStringTag "Function" is not callable');
129 t.end();
130});
131
132test('Functions', function (t) {
133 t.ok(isCallable(noop), 'function is callable');
134 t.ok(isCallable(classFake), 'function with name containing "class" is callable');
135 t.ok(isCallable(returnClass), 'function with string " class " is callable');
136 t.ok(isCallable(isCallable), 'isCallable is callable');
137 t.end();
138});
139
140test('Typed Arrays', { skip: typedArrayNames.length === 0 }, function (st) {
141 forEach(typedArrayNames, function (typedArray) {
142 st.ok(isCallable(global[typedArray]), typedArray + ' is callable');
143 });
144 st.end();
145});
146
147test('Generators', { skip: generators.length === 0 }, function (t) {
148 forEach(generators, function (genFn) {
149 t.ok(isCallable(genFn), 'generator function ' + genFn + ' is callable');
150 });
151 t.end();
152});
153
154test('Arrow functions', { skip: arrows.length === 0 }, function (t) {
155 forEach(arrows, function (arrowFn) {
156 t.ok(isCallable(arrowFn), 'arrow function ' + arrowFn + ' is callable');
157 });
158 t.ok(isCallable(weirdlyCommentedArrowFn), 'weirdly commented arrow functions are callable');
159 t.end();
160});
161
162test('"Class" constructors', {
163 skip: !classConstructor || !commentedClass || !commentedClassOneLine || !classAnonymous, todo: !hasDetectableClasses
164}, function (t) {
165 if (!hasDetectableClasses) {
166 t.comment('WARNING: This engine does not support detectable classes');
167 }
168 t.notOk(isCallable(classConstructor), 'class constructors are not callable');
169 t.notOk(isCallable(commentedClass), 'class constructors with comments in the signature are not callable');
170 t.notOk(isCallable(commentedClassOneLine), 'one-line class constructors with comments in the signature are not callable');
171 t.notOk(isCallable(classAnonymous), 'anonymous class constructors are not callable');
172 t.notOk(isCallable(classAnonymousCommentedOneLine), 'anonymous one-line class constructors with comments in the signature are not callable');
173 t.end();
174});
175
176test('`async function`s', { skip: asyncs.length === 0 }, function (t) {
177 forEach(asyncs, function (asyncFn) {
178 t.ok(isCallable(asyncFn), '`async function` ' + asyncFn + ' is callable');
179 });
180 t.end();
181});
182
183test('proxies of functions', { skip: !proxy }, function (t) {
184 t.equal(isCallable(proxy), true, 'proxies of functions are callable');
185 t.end();
186});
187
188test('throwing functions', function (t) {
189 t.plan(1);
190
191 var thrower = function (a) { return a.b; };
192 t.ok(isCallable(thrower), 'a function that throws is callable');
193});
194
195test('DOM', function (t) {
196 /* eslint-env browser */
197
198 t.test('document.all', { skip: typeof document !== 'object' }, function (st) {
199 st.notOk(isCallable(document), 'document is not callable');
200
201 var all = document.all;
202 var isFF3 = !isIE68 && Object.prototype.toString(all) === Object.prototype.toString.call(document.all); // this test is true in IE 6-8 also
203 var expected = false;
204 if (!isFF3) {
205 try {
206 expected = document.all('') == null; // eslint-disable-line eqeqeq
207 } catch (e) { /**/ }
208 }
209 st.equal(isCallable(document.all), expected, 'document.all is ' + (isFF3 ? 'not ' : '') + 'callable');
210
211 st.end();
212 });
213
214 forEach([
215 'HTMLElement',
216 'HTMLAnchorElement'
217 ], function (name) {
218 var constructor = global[name];
219
220 t.test(name, { skip: !constructor }, function (st) {
221 st.match(typeof constructor, /^(?:function|object)$/, name + ' is a function or object');
222
223 var callable = isCallable(constructor);
224 st.equal(typeof callable, 'boolean');
225
226 if (callable) {
227 st.doesNotThrow(
228 function () { Function.prototype.toString.call(constructor); },
229 'anything this library claims is callable should be accepted by Function toString'
230 );
231 } else {
232 st['throws'](
233 function () { Function.prototype.toString.call(constructor); },
234 TypeError,
235 'anything this library claims is not callable should not be accepted by Function toString'
236 );
237 }
238
239 st.end();
240 });
241 });
242
243 t.end();
244});