index.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. // jscs:disable requireUseStrict
  2. var test = require('tape');
  3. var functionBind = require('../implementation');
  4. var getCurrentContext = function () { return this; };
  5. test('functionBind is a function', function (t) {
  6. t.equal(typeof functionBind, 'function');
  7. t.end();
  8. });
  9. test('non-functions', function (t) {
  10. var nonFunctions = [true, false, [], {}, 42, 'foo', NaN, /a/g];
  11. t.plan(nonFunctions.length);
  12. for (var i = 0; i < nonFunctions.length; ++i) {
  13. try { functionBind.call(nonFunctions[i]); } catch (ex) {
  14. t.ok(ex instanceof TypeError, 'throws when given ' + String(nonFunctions[i]));
  15. }
  16. }
  17. t.end();
  18. });
  19. test('without a context', function (t) {
  20. t.test('binds properly', function (st) {
  21. var args, context;
  22. var namespace = {
  23. func: functionBind.call(function () {
  24. args = Array.prototype.slice.call(arguments);
  25. context = this;
  26. })
  27. };
  28. namespace.func(1, 2, 3);
  29. st.deepEqual(args, [1, 2, 3]);
  30. st.equal(context, getCurrentContext.call());
  31. st.end();
  32. });
  33. t.test('binds properly, and still supplies bound arguments', function (st) {
  34. var args, context;
  35. var namespace = {
  36. func: functionBind.call(function () {
  37. args = Array.prototype.slice.call(arguments);
  38. context = this;
  39. }, undefined, 1, 2, 3)
  40. };
  41. namespace.func(4, 5, 6);
  42. st.deepEqual(args, [1, 2, 3, 4, 5, 6]);
  43. st.equal(context, getCurrentContext.call());
  44. st.end();
  45. });
  46. t.test('returns properly', function (st) {
  47. var args;
  48. var namespace = {
  49. func: functionBind.call(function () {
  50. args = Array.prototype.slice.call(arguments);
  51. return this;
  52. }, null)
  53. };
  54. var context = namespace.func(1, 2, 3);
  55. st.equal(context, getCurrentContext.call(), 'returned context is namespaced context');
  56. st.deepEqual(args, [1, 2, 3], 'passed arguments are correct');
  57. st.end();
  58. });
  59. t.test('returns properly with bound arguments', function (st) {
  60. var args;
  61. var namespace = {
  62. func: functionBind.call(function () {
  63. args = Array.prototype.slice.call(arguments);
  64. return this;
  65. }, null, 1, 2, 3)
  66. };
  67. var context = namespace.func(4, 5, 6);
  68. st.equal(context, getCurrentContext.call(), 'returned context is namespaced context');
  69. st.deepEqual(args, [1, 2, 3, 4, 5, 6], 'passed arguments are correct');
  70. st.end();
  71. });
  72. t.test('called as a constructor', function (st) {
  73. var thunkify = function (value) {
  74. return function () { return value; };
  75. };
  76. st.test('returns object value', function (sst) {
  77. var expectedReturnValue = [1, 2, 3];
  78. var Constructor = functionBind.call(thunkify(expectedReturnValue), null);
  79. var result = new Constructor();
  80. sst.equal(result, expectedReturnValue);
  81. sst.end();
  82. });
  83. st.test('does not return primitive value', function (sst) {
  84. var Constructor = functionBind.call(thunkify(42), null);
  85. var result = new Constructor();
  86. sst.notEqual(result, 42);
  87. sst.end();
  88. });
  89. st.test('object from bound constructor is instance of original and bound constructor', function (sst) {
  90. var A = function (x) {
  91. this.name = x || 'A';
  92. };
  93. var B = functionBind.call(A, null, 'B');
  94. var result = new B();
  95. sst.ok(result instanceof B, 'result is instance of bound constructor');
  96. sst.ok(result instanceof A, 'result is instance of original constructor');
  97. sst.end();
  98. });
  99. st.end();
  100. });
  101. t.end();
  102. });
  103. test('with a context', function (t) {
  104. t.test('with no bound arguments', function (st) {
  105. var args, context;
  106. var boundContext = {};
  107. var namespace = {
  108. func: functionBind.call(function () {
  109. args = Array.prototype.slice.call(arguments);
  110. context = this;
  111. }, boundContext)
  112. };
  113. namespace.func(1, 2, 3);
  114. st.equal(context, boundContext, 'binds a context properly');
  115. st.deepEqual(args, [1, 2, 3], 'supplies passed arguments');
  116. st.end();
  117. });
  118. t.test('with bound arguments', function (st) {
  119. var args, context;
  120. var boundContext = {};
  121. var namespace = {
  122. func: functionBind.call(function () {
  123. args = Array.prototype.slice.call(arguments);
  124. context = this;
  125. }, boundContext, 1, 2, 3)
  126. };
  127. namespace.func(4, 5, 6);
  128. st.equal(context, boundContext, 'binds a context properly');
  129. st.deepEqual(args, [1, 2, 3, 4, 5, 6], 'supplies bound and passed arguments');
  130. st.end();
  131. });
  132. t.test('returns properly', function (st) {
  133. var boundContext = {};
  134. var args;
  135. var namespace = {
  136. func: functionBind.call(function () {
  137. args = Array.prototype.slice.call(arguments);
  138. return this;
  139. }, boundContext)
  140. };
  141. var context = namespace.func(1, 2, 3);
  142. st.equal(context, boundContext, 'returned context is bound context');
  143. st.notEqual(context, getCurrentContext.call(), 'returned context is not lexical context');
  144. st.deepEqual(args, [1, 2, 3], 'passed arguments are correct');
  145. st.end();
  146. });
  147. t.test('returns properly with bound arguments', function (st) {
  148. var boundContext = {};
  149. var args;
  150. var namespace = {
  151. func: functionBind.call(function () {
  152. args = Array.prototype.slice.call(arguments);
  153. return this;
  154. }, boundContext, 1, 2, 3)
  155. };
  156. var context = namespace.func(4, 5, 6);
  157. st.equal(context, boundContext, 'returned context is bound context');
  158. st.notEqual(context, getCurrentContext.call(), 'returned context is not lexical context');
  159. st.deepEqual(args, [1, 2, 3, 4, 5, 6], 'passed arguments are correct');
  160. st.end();
  161. });
  162. t.test('passes the correct arguments when called as a constructor', function (st) {
  163. var expected = { name: 'Correct' };
  164. var namespace = {
  165. Func: functionBind.call(function (arg) {
  166. return arg;
  167. }, { name: 'Incorrect' })
  168. };
  169. var returned = new namespace.Func(expected);
  170. st.equal(returned, expected, 'returns the right arg when called as a constructor');
  171. st.end();
  172. });
  173. t.test('has the new instance\'s context when called as a constructor', function (st) {
  174. var actualContext;
  175. var expectedContext = { foo: 'bar' };
  176. var namespace = {
  177. Func: functionBind.call(function () {
  178. actualContext = this;
  179. }, expectedContext)
  180. };
  181. var result = new namespace.Func();
  182. st.equal(result instanceof namespace.Func, true);
  183. st.notEqual(actualContext, expectedContext);
  184. st.end();
  185. });
  186. t.end();
  187. });
  188. test('bound function length', function (t) {
  189. t.test('sets a correct length without thisArg', function (st) {
  190. var subject = functionBind.call(function (a, b, c) { return a + b + c; });
  191. st.equal(subject.length, 3);
  192. st.equal(subject(1, 2, 3), 6);
  193. st.end();
  194. });
  195. t.test('sets a correct length with thisArg', function (st) {
  196. var subject = functionBind.call(function (a, b, c) { return a + b + c; }, {});
  197. st.equal(subject.length, 3);
  198. st.equal(subject(1, 2, 3), 6);
  199. st.end();
  200. });
  201. t.test('sets a correct length without thisArg and first argument', function (st) {
  202. var subject = functionBind.call(function (a, b, c) { return a + b + c; }, undefined, 1);
  203. st.equal(subject.length, 2);
  204. st.equal(subject(2, 3), 6);
  205. st.end();
  206. });
  207. t.test('sets a correct length with thisArg and first argument', function (st) {
  208. var subject = functionBind.call(function (a, b, c) { return a + b + c; }, {}, 1);
  209. st.equal(subject.length, 2);
  210. st.equal(subject(2, 3), 6);
  211. st.end();
  212. });
  213. t.test('sets a correct length without thisArg and too many arguments', function (st) {
  214. var subject = functionBind.call(function (a, b, c) { return a + b + c; }, undefined, 1, 2, 3, 4);
  215. st.equal(subject.length, 0);
  216. st.equal(subject(), 6);
  217. st.end();
  218. });
  219. t.test('sets a correct length with thisArg and too many arguments', function (st) {
  220. var subject = functionBind.call(function (a, b, c) { return a + b + c; }, {}, 1, 2, 3, 4);
  221. st.equal(subject.length, 0);
  222. st.equal(subject(), 6);
  223. st.end();
  224. });
  225. });