• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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