// Licensed to the Software Freedom Conservancy (SFC) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The SFC licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. 'use strict'; const assert = require('assert'); const promise = require('../../lib/promise'); const {enablePromiseManager, promiseManagerSuite} = require('../../lib/test/promise'); describe('promise.consume()', function() { promiseManagerSuite(() => { it('requires inputs to be generator functions', function() { assert.throws(function() { promise.consume(function() {}); }); }); it('handles a basic generator with no yielded promises', function() { var values = []; return promise.consume(function* () { var i = 0; while (i < 4) { i = yield i + 1; values.push(i); } }).then(function() { assert.deepEqual([1, 2, 3, 4], values); }); }); it('handles a promise yielding generator', function() { var values = []; return promise.consume(function* () { var i = 0; while (i < 4) { // Test that things are actually async here. setTimeout(function() { values.push(i * 2); }, 10); yield promise.delayed(10).then(function() { values.push(i++); }); } }).then(function() { assert.deepEqual([0, 0, 2, 1, 4, 2, 6, 3], values); }); }); it('assignments to yielded promises get fulfilled value', function() { return promise.consume(function* () { let x = yield Promise.resolve(2); assert.equal(2, x); }); }); it('uses final return value as fulfillment value', function() { return promise.consume(function* () { yield 1; yield 2; return 3; }).then(function(value) { assert.equal(3, value); }); }); it('throws rejected promise errors within the generator', function() { var values = []; return promise.consume(function* () { values.push('a'); var e = Error('stub error'); try { yield Promise.reject(e); values.push('b'); } catch (ex) { assert.equal(e, ex); values.push('c'); } values.push('d'); }).then(function() { assert.deepEqual(['a', 'c', 'd'], values); }); }); it('aborts the generator if there is an unhandled rejection', function() { var values = []; var e = Error('stub error'); return promise.consume(function* () { values.push(1); yield promise.rejected(e); values.push(2); }).catch(function() { assert.deepEqual([1], values); }); }); it('yield waits for promises', function() { let values = []; let blocker = promise.delayed(100).then(() => { assert.deepEqual([1], values); return 2; }); return promise.consume(function* () { values.push(1); values.push(yield blocker, 3); }).then(function() { assert.deepEqual([1, 2, 3], values); }); }); it('accepts custom scopes', function() { return promise.consume(function* () { return this.name; }, {name: 'Bob'}).then(function(value) { assert.equal('Bob', value); }); }); it('accepts initial generator arguments', function() { return promise.consume(function* (a, b) { assert.equal('red', a); assert.equal('apples', b); }, null, 'red', 'apples'); }); }); enablePromiseManager(() => { it('is possible to cancel promise generators', function() { var values = []; var p = promise.consume(function* () { var i = 0; while (i < 3) { yield promise.delayed(100).then(function() { values.push(i++); }); } }); return promise.delayed(75).then(function() { p.cancel(); return p.catch(function() { return promise.delayed(300); }); }).then(function() { assert.deepEqual([0], values); }); }); it('executes generator within the control flow', function() { var promises = [ promise.defer(), promise.defer() ]; var values = []; setTimeout(function() { assert.deepEqual([], values); promises[0].fulfill(1); }, 100); setTimeout(function() { assert.deepEqual([1], values); promises[1].fulfill(2); }, 200); return promise.controlFlow().execute(function* () { values.push(yield promises[0].promise); values.push(yield promises[1].promise); values.push('fin'); }).then(function() { assert.deepEqual([1, 2, 'fin'], values); }); }); it('handles tasks scheduled in generator', function() { var flow = promise.controlFlow(); return flow.execute(function* () { var x = yield flow.execute(function() { return promise.delayed(10).then(function() { return 1; }); }); var y = yield flow.execute(function() { return 2; }); return x + y; }).then(function(value) { assert.equal(3, value); }); }); it('blocks the control flow while processing generator', function() { var values = []; return promise.controlFlow().wait(function* () { yield values.push(1); values.push(yield promise.delayed(10).then(function() { return 2; })); yield values.push(3); return values.length === 6; }, 250).then(function() { assert.deepEqual([1, 2, 3, 1, 2, 3], values); }); }); it('ControlFlow.wait() will timeout on long generator', function() { var values = []; return promise.controlFlow().wait(function* () { var i = 0; while (i < 3) { yield promise.delayed(100).then(function() { values.push(i++); }); } }, 75).catch(function() { assert.deepEqual( [0, 1, 2], values, 'Should complete one loop of wait condition'); }); }); describe('generators in promise callbacks', function() { it('works with no initial value', function() { var promises = [ promise.defer(), promise.defer() ]; var values = []; setTimeout(function() { promises[0].fulfill(1); }, 50); setTimeout(function() { promises[1].fulfill(2); }, 100); return promise.fulfilled().then(function*() { values.push(yield promises[0].promise); values.push(yield promises[1].promise); values.push('fin'); }).then(function() { assert.deepEqual([1, 2, 'fin'], values); }); }); it('starts the generator with promised value', function() { var promises = [ promise.defer(), promise.defer() ]; var values = []; setTimeout(function() { promises[0].fulfill(1); }, 50); setTimeout(function() { promises[1].fulfill(2); }, 100); return promise.fulfilled(3).then(function*(value) { var p1 = yield promises[0].promise; var p2 = yield promises[1].promise; values.push(value + p1); values.push(value + p2); values.push('fin'); }).then(function() { assert.deepEqual([4, 5, 'fin'], values); }); }); it('throws yielded rejections within the generator callback', function() { var d = promise.defer(); var e = Error('stub'); setTimeout(function() { d.reject(e); }, 50); return promise.fulfilled().then(function*() { var threw = false; try { yield d.promise; } catch (ex) { threw = true; assert.equal(e, ex); } assert.ok(threw); }); }); }); }); });