1// META: global=window,worker 2'use strict'; 3 4test(() => { 5 const ws = new WritableStream({}); 6 const writer = ws.getWriter(); 7 writer.releaseLock(); 8 9 assert_throws_js(TypeError, () => writer.desiredSize, 'desiredSize should throw a TypeError'); 10}, 'desiredSize on a released writer'); 11 12test(() => { 13 const ws = new WritableStream({}); 14 15 const writer = ws.getWriter(); 16 17 assert_equals(writer.desiredSize, 1, 'desiredSize should be 1'); 18}, 'desiredSize initial value'); 19 20promise_test(() => { 21 const ws = new WritableStream({}); 22 23 const writer = ws.getWriter(); 24 25 writer.close(); 26 27 return writer.closed.then(() => { 28 assert_equals(writer.desiredSize, 0, 'desiredSize should be 0'); 29 }); 30}, 'desiredSize on a writer for a closed stream'); 31 32test(() => { 33 const ws = new WritableStream({ 34 start(c) { 35 c.error(); 36 } 37 }); 38 39 const writer = ws.getWriter(); 40 assert_equals(writer.desiredSize, null, 'desiredSize should be null'); 41}, 'desiredSize on a writer for an errored stream'); 42 43test(() => { 44 const ws = new WritableStream({}); 45 46 const writer = ws.getWriter(); 47 writer.close(); 48 writer.releaseLock(); 49 50 ws.getWriter(); 51}, 'ws.getWriter() on a closing WritableStream'); 52 53promise_test(() => { 54 const ws = new WritableStream({}); 55 56 const writer = ws.getWriter(); 57 return writer.close().then(() => { 58 writer.releaseLock(); 59 60 ws.getWriter(); 61 }); 62}, 'ws.getWriter() on a closed WritableStream'); 63 64test(() => { 65 const ws = new WritableStream({}); 66 67 const writer = ws.getWriter(); 68 writer.abort(); 69 writer.releaseLock(); 70 71 ws.getWriter(); 72}, 'ws.getWriter() on an aborted WritableStream'); 73 74promise_test(() => { 75 const ws = new WritableStream({ 76 start(c) { 77 c.error(); 78 } 79 }); 80 81 const writer = ws.getWriter(); 82 return writer.closed.then( 83 v => assert_unreached('writer.closed fulfilled unexpectedly with: ' + v), 84 () => { 85 writer.releaseLock(); 86 87 ws.getWriter(); 88 } 89 ); 90}, 'ws.getWriter() on an errored WritableStream'); 91 92promise_test(() => { 93 const ws = new WritableStream({}); 94 95 const writer = ws.getWriter(); 96 writer.releaseLock(); 97 98 return writer.closed.then( 99 v => assert_unreached('writer.closed fulfilled unexpectedly with: ' + v), 100 closedRejection => { 101 assert_equals(closedRejection.name, 'TypeError', 'closed promise should reject with a TypeError'); 102 return writer.ready.then( 103 v => assert_unreached('writer.ready fulfilled unexpectedly with: ' + v), 104 readyRejection => assert_equals(readyRejection, closedRejection, 105 'ready promise should reject with the same error') 106 ); 107 } 108 ); 109}, 'closed and ready on a released writer'); 110 111promise_test(t => { 112 let thisObject = null; 113 // Calls to Sink methods after the first are implicitly ignored. Only the first value that is passed to the resolver 114 // is used. 115 class Sink { 116 start() { 117 // Called twice 118 t.step(() => { 119 assert_equals(this, thisObject, 'start should be called as a method'); 120 }); 121 } 122 123 write() { 124 t.step(() => { 125 assert_equals(this, thisObject, 'write should be called as a method'); 126 }); 127 } 128 129 close() { 130 t.step(() => { 131 assert_equals(this, thisObject, 'close should be called as a method'); 132 }); 133 } 134 135 abort() { 136 t.step(() => { 137 assert_equals(this, thisObject, 'abort should be called as a method'); 138 }); 139 } 140 } 141 142 const theSink = new Sink(); 143 thisObject = theSink; 144 const ws = new WritableStream(theSink); 145 146 const writer = ws.getWriter(); 147 148 writer.write('a'); 149 const closePromise = writer.close(); 150 151 const ws2 = new WritableStream(theSink); 152 const writer2 = ws2.getWriter(); 153 const abortPromise = writer2.abort(); 154 155 return Promise.all([ 156 closePromise, 157 abortPromise 158 ]); 159}, 'WritableStream should call underlying sink methods as methods'); 160 161promise_test(t => { 162 function functionWithOverloads() {} 163 functionWithOverloads.apply = t.unreached_func('apply() should not be called'); 164 functionWithOverloads.call = t.unreached_func('call() should not be called'); 165 const underlyingSink = { 166 start: functionWithOverloads, 167 write: functionWithOverloads, 168 close: functionWithOverloads, 169 abort: functionWithOverloads 170 }; 171 // Test start(), write(), close(). 172 const ws1 = new WritableStream(underlyingSink); 173 const writer1 = ws1.getWriter(); 174 writer1.write('a'); 175 writer1.close(); 176 177 // Test abort(). 178 const abortError = new Error(); 179 abortError.name = 'abort error'; 180 181 const ws2 = new WritableStream(underlyingSink); 182 const writer2 = ws2.getWriter(); 183 writer2.abort(abortError); 184 185 // Test abort() with a close underlying sink method present. (Historical; see 186 // https://github.com/whatwg/streams/issues/620#issuecomment-263483953 for what used to be 187 // tested here. But more coverage can't hurt.) 188 const ws3 = new WritableStream({ 189 start: functionWithOverloads, 190 write: functionWithOverloads, 191 close: functionWithOverloads 192 }); 193 const writer3 = ws3.getWriter(); 194 writer3.abort(abortError); 195 196 return writer1.closed 197 .then(() => promise_rejects_exactly(t, abortError, writer2.closed, 'writer2.closed should be rejected')) 198 .then(() => promise_rejects_exactly(t, abortError, writer3.closed, 'writer3.closed should be rejected')); 199}, 'methods should not not have .apply() or .call() called'); 200 201promise_test(() => { 202 const strategy = { 203 size() { 204 if (this !== undefined) { 205 throw new Error('size called as a method'); 206 } 207 return 1; 208 } 209 }; 210 211 const ws = new WritableStream({}, strategy); 212 const writer = ws.getWriter(); 213 return writer.write('a'); 214}, 'WritableStream\'s strategy.size should not be called as a method'); 215 216promise_test(() => { 217 const ws = new WritableStream(); 218 const writer1 = ws.getWriter(); 219 assert_equals(undefined, writer1.releaseLock(), 'releaseLock() should return undefined'); 220 const writer2 = ws.getWriter(); 221 assert_equals(undefined, writer1.releaseLock(), 'no-op releaseLock() should return undefined'); 222 // Calling releaseLock() on writer1 should not interfere with writer2. If it did, then the ready promise would be 223 // rejected. 224 return writer2.ready; 225}, 'redundant releaseLock() is no-op'); 226 227promise_test(() => { 228 const events = []; 229 const ws = new WritableStream(); 230 const writer = ws.getWriter(); 231 return writer.ready.then(() => { 232 // Force the ready promise back to a pending state. 233 const writerPromise = writer.write('dummy'); 234 const readyPromise = writer.ready.catch(() => events.push('ready')); 235 const closedPromise = writer.closed.catch(() => events.push('closed')); 236 writer.releaseLock(); 237 return Promise.all([readyPromise, closedPromise]).then(() => { 238 assert_array_equals(events, ['ready', 'closed'], 'ready promise should fire before closed promise'); 239 // Stop the writer promise hanging around after the test has finished. 240 return Promise.all([ 241 writerPromise, 242 ws.abort() 243 ]); 244 }); 245 }); 246}, 'ready promise should fire before closed on releaseLock'); 247 248test(() => { 249 class Subclass extends WritableStream { 250 extraFunction() { 251 return true; 252 } 253 } 254 assert_equals( 255 Object.getPrototypeOf(Subclass.prototype), WritableStream.prototype, 256 'Subclass.prototype\'s prototype should be WritableStream.prototype'); 257 assert_equals(Object.getPrototypeOf(Subclass), WritableStream, 258 'Subclass\'s prototype should be WritableStream'); 259 const sub = new Subclass(); 260 assert_true(sub instanceof WritableStream, 261 'Subclass object should be an instance of WritableStream'); 262 assert_true(sub instanceof Subclass, 263 'Subclass object should be an instance of Subclass'); 264 const lockedGetter = Object.getOwnPropertyDescriptor( 265 WritableStream.prototype, 'locked').get; 266 assert_equals(lockedGetter.call(sub), sub.locked, 267 'Subclass object should pass brand check'); 268 assert_true(sub.extraFunction(), 269 'extraFunction() should be present on Subclass object'); 270}, 'Subclassing WritableStream should work'); 271 272test(() => { 273 const ws = new WritableStream(); 274 assert_false(ws.locked, 'stream should not be locked'); 275 ws.getWriter(); 276 assert_true(ws.locked, 'stream should be locked'); 277}, 'the locked getter should return true if the stream has a writer'); 278