deno/cli/tests/unit/streams_writable_test.ts

254 lines
6.5 KiB
TypeScript
Raw Normal View History

// 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");
});