blob: bc4ac909c193977b1ea700d1361b6cdc4e60b274 [file] [log] [blame]
'use strict';
if (self.importScripts) {
self.importScripts('/resources/testharness.js');
self.importScripts('../resources/rs-utils.js');
self.importScripts('../resources/test-utils.js');
}
function duckTypedPassThroughTransform() {
let enqueueInReadable;
let closeReadable;
return {
writable: new WritableStream({
write(chunk) {
enqueueInReadable(chunk);
},
close() {
closeReadable();
}
}),
readable: new ReadableStream({
start(c) {
enqueueInReadable = c.enqueue.bind(c);
closeReadable = c.close.bind(c);
}
})
};
}
promise_test(() => {
const readableEnd = sequentialReadableStream(5).pipeThrough(duckTypedPassThroughTransform());
return readableStreamToArray(readableEnd).then(chunks =>
assert_array_equals(chunks, [1, 2, 3, 4, 5]), 'chunks should match');
}, 'Piping through a duck-typed pass-through transform stream should work');
promise_test(() => {
const transform = {
writable: new WritableStream({
start(c) {
c.error(new Error('this rejection should not be reported as unhandled'));
}
}),
readable: new ReadableStream()
};
sequentialReadableStream(5).pipeThrough(transform);
// The test harness should complain about unhandled rejections by then.
return flushAsyncEvents();
}, 'Piping through a transform errored on the writable end does not cause an unhandled promise rejection');
test(() => {
let calledWithArgs;
const dummy = {
pipeTo(...args) {
calledWithArgs = args;
// Does not return anything, testing the spec's guard against trying to mark [[PromiseIsHandled]] on undefined.
}
};
const fakeWritable = { fake: 'writable' };
const fakeReadable = { fake: 'readable' };
const arg2 = { arg: 'arg2' };
const arg3 = { arg: 'arg3' };
ReadableStream.prototype.pipeThrough.call(dummy, { writable: fakeWritable, readable: fakeReadable }, arg2, arg3);
assert_array_equals(calledWithArgs, [fakeWritable, arg2],
'The this value\'s pipeTo method should be called with the appropriate arguments');
}, 'pipeThrough generically calls pipeTo with the appropriate args');
test(() => {
const dummy = {
pipeTo() {
return { not: 'a promise' };
}
};
ReadableStream.prototype.pipeThrough.call(dummy, { });
// Test passes if this doesn't throw or crash.
}, 'pipeThrough can handle calling a pipeTo that returns a non-promise object');
test(() => {
const dummy = {
pipeTo() {
return {
then() {},
this: 'is not a real promise'
};
}
};
ReadableStream.prototype.pipeThrough.call(dummy, { });
// Test passes if this doesn't throw or crash.
}, 'pipeThrough can handle calling a pipeTo that returns a non-promise thenable object');
promise_test(() => {
const dummy = {
pipeTo() {
return Promise.reject(new Error('this rejection should not be reported as unhandled'));
}
};
ReadableStream.prototype.pipeThrough.call(dummy, { });
// The test harness should complain about unhandled rejections by then.
return flushAsyncEvents();
}, 'pipeThrough should mark a real promise from a fake readable as handled');
test(() => {
let thenCalled = false
let catchCalled = false;
const dummy = {
pipeTo() {
const fakePromise = Object.create(Promise.prototype);
fakePromise.then = () => {
thenCalled = true;
};
fakePromise.catch = () => {
catchCalled = true;
};
assert_true(fakePromise instanceof Promise, 'fakePromise fools instanceof');
return fakePromise;
}
};
// An incorrect implementation which uses an internal method to mark the promise as handled will throw or crash here.
ReadableStream.prototype.pipeThrough.call(dummy, { });
// An incorrect implementation that tries to mark the promise as handled by calling .then() or .catch() on the object
// will fail these tests.
assert_false(thenCalled, 'then should not be called');
assert_false(catchCalled, 'catch should not be called');
}, 'pipeThrough should not be fooled by an object whose instanceof Promise returns true');
done();