1// META: global=window,worker 2// META: script=../resources/test-utils.js 3// META: script=../resources/recording-streams.js 4'use strict'; 5 6const error1 = new Error('error1'); 7error1.name = 'error1'; 8 9const error2 = new Error('error2'); 10error2.name = 'error2'; 11 12promise_test(t => { 13 const ws = new WritableStream({ 14 write: t.unreached_func('write() should not be called') 15 }); 16 17 const writer = ws.getWriter(); 18 const writePromise = writer.write('a'); 19 20 const readyPromise = writer.ready; 21 22 writer.abort(error1); 23 24 assert_equals(writer.ready, readyPromise, 'the ready promise property should not change'); 25 26 return Promise.all([ 27 promise_rejects_exactly(t, error1, readyPromise, 'the ready promise should reject with error1'), 28 promise_rejects_exactly(t, error1, writePromise, 'the write() promise should reject with error1') 29 ]); 30}, 'Aborting a WritableStream before it starts should cause the writer\'s unsettled ready promise to reject'); 31 32promise_test(t => { 33 const ws = new WritableStream(); 34 35 const writer = ws.getWriter(); 36 writer.write('a'); 37 38 const readyPromise = writer.ready; 39 40 return readyPromise.then(() => { 41 writer.abort(error1); 42 43 assert_not_equals(writer.ready, readyPromise, 'the ready promise property should change'); 44 return promise_rejects_exactly(t, error1, writer.ready, 'the ready promise should reject with error1'); 45 }); 46}, 'Aborting a WritableStream should cause the writer\'s fulfilled ready promise to reset to a rejected one'); 47 48promise_test(t => { 49 const ws = new WritableStream(); 50 const writer = ws.getWriter(); 51 52 writer.releaseLock(); 53 54 return promise_rejects_js(t, TypeError, writer.abort(), 'abort() should reject with a TypeError'); 55}, 'abort() on a released writer rejects'); 56 57promise_test(t => { 58 const ws = recordingWritableStream(); 59 60 return delay(0) 61 .then(() => { 62 const writer = ws.getWriter(); 63 64 const abortPromise = writer.abort(error1); 65 66 return Promise.all([ 67 promise_rejects_exactly(t, error1, writer.write(1), 'write(1) must reject with error1'), 68 promise_rejects_exactly(t, error1, writer.write(2), 'write(2) must reject with error1'), 69 abortPromise 70 ]); 71 }) 72 .then(() => { 73 assert_array_equals(ws.events, ['abort', error1]); 74 }); 75}, 'Aborting a WritableStream immediately prevents future writes'); 76 77promise_test(t => { 78 const ws = recordingWritableStream(); 79 const results = []; 80 81 return delay(0) 82 .then(() => { 83 const writer = ws.getWriter(); 84 85 results.push( 86 writer.write(1), 87 promise_rejects_exactly(t, error1, writer.write(2), 'write(2) must reject with error1'), 88 promise_rejects_exactly(t, error1, writer.write(3), 'write(3) must reject with error1') 89 ); 90 91 const abortPromise = writer.abort(error1); 92 93 results.push( 94 promise_rejects_exactly(t, error1, writer.write(4), 'write(4) must reject with error1'), 95 promise_rejects_exactly(t, error1, writer.write(5), 'write(5) must reject with error1') 96 ); 97 98 return abortPromise; 99 }).then(() => { 100 assert_array_equals(ws.events, ['write', 1, 'abort', error1]); 101 102 return Promise.all(results); 103 }); 104}, 'Aborting a WritableStream prevents further writes after any that are in progress'); 105 106promise_test(() => { 107 const ws = new WritableStream({ 108 abort() { 109 return 'Hello'; 110 } 111 }); 112 const writer = ws.getWriter(); 113 114 return writer.abort('a').then(value => { 115 assert_equals(value, undefined, 'fulfillment value must be undefined'); 116 }); 117}, 'Fulfillment value of writer.abort() call must be undefined even if the underlying sink returns a non-undefined ' + 118 'value'); 119 120promise_test(t => { 121 const ws = new WritableStream({ 122 abort() { 123 throw error1; 124 } 125 }); 126 const writer = ws.getWriter(); 127 128 return promise_rejects_exactly(t, error1, writer.abort(undefined), 129 'rejection reason of abortPromise must be the error thrown by abort'); 130}, 'WritableStream if sink\'s abort throws, the promise returned by writer.abort() rejects'); 131 132promise_test(t => { 133 const ws = new WritableStream({ 134 abort() { 135 throw error1; 136 } 137 }); 138 const writer = ws.getWriter(); 139 140 const abortPromise1 = writer.abort(undefined); 141 const abortPromise2 = writer.abort(undefined); 142 143 assert_equals(abortPromise1, abortPromise2, 'the promises must be the same'); 144 145 return promise_rejects_exactly(t, error1, abortPromise1, 'promise must have matching rejection'); 146}, 'WritableStream if sink\'s abort throws, the promise returned by multiple writer.abort()s is the same and rejects'); 147 148promise_test(t => { 149 const ws = new WritableStream({ 150 abort() { 151 throw error1; 152 } 153 }); 154 155 return promise_rejects_exactly(t, error1, ws.abort(undefined), 156 'rejection reason of abortPromise must be the error thrown by abort'); 157}, 'WritableStream if sink\'s abort throws, the promise returned by ws.abort() rejects'); 158 159promise_test(t => { 160 let resolveWritePromise; 161 const ws = new WritableStream({ 162 write() { 163 return new Promise(resolve => { 164 resolveWritePromise = resolve; 165 }); 166 }, 167 abort() { 168 throw error1; 169 } 170 }); 171 172 const writer = ws.getWriter(); 173 174 writer.write().catch(() => {}); 175 return flushAsyncEvents().then(() => { 176 const abortPromise = writer.abort(undefined); 177 178 resolveWritePromise(); 179 return promise_rejects_exactly(t, error1, abortPromise, 180 'rejection reason of abortPromise must be the error thrown by abort'); 181 }); 182}, 'WritableStream if sink\'s abort throws, for an abort performed during a write, the promise returned by ' + 183 'ws.abort() rejects'); 184 185promise_test(() => { 186 const ws = recordingWritableStream(); 187 const writer = ws.getWriter(); 188 189 return writer.abort(error1).then(() => { 190 assert_array_equals(ws.events, ['abort', error1]); 191 }); 192}, 'Aborting a WritableStream passes through the given reason'); 193 194promise_test(t => { 195 const ws = new WritableStream(); 196 const writer = ws.getWriter(); 197 198 const abortPromise = writer.abort(error1); 199 200 const events = []; 201 writer.ready.catch(() => { 202 events.push('ready'); 203 }); 204 writer.closed.catch(() => { 205 events.push('closed'); 206 }); 207 208 return Promise.all([ 209 abortPromise, 210 promise_rejects_exactly(t, error1, writer.write(), 'writing should reject with error1'), 211 promise_rejects_exactly(t, error1, writer.close(), 'closing should reject with error1'), 212 promise_rejects_exactly(t, error1, writer.ready, 'ready should reject with error1'), 213 promise_rejects_exactly(t, error1, writer.closed, 'closed should reject with error1') 214 ]).then(() => { 215 assert_array_equals(['ready', 'closed'], events, 'ready should reject before closed'); 216 }); 217}, 'Aborting a WritableStream puts it in an errored state with the error passed to abort()'); 218 219promise_test(t => { 220 const ws = new WritableStream(); 221 const writer = ws.getWriter(); 222 223 const writePromise = promise_rejects_exactly(t, error1, writer.write('a'), 224 'writing should reject with error1'); 225 226 writer.abort(error1); 227 228 return writePromise; 229}, 'Aborting a WritableStream causes any outstanding write() promises to be rejected with the reason supplied'); 230 231promise_test(t => { 232 const ws = recordingWritableStream(); 233 const writer = ws.getWriter(); 234 235 const closePromise = writer.close(); 236 const abortPromise = writer.abort(error1); 237 238 return Promise.all([ 239 promise_rejects_exactly(t, error1, writer.closed, 'closed should reject with error1'), 240 promise_rejects_exactly(t, error1, closePromise, 'close() should reject with error1'), 241 abortPromise 242 ]).then(() => { 243 assert_array_equals(ws.events, ['abort', error1]); 244 }); 245}, 'Closing but then immediately aborting a WritableStream causes the stream to error'); 246 247promise_test(() => { 248 let resolveClose; 249 const ws = new WritableStream({ 250 close() { 251 return new Promise(resolve => { 252 resolveClose = resolve; 253 }); 254 } 255 }); 256 const writer = ws.getWriter(); 257 258 const closePromise = writer.close(); 259 260 return delay(0).then(() => { 261 const abortPromise = writer.abort(error1); 262 resolveClose(); 263 return Promise.all([ 264 writer.closed, 265 abortPromise, 266 closePromise 267 ]); 268 }); 269}, 'Closing a WritableStream and aborting it while it closes causes the stream to ignore the abort attempt'); 270 271promise_test(() => { 272 const ws = new WritableStream(); 273 const writer = ws.getWriter(); 274 275 writer.close(); 276 277 return delay(0).then(() => writer.abort()); 278}, 'Aborting a WritableStream after it is closed is a no-op'); 279 280promise_test(t => { 281 // Testing that per https://github.com/whatwg/streams/issues/620#issuecomment-263483953 the fallback to close was 282 // removed. 283 284 // Cannot use recordingWritableStream since it always has an abort 285 let closeCalled = false; 286 const ws = new WritableStream({ 287 close() { 288 closeCalled = true; 289 } 290 }); 291 292 const writer = ws.getWriter(); 293 294 writer.abort(error1); 295 296 return promise_rejects_exactly(t, error1, writer.closed, 'closed should reject with error1').then(() => { 297 assert_false(closeCalled, 'close must not have been called'); 298 }); 299}, 'WritableStream should NOT call underlying sink\'s close if no abort is supplied (historical)'); 300 301promise_test(() => { 302 let thenCalled = false; 303 const ws = new WritableStream({ 304 abort() { 305 return { 306 then(onFulfilled) { 307 thenCalled = true; 308 onFulfilled(); 309 } 310 }; 311 } 312 }); 313 const writer = ws.getWriter(); 314 return writer.abort().then(() => assert_true(thenCalled, 'then() should be called')); 315}, 'returning a thenable from abort() should work'); 316 317promise_test(t => { 318 const ws = new WritableStream({ 319 write() { 320 return flushAsyncEvents(); 321 } 322 }); 323 const writer = ws.getWriter(); 324 return writer.ready.then(() => { 325 const writePromise = writer.write('a'); 326 writer.abort(error1); 327 let closedRejected = false; 328 return Promise.all([ 329 writePromise.then(() => assert_false(closedRejected, '.closed should not resolve before write()')), 330 promise_rejects_exactly(t, error1, writer.closed, '.closed should reject').then(() => { 331 closedRejected = true; 332 }) 333 ]); 334 }); 335}, '.closed should not resolve before fulfilled write()'); 336 337promise_test(t => { 338 const ws = new WritableStream({ 339 write() { 340 return Promise.reject(error1); 341 } 342 }); 343 const writer = ws.getWriter(); 344 return writer.ready.then(() => { 345 const writePromise = writer.write('a'); 346 const abortPromise = writer.abort(error2); 347 let closedRejected = false; 348 return Promise.all([ 349 promise_rejects_exactly(t, error1, writePromise, 'write() should reject') 350 .then(() => assert_false(closedRejected, '.closed should not resolve before write()')), 351 promise_rejects_exactly(t, error2, writer.closed, '.closed should reject') 352 .then(() => { 353 closedRejected = true; 354 }), 355 abortPromise 356 ]); 357 }); 358}, '.closed should not resolve before rejected write(); write() error should not overwrite abort() error'); 359 360promise_test(t => { 361 const ws = new WritableStream({ 362 write() { 363 return flushAsyncEvents(); 364 } 365 }, new CountQueuingStrategy({ highWaterMark: 4 })); 366 const writer = ws.getWriter(); 367 return writer.ready.then(() => { 368 const settlementOrder = []; 369 return Promise.all([ 370 writer.write('1').then(() => settlementOrder.push(1)), 371 promise_rejects_exactly(t, error1, writer.write('2'), 'first queued write should be rejected') 372 .then(() => settlementOrder.push(2)), 373 promise_rejects_exactly(t, error1, writer.write('3'), 'second queued write should be rejected') 374 .then(() => settlementOrder.push(3)), 375 writer.abort(error1) 376 ]).then(() => assert_array_equals([1, 2, 3], settlementOrder, 'writes should be satisfied in order')); 377 }); 378}, 'writes should be satisfied in order when aborting'); 379 380promise_test(t => { 381 const ws = new WritableStream({ 382 write() { 383 return Promise.reject(error1); 384 } 385 }, new CountQueuingStrategy({ highWaterMark: 4 })); 386 const writer = ws.getWriter(); 387 return writer.ready.then(() => { 388 const settlementOrder = []; 389 return Promise.all([ 390 promise_rejects_exactly(t, error1, writer.write('1'), 'in-flight write should be rejected') 391 .then(() => settlementOrder.push(1)), 392 promise_rejects_exactly(t, error2, writer.write('2'), 'first queued write should be rejected') 393 .then(() => settlementOrder.push(2)), 394 promise_rejects_exactly(t, error2, writer.write('3'), 'second queued write should be rejected') 395 .then(() => settlementOrder.push(3)), 396 writer.abort(error2) 397 ]).then(() => assert_array_equals([1, 2, 3], settlementOrder, 'writes should be satisfied in order')); 398 }); 399}, 'writes should be satisfied in order after rejected write when aborting'); 400 401promise_test(t => { 402 const ws = new WritableStream({ 403 write() { 404 return Promise.reject(error1); 405 } 406 }); 407 const writer = ws.getWriter(); 408 return writer.ready.then(() => { 409 return Promise.all([ 410 promise_rejects_exactly(t, error1, writer.write('a'), 'writer.write() should reject with error from underlying write()'), 411 promise_rejects_exactly(t, error2, writer.close(), 412 'writer.close() should reject with error from underlying write()'), 413 writer.abort(error2) 414 ]); 415 }); 416}, 'close() should reject with abort reason why abort() is first error'); 417 418promise_test(() => { 419 let resolveWrite; 420 const ws = recordingWritableStream({ 421 write() { 422 return new Promise(resolve => { 423 resolveWrite = resolve; 424 }); 425 } 426 }); 427 428 const writer = ws.getWriter(); 429 return writer.ready.then(() => { 430 writer.write('a'); 431 const abortPromise = writer.abort('b'); 432 return flushAsyncEvents().then(() => { 433 assert_array_equals(ws.events, ['write', 'a'], 'abort should not be called while write is in-flight'); 434 resolveWrite(); 435 return abortPromise.then(() => { 436 assert_array_equals(ws.events, ['write', 'a', 'abort', 'b'], 'abort should be called after the write finishes'); 437 }); 438 }); 439 }); 440}, 'underlying abort() should not be called until underlying write() completes'); 441 442promise_test(() => { 443 let resolveClose; 444 const ws = recordingWritableStream({ 445 close() { 446 return new Promise(resolve => { 447 resolveClose = resolve; 448 }); 449 } 450 }); 451 452 const writer = ws.getWriter(); 453 return writer.ready.then(() => { 454 writer.close(); 455 const abortPromise = writer.abort(); 456 return flushAsyncEvents().then(() => { 457 assert_array_equals(ws.events, ['close'], 'abort should not be called while close is in-flight'); 458 resolveClose(); 459 return abortPromise.then(() => { 460 assert_array_equals(ws.events, ['close'], 'abort should not be called'); 461 }); 462 }); 463 }); 464}, 'underlying abort() should not be called if underlying close() has started'); 465 466promise_test(t => { 467 let rejectClose; 468 let abortCalled = false; 469 const ws = new WritableStream({ 470 close() { 471 return new Promise((resolve, reject) => { 472 rejectClose = reject; 473 }); 474 }, 475 abort() { 476 abortCalled = true; 477 } 478 }); 479 480 const writer = ws.getWriter(); 481 return writer.ready.then(() => { 482 const closePromise = writer.close(); 483 const abortPromise = writer.abort(); 484 return flushAsyncEvents().then(() => { 485 assert_false(abortCalled, 'underlying abort should not be called while close is in-flight'); 486 rejectClose(error1); 487 return promise_rejects_exactly(t, error1, abortPromise, 'abort should reject with the same reason').then(() => { 488 return promise_rejects_exactly(t, error1, closePromise, 'close should reject with the same reason'); 489 }).then(() => { 490 assert_false(abortCalled, 'underlying abort should not be called after close completes'); 491 }); 492 }); 493 }); 494}, 'if underlying close() has started and then rejects, the abort() and close() promises should reject with the ' + 495 'underlying close rejection reason'); 496 497promise_test(t => { 498 let resolveWrite; 499 const ws = recordingWritableStream({ 500 write() { 501 return new Promise(resolve => { 502 resolveWrite = resolve; 503 }); 504 } 505 }); 506 507 const writer = ws.getWriter(); 508 return writer.ready.then(() => { 509 writer.write('a'); 510 const closePromise = writer.close(); 511 const abortPromise = writer.abort(error1); 512 513 return flushAsyncEvents().then(() => { 514 assert_array_equals(ws.events, ['write', 'a'], 'abort should not be called while write is in-flight'); 515 resolveWrite(); 516 return abortPromise.then(() => { 517 assert_array_equals(ws.events, ['write', 'a', 'abort', error1], 'abort should be called after write completes'); 518 return promise_rejects_exactly(t, error1, closePromise, 'promise returned by close() should be rejected'); 519 }); 520 }); 521 }); 522}, 'an abort() that happens during a write() should trigger the underlying abort() even with a close() queued'); 523 524promise_test(t => { 525 const ws = new WritableStream({ 526 write() { 527 return new Promise(() => {}); 528 } 529 }); 530 531 const writer = ws.getWriter(); 532 return writer.ready.then(() => { 533 writer.write('a'); 534 writer.abort(error1); 535 writer.releaseLock(); 536 const writer2 = ws.getWriter(); 537 return promise_rejects_exactly(t, error1, writer2.ready, 538 'ready of the second writer should be rejected with error1'); 539 }); 540}, 'if a writer is created for a stream with a pending abort, its ready should be rejected with the abort error'); 541 542promise_test(() => { 543 const ws = new WritableStream(); 544 const writer = ws.getWriter(); 545 return writer.ready.then(() => { 546 const closePromise = writer.close(); 547 const abortPromise = writer.abort(); 548 const events = []; 549 return Promise.all([ 550 closePromise.then(() => { events.push('close'); }), 551 abortPromise.then(() => { events.push('abort'); }) 552 ]).then(() => { 553 assert_array_equals(events, ['close', 'abort']); 554 }); 555 }); 556}, 'writer close() promise should resolve before abort() promise'); 557 558promise_test(t => { 559 const ws = new WritableStream({ 560 write(chunk, controller) { 561 controller.error(error1); 562 return new Promise(() => {}); 563 } 564 }); 565 const writer = ws.getWriter(); 566 return writer.ready.then(() => { 567 writer.write('a'); 568 return promise_rejects_exactly(t, error1, writer.ready, 'writer.ready should reject'); 569 }); 570}, 'writer.ready should reject on controller error without waiting for underlying write'); 571 572promise_test(t => { 573 let rejectWrite; 574 const ws = new WritableStream({ 575 write() { 576 return new Promise((resolve, reject) => { 577 rejectWrite = reject; 578 }); 579 } 580 }); 581 582 let writePromise; 583 let abortPromise; 584 585 const events = []; 586 587 const writer = ws.getWriter(); 588 589 writer.closed.catch(() => { 590 events.push('closed'); 591 }); 592 593 // Wait for ws to start 594 return flushAsyncEvents().then(() => { 595 writePromise = writer.write('a'); 596 writePromise.catch(() => { 597 events.push('writePromise'); 598 }); 599 600 abortPromise = writer.abort(error1); 601 abortPromise.then(() => { 602 events.push('abortPromise'); 603 }); 604 605 const writePromise2 = writer.write('a'); 606 607 return Promise.all([ 608 promise_rejects_exactly(t, error1, writePromise2, 'writePromise2 must reject with the error from abort'), 609 promise_rejects_exactly(t, error1, writer.ready, 'writer.ready must reject with the error from abort'), 610 flushAsyncEvents() 611 ]); 612 }).then(() => { 613 assert_array_equals(events, [], 'writePromise, abortPromise and writer.closed must not be rejected yet'); 614 615 rejectWrite(error2); 616 617 return Promise.all([ 618 promise_rejects_exactly(t, error2, writePromise, 619 'writePromise must reject with the error returned from the sink\'s write method'), 620 abortPromise, 621 promise_rejects_exactly(t, error1, writer.closed, 622 'writer.closed must reject with the error from abort'), 623 flushAsyncEvents() 624 ]); 625 }).then(() => { 626 assert_array_equals(events, ['writePromise', 'abortPromise', 'closed'], 627 'writePromise, abortPromise and writer.closed must settle'); 628 629 const writePromise3 = writer.write('a'); 630 631 return Promise.all([ 632 promise_rejects_exactly(t, error1, writePromise3, 633 'writePromise3 must reject with the error from abort'), 634 promise_rejects_exactly(t, error1, writer.ready, 635 'writer.ready must be still rejected with the error indicating abort') 636 ]); 637 }).then(() => { 638 writer.releaseLock(); 639 640 return Promise.all([ 641 promise_rejects_js(t, TypeError, writer.ready, 642 'writer.ready must be rejected with an error indicating release'), 643 promise_rejects_js(t, TypeError, writer.closed, 644 'writer.closed must be rejected with an error indicating release') 645 ]); 646 }); 647}, 'writer.abort() while there is an in-flight write, and then finish the write with rejection'); 648 649promise_test(t => { 650 let resolveWrite; 651 let controller; 652 const ws = new WritableStream({ 653 write(chunk, c) { 654 controller = c; 655 return new Promise(resolve => { 656 resolveWrite = resolve; 657 }); 658 } 659 }); 660 661 let writePromise; 662 let abortPromise; 663 664 const events = []; 665 666 const writer = ws.getWriter(); 667 668 writer.closed.catch(() => { 669 events.push('closed'); 670 }); 671 672 // Wait for ws to start 673 return flushAsyncEvents().then(() => { 674 writePromise = writer.write('a'); 675 writePromise.then(() => { 676 events.push('writePromise'); 677 }); 678 679 abortPromise = writer.abort(error1); 680 abortPromise.then(() => { 681 events.push('abortPromise'); 682 }); 683 684 const writePromise2 = writer.write('a'); 685 686 return Promise.all([ 687 promise_rejects_exactly(t, error1, writePromise2, 'writePromise2 must reject with the error from abort'), 688 promise_rejects_exactly(t, error1, writer.ready, 'writer.ready must reject with the error from abort'), 689 flushAsyncEvents() 690 ]); 691 }).then(() => { 692 assert_array_equals(events, [], 'writePromise, abortPromise and writer.closed must not be fulfilled/rejected yet'); 693 694 // This error is too late to change anything. abort() has already changed the stream state to 'erroring'. 695 controller.error(error2); 696 697 const writePromise3 = writer.write('a'); 698 699 return Promise.all([ 700 promise_rejects_exactly(t, error1, writePromise3, 701 'writePromise3 must reject with the error from abort'), 702 promise_rejects_exactly(t, error1, writer.ready, 703 'writer.ready must be still rejected with the error indicating abort'), 704 flushAsyncEvents() 705 ]); 706 }).then(() => { 707 assert_array_equals( 708 events, [], 709 'writePromise, abortPromise and writer.closed must not be fulfilled/rejected yet even after ' + 710 'controller.error() call'); 711 712 resolveWrite(); 713 714 return Promise.all([ 715 writePromise, 716 abortPromise, 717 promise_rejects_exactly(t, error1, writer.closed, 718 'writer.closed must reject with the error from abort'), 719 flushAsyncEvents() 720 ]); 721 }).then(() => { 722 assert_array_equals(events, ['writePromise', 'abortPromise', 'closed'], 723 'writePromise, abortPromise and writer.closed must settle'); 724 725 const writePromise4 = writer.write('a'); 726 727 return Promise.all([ 728 writePromise, 729 promise_rejects_exactly(t, error1, writePromise4, 730 'writePromise4 must reject with the error from abort'), 731 promise_rejects_exactly(t, error1, writer.ready, 732 'writer.ready must be still rejected with the error indicating abort') 733 ]); 734 }).then(() => { 735 writer.releaseLock(); 736 737 return Promise.all([ 738 promise_rejects_js(t, TypeError, writer.ready, 739 'writer.ready must be rejected with an error indicating release'), 740 promise_rejects_js(t, TypeError, writer.closed, 741 'writer.closed must be rejected with an error indicating release') 742 ]); 743 }); 744}, 'writer.abort(), controller.error() while there is an in-flight write, and then finish the write'); 745 746promise_test(t => { 747 let resolveClose; 748 let controller; 749 const ws = new WritableStream({ 750 start(c) { 751 controller = c; 752 }, 753 close() { 754 return new Promise(resolve => { 755 resolveClose = resolve; 756 }); 757 } 758 }); 759 760 let closePromise; 761 let abortPromise; 762 763 const events = []; 764 765 const writer = ws.getWriter(); 766 767 writer.closed.then(() => { 768 events.push('closed'); 769 }); 770 771 // Wait for ws to start 772 return flushAsyncEvents().then(() => { 773 closePromise = writer.close(); 774 closePromise.then(() => { 775 events.push('closePromise'); 776 }); 777 778 abortPromise = writer.abort(error1); 779 abortPromise.then(() => { 780 events.push('abortPromise'); 781 }); 782 783 return Promise.all([ 784 promise_rejects_js(t, TypeError, writer.close(), 785 'writer.close() must reject with an error indicating already closing'), 786 promise_rejects_exactly(t, error1, writer.ready, 'writer.ready must reject with the error from abort'), 787 flushAsyncEvents() 788 ]); 789 }).then(() => { 790 assert_array_equals(events, [], 'closePromise, abortPromise and writer.closed must not be fulfilled/rejected yet'); 791 792 controller.error(error2); 793 794 return Promise.all([ 795 promise_rejects_js(t, TypeError, writer.close(), 796 'writer.close() must reject with an error indicating already closing'), 797 promise_rejects_exactly(t, error1, writer.ready, 798 'writer.ready must be still rejected with the error indicating abort'), 799 flushAsyncEvents() 800 ]); 801 }).then(() => { 802 assert_array_equals( 803 events, [], 804 'closePromise, abortPromise and writer.closed must not be fulfilled/rejected yet even after ' + 805 'controller.error() call'); 806 807 resolveClose(); 808 809 return Promise.all([ 810 closePromise, 811 abortPromise, 812 writer.closed, 813 flushAsyncEvents() 814 ]); 815 }).then(() => { 816 assert_array_equals(events, ['closePromise', 'abortPromise', 'closed'], 817 'closedPromise, abortPromise and writer.closed must fulfill'); 818 819 return Promise.all([ 820 promise_rejects_js(t, TypeError, writer.close(), 821 'writer.close() must reject with an error indicating already closing'), 822 promise_rejects_exactly(t, error1, writer.ready, 823 'writer.ready must be still rejected with the error indicating abort') 824 ]); 825 }).then(() => { 826 writer.releaseLock(); 827 828 return Promise.all([ 829 promise_rejects_js(t, TypeError, writer.close(), 830 'writer.close() must reject with an error indicating release'), 831 promise_rejects_js(t, TypeError, writer.ready, 832 'writer.ready must be rejected with an error indicating release'), 833 promise_rejects_js(t, TypeError, writer.closed, 834 'writer.closed must be rejected with an error indicating release') 835 ]); 836 }); 837}, 'writer.abort(), controller.error() while there is an in-flight close, and then finish the close'); 838 839promise_test(t => { 840 let resolveWrite; 841 let controller; 842 const ws = recordingWritableStream({ 843 write(chunk, c) { 844 controller = c; 845 return new Promise(resolve => { 846 resolveWrite = resolve; 847 }); 848 } 849 }); 850 851 let writePromise; 852 let abortPromise; 853 854 const events = []; 855 856 const writer = ws.getWriter(); 857 858 writer.closed.catch(() => { 859 events.push('closed'); 860 }); 861 862 // Wait for ws to start 863 return flushAsyncEvents().then(() => { 864 writePromise = writer.write('a'); 865 writePromise.then(() => { 866 events.push('writePromise'); 867 }); 868 869 controller.error(error2); 870 871 const writePromise2 = writer.write('a'); 872 873 return Promise.all([ 874 promise_rejects_exactly(t, error2, writePromise2, 875 'writePromise2 must reject with the error passed to the controller\'s error method'), 876 promise_rejects_exactly(t, error2, writer.ready, 877 'writer.ready must reject with the error passed to the controller\'s error method'), 878 flushAsyncEvents() 879 ]); 880 }).then(() => { 881 assert_array_equals(events, [], 'writePromise and writer.closed must not be fulfilled/rejected yet'); 882 883 abortPromise = writer.abort(error1); 884 abortPromise.catch(() => { 885 events.push('abortPromise'); 886 }); 887 888 const writePromise3 = writer.write('a'); 889 890 return Promise.all([ 891 promise_rejects_exactly(t, error2, writePromise3, 892 'writePromise3 must reject with the error passed to the controller\'s error method'), 893 flushAsyncEvents() 894 ]); 895 }).then(() => { 896 assert_array_equals( 897 events, [], 898 'writePromise and writer.closed must not be fulfilled/rejected yet even after writer.abort()'); 899 900 resolveWrite(); 901 902 return Promise.all([ 903 promise_rejects_exactly(t, error2, abortPromise, 904 'abort() must reject with the error passed to the controller\'s error method'), 905 promise_rejects_exactly(t, error2, writer.closed, 906 'writer.closed must reject with the error passed to the controller\'s error method'), 907 flushAsyncEvents() 908 ]); 909 }).then(() => { 910 assert_array_equals(events, ['writePromise', 'abortPromise', 'closed'], 911 'writePromise, abortPromise and writer.closed must fulfill/reject'); 912 assert_array_equals(ws.events, ['write', 'a'], 'sink abort() should not be called'); 913 914 const writePromise4 = writer.write('a'); 915 916 return Promise.all([ 917 writePromise, 918 promise_rejects_exactly(t, error2, writePromise4, 919 'writePromise4 must reject with the error passed to the controller\'s error method'), 920 promise_rejects_exactly(t, error2, writer.ready, 921 'writer.ready must be still rejected with the error passed to the controller\'s error method') 922 ]); 923 }).then(() => { 924 writer.releaseLock(); 925 926 return Promise.all([ 927 promise_rejects_js(t, TypeError, writer.ready, 928 'writer.ready must be rejected with an error indicating release'), 929 promise_rejects_js(t, TypeError, writer.closed, 930 'writer.closed must be rejected with an error indicating release') 931 ]); 932 }); 933}, 'controller.error(), writer.abort() while there is an in-flight write, and then finish the write'); 934 935promise_test(t => { 936 let resolveClose; 937 let controller; 938 const ws = new WritableStream({ 939 start(c) { 940 controller = c; 941 }, 942 close() { 943 return new Promise(resolve => { 944 resolveClose = resolve; 945 }); 946 } 947 }); 948 949 let closePromise; 950 let abortPromise; 951 952 const events = []; 953 954 const writer = ws.getWriter(); 955 956 writer.closed.then(() => { 957 events.push('closed'); 958 }); 959 960 // Wait for ws to start 961 return flushAsyncEvents().then(() => { 962 closePromise = writer.close(); 963 closePromise.then(() => { 964 events.push('closePromise'); 965 }); 966 967 controller.error(error2); 968 969 return flushAsyncEvents(); 970 }).then(() => { 971 assert_array_equals(events, [], 'closePromise must not be fulfilled/rejected yet'); 972 973 abortPromise = writer.abort(error1); 974 abortPromise.then(() => { 975 events.push('abortPromise'); 976 }); 977 978 return Promise.all([ 979 promise_rejects_exactly(t, error2, writer.ready, 980 'writer.ready must reject with the error passed to the controller\'s error method'), 981 flushAsyncEvents() 982 ]); 983 }).then(() => { 984 assert_array_equals( 985 events, [], 986 'closePromise and writer.closed must not be fulfilled/rejected yet even after writer.abort()'); 987 988 resolveClose(); 989 990 return Promise.all([ 991 closePromise, 992 promise_rejects_exactly(t, error2, writer.ready, 993 'writer.ready must be still rejected with the error passed to the controller\'s error method'), 994 writer.closed, 995 flushAsyncEvents() 996 ]); 997 }).then(() => { 998 assert_array_equals(events, ['closePromise', 'abortPromise', 'closed'], 999 'abortPromise, closePromise and writer.closed must fulfill/reject'); 1000 }).then(() => { 1001 writer.releaseLock(); 1002 1003 return Promise.all([ 1004 promise_rejects_js(t, TypeError, writer.ready, 1005 'writer.ready must be rejected with an error indicating release'), 1006 promise_rejects_js(t, TypeError, writer.closed, 1007 'writer.closed must be rejected with an error indicating release') 1008 ]); 1009 }); 1010}, 'controller.error(), writer.abort() while there is an in-flight close, and then finish the close'); 1011 1012promise_test(t => { 1013 let resolveWrite; 1014 const ws = new WritableStream({ 1015 write() { 1016 return new Promise(resolve => { 1017 resolveWrite = resolve; 1018 }); 1019 } 1020 }); 1021 const writer = ws.getWriter(); 1022 return writer.ready.then(() => { 1023 const writePromise = writer.write('a'); 1024 const closed = writer.closed; 1025 const abortPromise = writer.abort(); 1026 writer.releaseLock(); 1027 resolveWrite(); 1028 return Promise.all([ 1029 writePromise, 1030 abortPromise, 1031 promise_rejects_js(t, TypeError, closed, 'closed should reject')]); 1032 }); 1033}, 'releaseLock() while aborting should reject the original closed promise'); 1034 1035// TODO(ricea): Consider removing this test if it is no longer useful. 1036promise_test(t => { 1037 let resolveWrite; 1038 let resolveAbort; 1039 let resolveAbortStarted; 1040 const abortStarted = new Promise(resolve => { 1041 resolveAbortStarted = resolve; 1042 }); 1043 const ws = new WritableStream({ 1044 write() { 1045 return new Promise(resolve => { 1046 resolveWrite = resolve; 1047 }); 1048 }, 1049 abort() { 1050 resolveAbortStarted(); 1051 return new Promise(resolve => { 1052 resolveAbort = resolve; 1053 }); 1054 } 1055 }); 1056 const writer = ws.getWriter(); 1057 return writer.ready.then(() => { 1058 const writePromise = writer.write('a'); 1059 const closed = writer.closed; 1060 const abortPromise = writer.abort(); 1061 resolveWrite(); 1062 return abortStarted.then(() => { 1063 writer.releaseLock(); 1064 assert_equals(writer.closed, closed, 'closed promise should not have changed'); 1065 resolveAbort(); 1066 return Promise.all([ 1067 writePromise, 1068 abortPromise, 1069 promise_rejects_js(t, TypeError, closed, 'closed should reject')]); 1070 }); 1071 }); 1072}, 'releaseLock() during delayed async abort() should reject the writer.closed promise'); 1073 1074promise_test(() => { 1075 let resolveStart; 1076 const ws = recordingWritableStream({ 1077 start() { 1078 return new Promise(resolve => { 1079 resolveStart = resolve; 1080 }); 1081 } 1082 }); 1083 const abortPromise = ws.abort('done'); 1084 return flushAsyncEvents().then(() => { 1085 assert_array_equals(ws.events, [], 'abort() should not be called during start()'); 1086 resolveStart(); 1087 return abortPromise.then(() => { 1088 assert_array_equals(ws.events, ['abort', 'done'], 'abort() should be called after start() is done'); 1089 }); 1090 }); 1091}, 'sink abort() should not be called until sink start() is done'); 1092 1093promise_test(() => { 1094 let resolveStart; 1095 let controller; 1096 const ws = recordingWritableStream({ 1097 start(c) { 1098 controller = c; 1099 return new Promise(resolve => { 1100 resolveStart = resolve; 1101 }); 1102 } 1103 }); 1104 const abortPromise = ws.abort('done'); 1105 controller.error(error1); 1106 resolveStart(); 1107 return abortPromise.then(() => 1108 assert_array_equals(ws.events, ['abort', 'done'], 1109 'abort() should still be called if start() errors the controller')); 1110}, 'if start attempts to error the controller after abort() has been called, then it should lose'); 1111 1112promise_test(() => { 1113 const ws = recordingWritableStream({ 1114 start() { 1115 return Promise.reject(error1); 1116 } 1117 }); 1118 return ws.abort('done').then(() => 1119 assert_array_equals(ws.events, ['abort', 'done'], 'abort() should still be called if start() rejects')); 1120}, 'stream abort() promise should still resolve if sink start() rejects'); 1121 1122promise_test(t => { 1123 const ws = new WritableStream(); 1124 const writer = ws.getWriter(); 1125 const writerReady1 = writer.ready; 1126 writer.abort(error1); 1127 const writerReady2 = writer.ready; 1128 assert_not_equals(writerReady1, writerReady2, 'abort() should replace the ready promise with a rejected one'); 1129 return Promise.all([writerReady1, 1130 promise_rejects_exactly(t, error1, writerReady2, 'writerReady2 should reject')]); 1131}, 'writer abort() during sink start() should replace the writer.ready promise synchronously'); 1132 1133promise_test(t => { 1134 const events = []; 1135 const ws = recordingWritableStream(); 1136 const writer = ws.getWriter(); 1137 const writePromise1 = writer.write(1); 1138 const abortPromise = writer.abort(error1); 1139 const writePromise2 = writer.write(2); 1140 const closePromise = writer.close(); 1141 writePromise1.catch(() => events.push('write1')); 1142 abortPromise.then(() => events.push('abort')); 1143 writePromise2.catch(() => events.push('write2')); 1144 closePromise.catch(() => events.push('close')); 1145 return Promise.all([ 1146 promise_rejects_exactly(t, error1, writePromise1, 'first write() should reject'), 1147 abortPromise, 1148 promise_rejects_exactly(t, error1, writePromise2, 'second write() should reject'), 1149 promise_rejects_exactly(t, error1, closePromise, 'close() should reject') 1150 ]) 1151 .then(() => { 1152 assert_array_equals(events, ['write2', 'write1', 'abort', 'close'], 1153 'promises should resolve in the standard order'); 1154 assert_array_equals(ws.events, ['abort', error1], 'underlying sink write() should not be called'); 1155 }); 1156}, 'promises returned from other writer methods should be rejected when writer abort() happens during sink start()'); 1157 1158promise_test(t => { 1159 let writeReject; 1160 let controller; 1161 const ws = new WritableStream({ 1162 write(chunk, c) { 1163 controller = c; 1164 return new Promise((resolve, reject) => { 1165 writeReject = reject; 1166 }); 1167 } 1168 }); 1169 const writer = ws.getWriter(); 1170 return writer.ready.then(() => { 1171 const writePromise = writer.write('a'); 1172 const abortPromise = writer.abort(); 1173 controller.error(error1); 1174 writeReject(error2); 1175 return Promise.all([ 1176 promise_rejects_exactly(t, error2, writePromise, 'write() should reject with error2'), 1177 abortPromise 1178 ]); 1179 }); 1180}, 'abort() should succeed despite rejection from write'); 1181 1182promise_test(t => { 1183 let closeReject; 1184 let controller; 1185 const ws = new WritableStream({ 1186 start(c) { 1187 controller = c; 1188 }, 1189 close() { 1190 return new Promise((resolve, reject) => { 1191 closeReject = reject; 1192 }); 1193 } 1194 }); 1195 const writer = ws.getWriter(); 1196 return writer.ready.then(() => { 1197 const closePromise = writer.close(); 1198 const abortPromise = writer.abort(); 1199 controller.error(error1); 1200 closeReject(error2); 1201 return Promise.all([ 1202 promise_rejects_exactly(t, error2, closePromise, 'close() should reject with error2'), 1203 promise_rejects_exactly(t, error2, abortPromise, 'abort() should reject with error2') 1204 ]); 1205 }); 1206}, 'abort() should be rejected with the rejection returned from close()'); 1207 1208promise_test(t => { 1209 let rejectWrite; 1210 const ws = recordingWritableStream({ 1211 write() { 1212 return new Promise((resolve, reject) => { 1213 rejectWrite = reject; 1214 }); 1215 } 1216 }); 1217 const writer = ws.getWriter(); 1218 return writer.ready.then(() => { 1219 const writePromise = writer.write('1'); 1220 const abortPromise = writer.abort(error2); 1221 rejectWrite(error1); 1222 return Promise.all([ 1223 promise_rejects_exactly(t, error1, writePromise, 'write should reject'), 1224 abortPromise, 1225 promise_rejects_exactly(t, error2, writer.closed, 'closed should reject with error2') 1226 ]); 1227 }).then(() => { 1228 assert_array_equals(ws.events, ['write', '1', 'abort', error2], 'abort sink method should be called'); 1229 }); 1230}, 'a rejecting sink.write() should not prevent sink.abort() from being called'); 1231 1232promise_test(() => { 1233 const ws = recordingWritableStream({ 1234 start() { 1235 return Promise.reject(error1); 1236 } 1237 }); 1238 return ws.abort(error2) 1239 .then(() => { 1240 assert_array_equals(ws.events, ['abort', error2]); 1241 }); 1242}, 'when start errors after stream abort(), underlying sink abort() should be called anyway'); 1243 1244promise_test(() => { 1245 const ws = new WritableStream(); 1246 const abortPromise1 = ws.abort(); 1247 const abortPromise2 = ws.abort(); 1248 assert_equals(abortPromise1, abortPromise2, 'the promises must be the same'); 1249 1250 return abortPromise1.then( 1251 v => assert_equals(v, undefined, 'abort() should fulfill with undefined')); 1252}, 'when calling abort() twice on the same stream, both should give the same promise that fulfills with undefined'); 1253 1254promise_test(() => { 1255 const ws = new WritableStream(); 1256 const abortPromise1 = ws.abort(); 1257 1258 return abortPromise1.then(v1 => { 1259 assert_equals(v1, undefined, 'first abort() should fulfill with undefined'); 1260 1261 const abortPromise2 = ws.abort(); 1262 assert_not_equals(abortPromise2, abortPromise1, 'because we waited, the second promise should be a new promise'); 1263 1264 return abortPromise2.then(v2 => { 1265 assert_equals(v2, undefined, 'second abort() should fulfill with undefined'); 1266 }); 1267 }); 1268}, 'when calling abort() twice on the same stream, but sequentially so so there\'s no pending abort the second time, ' + 1269 'both should fulfill with undefined'); 1270 1271promise_test(t => { 1272 const ws = new WritableStream({ 1273 start(c) { 1274 c.error(error1); 1275 } 1276 }); 1277 1278 const writer = ws.getWriter(); 1279 1280 return promise_rejects_exactly(t, error1, writer.closed, 'writer.closed should reject').then(() => { 1281 return writer.abort().then( 1282 v => assert_equals(v, undefined, 'abort() should fulfill with undefined')); 1283 }); 1284}, 'calling abort() on an errored stream should fulfill with undefined'); 1285 1286promise_test(t => { 1287 let controller; 1288 let resolveWrite; 1289 const ws = recordingWritableStream({ 1290 start(c) { 1291 controller = c; 1292 }, 1293 write() { 1294 return new Promise(resolve => { 1295 resolveWrite = resolve; 1296 }); 1297 } 1298 }); 1299 const writer = ws.getWriter(); 1300 return writer.ready.then(() => { 1301 const writePromise = writer.write('chunk'); 1302 controller.error(error1); 1303 const abortPromise = writer.abort(error2); 1304 resolveWrite(); 1305 return Promise.all([ 1306 writePromise, 1307 promise_rejects_exactly(t, error1, abortPromise, 'abort() should reject') 1308 ]).then(() => { 1309 assert_array_equals(ws.events, ['write', 'chunk'], 'sink abort() should not be called'); 1310 }); 1311 }); 1312}, 'sink abort() should not be called if stream was erroring due to controller.error() before abort() was called'); 1313 1314promise_test(t => { 1315 let resolveWrite; 1316 let size = 1; 1317 const ws = recordingWritableStream({ 1318 write() { 1319 return new Promise(resolve => { 1320 resolveWrite = resolve; 1321 }); 1322 } 1323 }, { 1324 size() { 1325 return size; 1326 }, 1327 highWaterMark: 1 1328 }); 1329 const writer = ws.getWriter(); 1330 return writer.ready.then(() => { 1331 const writePromise1 = writer.write('chunk1'); 1332 size = NaN; 1333 const writePromise2 = writer.write('chunk2'); 1334 const abortPromise = writer.abort(error2); 1335 resolveWrite(); 1336 return Promise.all([ 1337 writePromise1, 1338 promise_rejects_js(t, RangeError, writePromise2, 'second write() should reject'), 1339 promise_rejects_js(t, RangeError, abortPromise, 'abort() should reject') 1340 ]).then(() => { 1341 assert_array_equals(ws.events, ['write', 'chunk1'], 'sink abort() should not be called'); 1342 }); 1343 }); 1344}, 'sink abort() should not be called if stream was erroring due to bad strategy before abort() was called'); 1345 1346promise_test(t => { 1347 const ws = new WritableStream(); 1348 return ws.abort().then(() => { 1349 const writer = ws.getWriter(); 1350 return writer.closed.then(t.unreached_func('closed promise should not fulfill'), 1351 e => assert_equals(e, undefined, 'e should be undefined')); 1352 }); 1353}, 'abort with no arguments should set the stored error to undefined'); 1354 1355promise_test(t => { 1356 const ws = new WritableStream(); 1357 return ws.abort(undefined).then(() => { 1358 const writer = ws.getWriter(); 1359 return writer.closed.then(t.unreached_func('closed promise should not fulfill'), 1360 e => assert_equals(e, undefined, 'e should be undefined')); 1361 }); 1362}, 'abort with an undefined argument should set the stored error to undefined'); 1363 1364promise_test(t => { 1365 const ws = new WritableStream(); 1366 return ws.abort('string argument').then(() => { 1367 const writer = ws.getWriter(); 1368 return writer.closed.then(t.unreached_func('closed promise should not fulfill'), 1369 e => assert_equals(e, 'string argument', 'e should be \'string argument\'')); 1370 }); 1371}, 'abort with a string argument should set the stored error to that argument'); 1372 1373promise_test(t => { 1374 const ws = new WritableStream(); 1375 const writer = ws.getWriter(); 1376 return promise_rejects_js(t, TypeError, ws.abort(), 'abort should reject') 1377 .then(() => writer.ready); 1378}, 'abort on a locked stream should reject'); 1379 1380test(t => { 1381 let ctrl; 1382 const ws = new WritableStream({start(c) { ctrl = c; }}); 1383 const e = Error('hello'); 1384 1385 assert_true(ctrl.signal instanceof AbortSignal); 1386 assert_false(ctrl.signal.aborted); 1387 assert_equals(ctrl.signal.reason, undefined, 'signal.reason before abort'); 1388 ws.abort(e); 1389 assert_true(ctrl.signal.aborted); 1390 assert_equals(ctrl.signal.reason, e); 1391}, 'WritableStreamDefaultController.signal'); 1392 1393promise_test(async t => { 1394 let ctrl; 1395 let resolve; 1396 const called = new Promise(r => resolve = r); 1397 1398 const ws = new WritableStream({ 1399 start(c) { ctrl = c; }, 1400 write() { resolve(); return new Promise(() => {}); } 1401 }); 1402 const writer = ws.getWriter(); 1403 1404 writer.write(99); 1405 await called; 1406 1407 assert_false(ctrl.signal.aborted); 1408 assert_equals(ctrl.signal.reason, undefined, 'signal.reason before abort'); 1409 writer.abort(); 1410 assert_true(ctrl.signal.aborted); 1411 assert_true(ctrl.signal.reason instanceof DOMException, 'signal.reason is a DOMException'); 1412 assert_equals(ctrl.signal.reason.name, 'AbortError', 'signal.reason is an AbortError'); 1413}, 'the abort signal is signalled synchronously - write'); 1414 1415promise_test(async t => { 1416 let ctrl; 1417 let resolve; 1418 const called = new Promise(r => resolve = r); 1419 1420 const ws = new WritableStream({ 1421 start(c) { ctrl = c; }, 1422 close() { resolve(); return new Promise(() => {}); } 1423 }); 1424 const writer = ws.getWriter(); 1425 1426 writer.close(99); 1427 await called; 1428 1429 assert_false(ctrl.signal.aborted); 1430 writer.abort(); 1431 assert_true(ctrl.signal.aborted); 1432}, 'the abort signal is signalled synchronously - close'); 1433 1434promise_test(async t => { 1435 let ctrl; 1436 const ws = new WritableStream({start(c) { ctrl = c; }}); 1437 const writer = ws.getWriter(); 1438 1439 const e = TypeError(); 1440 ctrl.error(e); 1441 await promise_rejects_exactly(t, e, writer.closed); 1442 assert_false(ctrl.signal.aborted); 1443}, 'the abort signal is not signalled on error'); 1444 1445promise_test(async t => { 1446 let ctrl; 1447 const e = TypeError(); 1448 const ws = new WritableStream({ 1449 start(c) { ctrl = c; }, 1450 async write() { throw e; } 1451 }); 1452 const writer = ws.getWriter(); 1453 1454 await promise_rejects_exactly(t, e, writer.write('hello'), 'write result'); 1455 await promise_rejects_exactly(t, e, writer.closed, 'closed'); 1456 assert_false(ctrl.signal.aborted); 1457}, 'the abort signal is not signalled on write failure'); 1458 1459promise_test(async t => { 1460 let ctrl; 1461 const e = TypeError(); 1462 const ws = new WritableStream({ 1463 start(c) { ctrl = c; }, 1464 async close() { throw e; } 1465 }); 1466 const writer = ws.getWriter(); 1467 1468 await promise_rejects_exactly(t, e, writer.close(), 'close result'); 1469 await promise_rejects_exactly(t, e, writer.closed, 'closed'); 1470 assert_false(ctrl.signal.aborted); 1471}, 'the abort signal is not signalled on close failure'); 1472 1473promise_test(async t => { 1474 let ctrl; 1475 const e1 = SyntaxError(); 1476 const e2 = TypeError(); 1477 const ws = new WritableStream({ 1478 start(c) { ctrl = c; }, 1479 }); 1480 1481 const writer = ws.getWriter(); 1482 ctrl.signal.addEventListener('abort', () => writer.abort(e2)); 1483 writer.abort(e1); 1484 assert_true(ctrl.signal.aborted); 1485 1486 await promise_rejects_exactly(t, e2, writer.closed, 'closed'); 1487}, 'recursive abort() call'); 1488