// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. import { unitTest, assert, assertEquals, assertThrows } from "./test_util.ts"; unitTest(function writableStreamDesiredSizeOnReleasedWriter() { const ws = new WritableStream(); const writer = ws.getWriter(); writer.releaseLock(); assertThrows(() => { writer.desiredSize; }, TypeError); }); unitTest(function writableStreamDesiredSizeInitialValue() { const ws = new WritableStream(); const writer = ws.getWriter(); assertEquals(writer.desiredSize, 1); }); unitTest(async function writableStreamDesiredSizeClosed() { const ws = new WritableStream(); const writer = ws.getWriter(); await writer.close(); assertEquals(writer.desiredSize, 0); }); unitTest(function writableStreamStartThrowsDesiredSizeNull() { const ws = new WritableStream({ start(c): void { c.error(); }, }); const writer = ws.getWriter(); assertEquals(writer.desiredSize, null, "desiredSize should be null"); }); unitTest(function getWriterOnClosingStream() { const ws = new WritableStream({}); const writer = ws.getWriter(); writer.close(); writer.releaseLock(); ws.getWriter(); }); unitTest(async function getWriterOnClosedStream() { const ws = new WritableStream({}); const writer = ws.getWriter(); await writer.close(); writer.releaseLock(); ws.getWriter(); }); unitTest(function getWriterOnAbortedStream() { const ws = new WritableStream({}); const writer = ws.getWriter(); writer.abort(); writer.releaseLock(); ws.getWriter(); }); unitTest(function getWriterOnErroredStream() { const ws = new WritableStream({ start(c): void { c.error(); }, }); const writer = ws.getWriter(); return writer.closed.then( (v) => { throw new Error(`writer.closed fulfilled unexpectedly with: ${v}`); }, () => { writer.releaseLock(); ws.getWriter(); }, ); }); unitTest(function closedAndReadyOnReleasedWriter() { const ws = new WritableStream({}); const writer = ws.getWriter(); writer.releaseLock(); return writer.closed.then( (v) => { throw new Error("writer.closed fulfilled unexpectedly with: " + v); }, (closedRejection) => { assertEquals( closedRejection.name, "TypeError", "closed promise should reject with a TypeError", ); return writer.ready.then( (v) => { throw new Error("writer.ready fulfilled unexpectedly with: " + v); }, (readyRejection) => assertEquals( readyRejection, closedRejection, "ready promise should reject with the same error", ), ); }, ); }); unitTest(function sinkMethodsCalledAsMethods() { let thisObject: Sink | null = null; // Calls to Sink methods after the first are implicitly ignored. Only the // first value that is passed to the resolver is used. class Sink { start(): void { assertEquals(this, thisObject, "start should be called as a method"); } write(): void { assertEquals(this, thisObject, "write should be called as a method"); } close(): void { assertEquals(this, thisObject, "close should be called as a method"); } abort(): void { assertEquals(this, thisObject, "abort should be called as a method"); } } const theSink = new Sink(); thisObject = theSink; const ws = new WritableStream(theSink); const writer = ws.getWriter(); writer.write("a"); const closePromise = writer.close(); const ws2 = new WritableStream(theSink); const writer2 = ws2.getWriter(); const abortPromise = writer2.abort(); return Promise.all([closePromise, abortPromise]).then(undefined); }); unitTest(function sizeShouldNotBeCalledAsMethod() { const strategy = { size(): number { if (this !== undefined) { throw new Error("size called as a method"); } return 1; }, }; const ws = new WritableStream({}, strategy); const writer = ws.getWriter(); return writer.write("a"); }); unitTest(function redundantReleaseLockIsNoOp() { const ws = new WritableStream(); const writer1 = ws.getWriter(); assertEquals( undefined, writer1.releaseLock(), "releaseLock() should return undefined", ); const writer2 = ws.getWriter(); assertEquals( undefined, writer1.releaseLock(), "no-op releaseLock() should return undefined", ); // Calling releaseLock() on writer1 should not interfere with writer2. If it did, then the ready promise would be // rejected. return writer2.ready; }); unitTest(function readyPromiseShouldFireBeforeReleaseLock() { const events: string[] = []; const ws = new WritableStream(); const writer = ws.getWriter(); return writer.ready.then(() => { // Force the ready promise back to a pending state. const writerPromise = writer.write("dummy"); const readyPromise = writer.ready.catch(() => events.push("ready")); const closedPromise = writer.closed.catch(() => events.push("closed")); writer.releaseLock(); return Promise.all([readyPromise, closedPromise]).then(() => { assertEquals( events, ["ready", "closed"], "ready promise should fire before closed promise", ); // Stop the writer promise hanging around after the test has finished. return Promise.all([writerPromise, ws.abort()]).then(undefined); }); }); }); unitTest(function subclassingWritableStream() { class Subclass extends WritableStream { extraFunction(): boolean { return true; } } assert( Object.getPrototypeOf(Subclass.prototype) === WritableStream.prototype, "Subclass.prototype's prototype should be WritableStream.prototype", ); assert( Object.getPrototypeOf(Subclass) === WritableStream, "Subclass's prototype should be WritableStream", ); const sub = new Subclass(); assert( sub instanceof WritableStream, "Subclass object should be an instance of WritableStream", ); assert( sub instanceof Subclass, "Subclass object should be an instance of Subclass", ); const lockedGetter = Object.getOwnPropertyDescriptor( WritableStream.prototype, "locked", )!.get!; assert( lockedGetter.call(sub) === sub.locked, "Subclass object should pass brand check", ); assert( sub.extraFunction(), "extraFunction() should be present on Subclass object", ); }); unitTest(function lockedGetterShouldReturnTrue() { const ws = new WritableStream(); assert(!ws.locked, "stream should not be locked"); ws.getWriter(); assert(ws.locked, "stream should be locked"); });