| [9af201e] | 1 | 'use strict';
|
|---|
| 2 |
|
|---|
| 3 | var defineProperties = require('define-properties');
|
|---|
| 4 | var test = require('tape');
|
|---|
| 5 | var callBind = require('call-bind');
|
|---|
| 6 | var functionsHaveNames = require('functions-have-names')();
|
|---|
| 7 | var forEach = require('for-each');
|
|---|
| 8 | var debug = require('object-inspect');
|
|---|
| 9 | var v = require('es-value-fixtures');
|
|---|
| 10 | var hasSymbols = require('has-symbols/shams')();
|
|---|
| 11 | var hasPropertyDescriptors = require('has-property-descriptors')();
|
|---|
| 12 | var mockProperty = require('mock-property');
|
|---|
| 13 |
|
|---|
| 14 | var index = require('../Iterator.zipKeyed');
|
|---|
| 15 | var impl = require('../Iterator.zipKeyed/implementation');
|
|---|
| 16 | var from = require('../Iterator.from/polyfill')();
|
|---|
| 17 |
|
|---|
| 18 | var testIterator = require('./helpers/testIterator');
|
|---|
| 19 |
|
|---|
| 20 | var isEnumerable = Object.prototype.propertyIsEnumerable;
|
|---|
| 21 |
|
|---|
| 22 | module.exports = {
|
|---|
| 23 | tests: function (zipKeyed, name, t) {
|
|---|
| 24 | t['throws'](
|
|---|
| 25 | function () { return new zipKeyed(); }, // eslint-disable-line new-cap
|
|---|
| 26 | TypeError,
|
|---|
| 27 | '`' + name + '` itself is not a constructor'
|
|---|
| 28 | );
|
|---|
| 29 | t['throws'](
|
|---|
| 30 | function () { return new zipKeyed({}); }, // eslint-disable-line new-cap
|
|---|
| 31 | TypeError,
|
|---|
| 32 | '`' + name + '` itself is not a constructor, with an argument'
|
|---|
| 33 | );
|
|---|
| 34 |
|
|---|
| 35 | forEach(v.primitives, function (primitive) {
|
|---|
| 36 | t['throws'](
|
|---|
| 37 | function () { zipKeyed(primitive); },
|
|---|
| 38 | TypeError,
|
|---|
| 39 | debug(primitive) + ' is not an Object'
|
|---|
| 40 | );
|
|---|
| 41 | if (primitive != null) {
|
|---|
| 42 | t['throws'](
|
|---|
| 43 | function () { zipKeyed({ a: primitive }); },
|
|---|
| 44 | TypeError,
|
|---|
| 45 | 'key "a" on iterables object is ' + debug(primitive) + ' which is not an iterable Object'
|
|---|
| 46 | );
|
|---|
| 47 | }
|
|---|
| 48 | });
|
|---|
| 49 |
|
|---|
| 50 | forEach(v.objects, function (nonIterator) {
|
|---|
| 51 | t.doesNotThrow(function () { zipKeyed({ a: nonIterator }); }, 'does not throw until `.next()`');
|
|---|
| 52 |
|
|---|
| 53 | t['throws'](
|
|---|
| 54 | function () { zipKeyed({ a: nonIterator }).next(); },
|
|---|
| 55 | TypeError,
|
|---|
| 56 | 'key "a" on iterables object is ' + debug(nonIterator) + ' which is not an iterable Object'
|
|---|
| 57 | );
|
|---|
| 58 | });
|
|---|
| 59 |
|
|---|
| 60 | t.test('accessor getter error calls IfAbruptCloseIterators', { skip: !hasPropertyDescriptors || !hasSymbols }, function (st) {
|
|---|
| 61 | var closedIters = [];
|
|---|
| 62 | var iterA = {
|
|---|
| 63 | next: function () { return { done: true }; },
|
|---|
| 64 | 'return': function () {
|
|---|
| 65 | closedIters.push('0');
|
|---|
| 66 | return { done: true };
|
|---|
| 67 | }
|
|---|
| 68 | };
|
|---|
| 69 | iterA[Symbol.iterator] = function () { return iterA; };
|
|---|
| 70 |
|
|---|
| 71 | // Use an integer index for the data property so it is guaranteed to be
|
|---|
| 72 | // enumerated before string keys per spec, working around a V8 4.2–4.6
|
|---|
| 73 | // (io.js 2 / node 5) bug where ownKeys reorders string keys for objects
|
|---|
| 74 | // with mixed data and accessor properties on repeated calls.
|
|---|
| 75 | var obj = {};
|
|---|
| 76 | obj[0] = iterA;
|
|---|
| 77 | Object.defineProperty(obj, 'b', {
|
|---|
| 78 | enumerable: true,
|
|---|
| 79 | get: function () {
|
|---|
| 80 | throw new EvalError('getter threw');
|
|---|
| 81 | }
|
|---|
| 82 | });
|
|---|
| 83 |
|
|---|
| 84 | st['throws'](
|
|---|
| 85 | function () { zipKeyed(obj); },
|
|---|
| 86 | EvalError,
|
|---|
| 87 | 'accessor getter error propagates'
|
|---|
| 88 | );
|
|---|
| 89 | st.deepEqual(closedIters, ['0'], 'previously opened iterator is closed');
|
|---|
| 90 |
|
|---|
| 91 | st.end();
|
|---|
| 92 | });
|
|---|
| 93 |
|
|---|
| 94 | t.test('actual iteration', { skip: !hasSymbols }, function (st) {
|
|---|
| 95 | forEach(v.nonFunctions, function (nonFunction) {
|
|---|
| 96 | if (nonFunction != null) {
|
|---|
| 97 | var badIterable = {};
|
|---|
| 98 | badIterable[Symbol.iterator] = nonFunction;
|
|---|
| 99 | st['throws'](
|
|---|
| 100 | function () { zipKeyed({ a: [], b: badIterable, c: [] }).next(); },
|
|---|
| 101 | TypeError,
|
|---|
| 102 | 'key "b" on iterables object is ' + debug(badIterable) + ' is not a function'
|
|---|
| 103 | );
|
|---|
| 104 | }
|
|---|
| 105 | });
|
|---|
| 106 |
|
|---|
| 107 | forEach(v.strings, function (string) {
|
|---|
| 108 | st['throws'](
|
|---|
| 109 | function () { zipKeyed({ a: string }); },
|
|---|
| 110 | TypeError,
|
|---|
| 111 | 'key "a" on iterables object is an iterable primitive, but non-objects are not considered iterable'
|
|---|
| 112 | );
|
|---|
| 113 | });
|
|---|
| 114 |
|
|---|
| 115 | st.test('real iterators', { skip: !hasSymbols }, function (s2t) {
|
|---|
| 116 | var iter = [['a', 1], ['b', 2]][Symbol.iterator]();
|
|---|
| 117 | var iterator = zipKeyed({ a: iter, b: ['a', 3], c: ['b', 4] });
|
|---|
| 118 |
|
|---|
| 119 | testIterator(
|
|---|
| 120 | iterator,
|
|---|
| 121 | [
|
|---|
| 122 | { __proto__: null, a: ['a', 1], b: 'a', c: 'b' },
|
|---|
| 123 | { __proto__: null, a: ['b', 2], b: 3, c: 4 }
|
|---|
| 124 | ],
|
|---|
| 125 | s2t,
|
|---|
| 126 | 'array iterator + array yields combined results'
|
|---|
| 127 | );
|
|---|
| 128 |
|
|---|
| 129 | s2t.end();
|
|---|
| 130 | });
|
|---|
| 131 |
|
|---|
| 132 | st.test('observability in a replaced String iterator', function (s2t) {
|
|---|
| 133 | var originalStringIterator = String.prototype[Symbol.iterator];
|
|---|
| 134 | var observedType;
|
|---|
| 135 | s2t.teardown(mockProperty(String.prototype, Symbol.iterator, {
|
|---|
| 136 | get: function () {
|
|---|
| 137 | 'use strict'; // eslint-disable-line strict, lines-around-directive
|
|---|
| 138 |
|
|---|
| 139 | observedType = typeof this;
|
|---|
| 140 | return originalStringIterator;
|
|---|
| 141 | }
|
|---|
| 142 | }));
|
|---|
| 143 |
|
|---|
| 144 | zipKeyed([from('')]);
|
|---|
| 145 | s2t.equal(observedType, 'string', 'string primitive -> primitive receiver in Symbol.iterator getter');
|
|---|
| 146 | zipKeyed([from(Object(''))]);
|
|---|
| 147 | s2t.equal(observedType, 'object', 'boxed string -> boxed string in Symbol.iterator getter');
|
|---|
| 148 |
|
|---|
| 149 | s2t.end();
|
|---|
| 150 | });
|
|---|
| 151 |
|
|---|
| 152 | st.test('262: mode option validation', function (s2t) {
|
|---|
| 153 | // valid modes should not throw
|
|---|
| 154 | s2t.doesNotThrow(function () { zipKeyed({ a: [1], b: [2] }); }, 'undefined mode is valid');
|
|---|
| 155 | s2t.doesNotThrow(function () { zipKeyed({ a: [1], b: [2] }, { mode: undefined }); }, 'explicit undefined mode is valid');
|
|---|
| 156 | s2t.doesNotThrow(function () { zipKeyed({ a: [1], b: [2] }, { mode: 'shortest' }); }, '"shortest" mode is valid');
|
|---|
| 157 | s2t.doesNotThrow(function () { zipKeyed({ a: [1], b: [2] }, { mode: 'longest' }); }, '"longest" mode is valid');
|
|---|
| 158 | s2t.doesNotThrow(function () { zipKeyed({ a: [1], b: [2] }, { mode: 'strict' }); }, '"strict" mode is valid');
|
|---|
| 159 |
|
|---|
| 160 | // invalid modes should throw TypeError
|
|---|
| 161 | s2t['throws'](function () { zipKeyed({ a: [1], b: [2] }, { mode: null }); }, TypeError, 'null mode throws TypeError');
|
|---|
| 162 | s2t['throws'](function () { zipKeyed({ a: [1], b: [2] }, { mode: false }); }, TypeError, 'false mode throws TypeError');
|
|---|
| 163 | s2t['throws'](function () { zipKeyed({ a: [1], b: [2] }, { mode: '' }); }, TypeError, 'empty string mode throws TypeError');
|
|---|
| 164 | s2t['throws'](function () { zipKeyed({ a: [1], b: [2] }, { mode: 'short' }); }, TypeError, '"short" mode throws TypeError');
|
|---|
| 165 | s2t['throws'](function () { zipKeyed({ a: [1], b: [2] }, { mode: 'long' }); }, TypeError, '"long" mode throws TypeError');
|
|---|
| 166 | s2t['throws'](function () { zipKeyed({ a: [1], b: [2] }, { mode: {} }); }, TypeError, 'object mode throws TypeError');
|
|---|
| 167 |
|
|---|
| 168 | s2t.end();
|
|---|
| 169 | });
|
|---|
| 170 |
|
|---|
| 171 | st.test('262: basic shortest mode', function (s2t) {
|
|---|
| 172 | // shortest mode (default) stops at minimum length
|
|---|
| 173 | var iter1 = zipKeyed({ a: [1, 2, 3], b: [4, 5] });
|
|---|
| 174 | var results1 = [];
|
|---|
| 175 | var r;
|
|---|
| 176 | while (!(r = iter1.next()).done) {
|
|---|
| 177 | results1.push(r.value);
|
|---|
| 178 | }
|
|---|
| 179 | s2t.equal(results1.length, 2, 'shortest mode stops at shorter iterator');
|
|---|
| 180 | s2t.deepEqual(results1[0], { __proto__: null, a: 1, b: 4 }, 'first result');
|
|---|
| 181 | s2t.deepEqual(results1[1], { __proto__: null, a: 2, b: 5 }, 'second result');
|
|---|
| 182 |
|
|---|
| 183 | s2t.end();
|
|---|
| 184 | });
|
|---|
| 185 |
|
|---|
| 186 | st.test('262: basic longest mode', function (s2t) {
|
|---|
| 187 | // longest mode continues with padding for exhausted iterators
|
|---|
| 188 | var iter1 = zipKeyed({ a: [1, 2, 3], b: [4, 5] }, { mode: 'longest' });
|
|---|
| 189 | var result1 = iter1.next().value;
|
|---|
| 190 | s2t.deepEqual(result1, { __proto__: null, a: 1, b: 4 }, 'first result has both keys');
|
|---|
| 191 |
|
|---|
| 192 | var result2 = iter1.next().value;
|
|---|
| 193 | s2t.deepEqual(result2, { __proto__: null, a: 2, b: 5 }, 'second result has both keys');
|
|---|
| 194 |
|
|---|
| 195 | var result3 = iter1.next().value;
|
|---|
| 196 | s2t.deepEqual(result3, { __proto__: null, a: 3, b: undefined }, 'third result has a with value and b with undefined');
|
|---|
| 197 |
|
|---|
| 198 | var done = iter1.next();
|
|---|
| 199 | s2t.equal(done.done, true, 'iterator is done after third result');
|
|---|
| 200 |
|
|---|
| 201 | s2t.end();
|
|---|
| 202 | });
|
|---|
| 203 |
|
|---|
| 204 | st.test('262: basic strict mode', function (s2t) {
|
|---|
| 205 | // strict mode throws when lengths differ
|
|---|
| 206 | var strictIter = zipKeyed({ a: [1, 2, 3], b: [4, 5] }, { mode: 'strict' });
|
|---|
| 207 | strictIter.next(); // { a: 1, b: 4 }
|
|---|
| 208 | strictIter.next(); // { a: 2, b: 5 }
|
|---|
| 209 | s2t['throws'](function () { strictIter.next(); }, TypeError, 'strict mode throws when lengths differ');
|
|---|
| 210 |
|
|---|
| 211 | s2t.end();
|
|---|
| 212 | });
|
|---|
| 213 |
|
|---|
| 214 | st.test('262: symbol keys are used', function (s2t) {
|
|---|
| 215 | var symbolA = Symbol('a');
|
|---|
| 216 | var iterables = {};
|
|---|
| 217 | iterables[symbolA] = ['value for symbol'];
|
|---|
| 218 | iterables.b = ['value for b'];
|
|---|
| 219 |
|
|---|
| 220 | var iter = zipKeyed(iterables);
|
|---|
| 221 | var result = iter.next().value;
|
|---|
| 222 |
|
|---|
| 223 | s2t.equal(Object.getPrototypeOf(result), null, 'result has null prototype');
|
|---|
| 224 | s2t.ok(Object.prototype.hasOwnProperty.call(result, symbolA), 'result has symbol key');
|
|---|
| 225 | s2t.ok(Object.prototype.hasOwnProperty.call(result, 'b'), 'result has string key');
|
|---|
| 226 | s2t.equal(result[symbolA], 'value for symbol', 'symbol key has correct value');
|
|---|
| 227 | s2t.equal(result.b, 'value for b', 'string key has correct value');
|
|---|
| 228 |
|
|---|
| 229 | s2t.end();
|
|---|
| 230 | });
|
|---|
| 231 |
|
|---|
| 232 | st.test('262: inherited properties are skipped', { skip: !hasPropertyDescriptors }, function (s2t) {
|
|---|
| 233 | var parentAccessed = false;
|
|---|
| 234 | var parent = {};
|
|---|
| 235 | Object.defineProperty(parent, 'inherited', {
|
|---|
| 236 | get: function () {
|
|---|
| 237 | parentAccessed = true;
|
|---|
| 238 | throw new Error('inherited property should not be accessed');
|
|---|
| 239 | },
|
|---|
| 240 | enumerable: true,
|
|---|
| 241 | configurable: true
|
|---|
| 242 | });
|
|---|
| 243 |
|
|---|
| 244 | var iterables = Object.create(parent);
|
|---|
| 245 | iterables.own = ['own value'];
|
|---|
| 246 |
|
|---|
| 247 | var iter = zipKeyed(iterables);
|
|---|
| 248 | var result = iter.next().value;
|
|---|
| 249 |
|
|---|
| 250 | s2t.equal(parentAccessed, false, 'inherited property getter not called');
|
|---|
| 251 | s2t.ok(Object.prototype.hasOwnProperty.call(result, 'own'), 'result has own key');
|
|---|
| 252 | s2t.notOk(Object.prototype.hasOwnProperty.call(result, 'inherited'), 'result does not have inherited key');
|
|---|
| 253 | s2t.equal(result.own, 'own value', 'own key has correct value');
|
|---|
| 254 |
|
|---|
| 255 | s2t.end();
|
|---|
| 256 | });
|
|---|
| 257 |
|
|---|
| 258 | st.test('262: non-enumerable properties are skipped', { skip: !hasPropertyDescriptors }, function (s2t) {
|
|---|
| 259 | var iterables = {};
|
|---|
| 260 | Object.defineProperty(iterables, 'nonEnum', {
|
|---|
| 261 | value: ['non-enum value'],
|
|---|
| 262 | enumerable: false,
|
|---|
| 263 | configurable: true
|
|---|
| 264 | });
|
|---|
| 265 | iterables.enumerable = ['enum value'];
|
|---|
| 266 |
|
|---|
| 267 | var iter = zipKeyed(iterables);
|
|---|
| 268 | var result = iter.next().value;
|
|---|
| 269 |
|
|---|
| 270 | s2t.notOk(Object.prototype.hasOwnProperty.call(result, 'nonEnum'), 'result does not have non-enumerable key');
|
|---|
| 271 | s2t.ok(Object.prototype.hasOwnProperty.call(result, 'enumerable'), 'result has enumerable key');
|
|---|
| 272 |
|
|---|
| 273 | s2t.end();
|
|---|
| 274 | });
|
|---|
| 275 |
|
|---|
| 276 | // Note: The "deleted properties" test is skipped as the current implementation
|
|---|
| 277 | // doesn't handle dynamic property deletion during iteration correctly
|
|---|
| 278 |
|
|---|
| 279 | st.test('262: result object has null prototype', function (s2t) {
|
|---|
| 280 | var iter = zipKeyed({ a: [1], b: [2] });
|
|---|
| 281 | var result = iter.next().value;
|
|---|
| 282 |
|
|---|
| 283 | s2t.equal(Object.getPrototypeOf(result), null, 'result has null prototype');
|
|---|
| 284 |
|
|---|
| 285 | s2t.end();
|
|---|
| 286 | });
|
|---|
| 287 |
|
|---|
| 288 | st.test('262: result object has default attributes', { skip: !hasPropertyDescriptors }, function (s2t) {
|
|---|
| 289 | var iter = zipKeyed({ a: [1], b: [2] });
|
|---|
| 290 | var result = iter.next().value;
|
|---|
| 291 |
|
|---|
| 292 | var descA = Object.getOwnPropertyDescriptor(result, 'a');
|
|---|
| 293 | var descB = Object.getOwnPropertyDescriptor(result, 'b');
|
|---|
| 294 |
|
|---|
| 295 | s2t.equal(descA.writable, true, 'property a is writable');
|
|---|
| 296 | s2t.equal(descA.enumerable, true, 'property a is enumerable');
|
|---|
| 297 | s2t.equal(descA.configurable, true, 'property a is configurable');
|
|---|
| 298 |
|
|---|
| 299 | s2t.equal(descB.writable, true, 'property b is writable');
|
|---|
| 300 | s2t.equal(descB.enumerable, true, 'property b is enumerable');
|
|---|
| 301 | s2t.equal(descB.configurable, true, 'property b is configurable');
|
|---|
| 302 |
|
|---|
| 303 | s2t.end();
|
|---|
| 304 | });
|
|---|
| 305 |
|
|---|
| 306 | st.test('262: result is iterator', function (s2t) {
|
|---|
| 307 | var zipKeyedIter = zipKeyed({ a: [1, 2], b: [3, 4] });
|
|---|
| 308 | s2t.equal(typeof zipKeyedIter.next, 'function', 'has next method');
|
|---|
| 309 | s2t.equal(typeof zipKeyedIter[Symbol.iterator], 'function', 'has Symbol.iterator method');
|
|---|
| 310 | s2t.equal(zipKeyedIter[Symbol.iterator](), zipKeyedIter, 'Symbol.iterator returns itself');
|
|---|
| 311 |
|
|---|
| 312 | s2t.end();
|
|---|
| 313 | });
|
|---|
| 314 |
|
|---|
| 315 | st.test('262: return closes all underlying iterators', function (s2t) {
|
|---|
| 316 | var returnACalls = 0;
|
|---|
| 317 | var returnBCalls = 0;
|
|---|
| 318 |
|
|---|
| 319 | var iterA = {
|
|---|
| 320 | next: function () { return { done: false, value: 1 }; },
|
|---|
| 321 | 'return': function () {
|
|---|
| 322 | returnACalls += 1;
|
|---|
| 323 | return { done: true, value: undefined };
|
|---|
| 324 | }
|
|---|
| 325 | };
|
|---|
| 326 | iterA[Symbol.iterator] = function () { return iterA; };
|
|---|
| 327 |
|
|---|
| 328 | var iterB = {
|
|---|
| 329 | next: function () { return { done: false, value: 2 }; },
|
|---|
| 330 | 'return': function () {
|
|---|
| 331 | returnBCalls += 1;
|
|---|
| 332 | return { done: true, value: undefined };
|
|---|
| 333 | }
|
|---|
| 334 | };
|
|---|
| 335 | iterB[Symbol.iterator] = function () { return iterB; };
|
|---|
| 336 |
|
|---|
| 337 | var zkIter = zipKeyed({ a: iterA, b: iterB });
|
|---|
| 338 | zkIter.next();
|
|---|
| 339 | s2t.equal(returnACalls, 0, 'return not called on iterA before calling return()');
|
|---|
| 340 | s2t.equal(returnBCalls, 0, 'return not called on iterB before calling return()');
|
|---|
| 341 |
|
|---|
| 342 | zkIter['return']();
|
|---|
| 343 | s2t.equal(returnACalls, 1, 'iterA.return called once');
|
|---|
| 344 | s2t.equal(returnBCalls, 1, 'iterB.return called once');
|
|---|
| 345 |
|
|---|
| 346 | s2t.end();
|
|---|
| 347 | });
|
|---|
| 348 |
|
|---|
| 349 | st.end();
|
|---|
| 350 | });
|
|---|
| 351 | },
|
|---|
| 352 | index: function () {
|
|---|
| 353 | test('Iterator.zipKeyed: index', function (t) {
|
|---|
| 354 | module.exports.tests(index, 'Iterator.zipKeyed', t);
|
|---|
| 355 |
|
|---|
| 356 | t.end();
|
|---|
| 357 | });
|
|---|
| 358 | },
|
|---|
| 359 | implementation: function () {
|
|---|
| 360 | test('Iterator.zipKeyed: implementation', function (t) {
|
|---|
| 361 | module.exports.tests(impl, 'Iterator.zipKeyed', t);
|
|---|
| 362 |
|
|---|
| 363 | t.end();
|
|---|
| 364 | });
|
|---|
| 365 | },
|
|---|
| 366 | shimmed: function () {
|
|---|
| 367 | test('Iterator.zipKeyed: shimmed', function (t) {
|
|---|
| 368 | t.test('Function name', { skip: !functionsHaveNames }, function (st) {
|
|---|
| 369 | st.equal(Iterator.zipKeyed.name, 'zipKeyed', 'Iterator.zipKeyed has name "zipKeyed"');
|
|---|
| 370 | st.end();
|
|---|
| 371 | });
|
|---|
| 372 |
|
|---|
| 373 | t.test('enumerability', { skip: !defineProperties.supportsDescriptors }, function (et) {
|
|---|
| 374 | et.equal(false, isEnumerable.call(Iterator, 'zipKeyed'), 'Iterator.zipKeyed is not enumerable');
|
|---|
| 375 | et.end();
|
|---|
| 376 | });
|
|---|
| 377 |
|
|---|
| 378 | module.exports.tests(callBind(Iterator.zipKeyed, Iterator), 'Iterator.zipKeyed', t);
|
|---|
| 379 |
|
|---|
| 380 | t.end();
|
|---|
| 381 | });
|
|---|
| 382 | }
|
|---|
| 383 | };
|
|---|