@@ -133,8 +133,14 @@ test(() => {
133
133
134
134
// This tests that once `Observable.from()` detects a non-null and non-undefined
135
135
// `[Symbol.iterator]` property, we've committed to converting as an iterable.
136
- // If the value of that property is not callable, we don't silently move on to
137
- // the next conversion type — we throw a TypeError;
136
+ // If the value of that property is then not callable, we don't silently move on
137
+ // to the next conversion type — we throw a TypeError.
138
+ //
139
+ // That's because that's what TC39's `GetMethod()` [1] calls for, which is what
140
+ // `Observable.from()` first uses in the iterable conversion branch [2].
141
+ //
142
+ // [1]: https://tc39.es/ecma262/multipage/abstract-operations.html#sec-getmethod
143
+ // [2]: http://wicg.github.io/observable/#from-iterable-conversion
138
144
test ( ( ) => {
139
145
let results = [ ] ;
140
146
const iterable = {
@@ -149,11 +155,84 @@ test(() => {
149
155
}
150
156
151
157
assert_true ( errorThrown instanceof TypeError ) ;
152
- assert_equals ( errorThrown . message ,
153
- "Failed to execute 'from' on 'Observable': @@iterator must be a " +
154
- "callable." ) ;
155
158
} , "from(): [Symbol.iterator] not callable" ) ;
156
159
160
+ test ( ( ) => {
161
+ let results = [ ] ;
162
+ const iterable = {
163
+ calledOnce : false ,
164
+ get [ Symbol . iterator ] ( ) {
165
+ if ( this . calledOnce ) {
166
+ // Return a non-callable primitive the second time `@@iterator` is
167
+ // called.
168
+ return 10 ;
169
+ }
170
+
171
+ this . calledOnce = true ;
172
+ return this . validImplementation ;
173
+ } ,
174
+ validImplementation : ( ) => {
175
+ return {
176
+ next ( ) { return { done : true } ; }
177
+ }
178
+ }
179
+ } ;
180
+
181
+ let errorThrown = null ;
182
+
183
+ const observable = Observable . from ( iterable ) ;
184
+ observable . subscribe ( {
185
+ next : v => results . push ( "should not be called" ) ,
186
+ error : e => {
187
+ errorThrown = e ;
188
+ results . push ( e ) ;
189
+ } ,
190
+ } ) ;
191
+
192
+ assert_array_equals ( results , [ errorThrown ] ,
193
+ "An error was plumbed through the Observable" ) ;
194
+ assert_true ( errorThrown instanceof TypeError ) ;
195
+ } , "from(): [Symbol.iterator] not callable AFTER SUBSCRIBE throws" ) ;
196
+
197
+ test ( ( ) => {
198
+ let results = [ ] ;
199
+ const iterable = {
200
+ calledOnce : false ,
201
+ validImplementation : ( ) => {
202
+ return {
203
+ next ( ) { return { done : true } ; }
204
+ }
205
+ } ,
206
+ get [ Symbol . iterator ] ( ) {
207
+ if ( this . calledOnce ) {
208
+ // Return null the second time `@@iterator` is called.
209
+ return null ;
210
+ }
211
+
212
+ this . calledOnce = true ;
213
+ return this . validImplementation ;
214
+ }
215
+ } ;
216
+
217
+ let errorThrown = null ;
218
+
219
+ const observable = Observable . from ( iterable ) ;
220
+ observable . subscribe ( {
221
+ next : v => results . push ( "should not be called" ) ,
222
+ error : e => {
223
+ errorThrown = e ;
224
+ results . push ( e ) ;
225
+ } ,
226
+ } ) ;
227
+
228
+ assert_array_equals ( results , [ errorThrown ] ,
229
+ "An error was plumbed through the Observable" ) ;
230
+ assert_true ( errorThrown instanceof TypeError ) ;
231
+ assert_equals ( errorThrown . message ,
232
+ "Failed to execute 'subscribe' on 'Observable': @@iterator must not be " +
233
+ "undefined or null" ) ;
234
+ } , "from(): [Symbol.iterator] returns null AFTER SUBSCRIBE throws" ) ;
235
+
157
236
test ( ( ) => {
158
237
let results = [ ] ;
159
238
const customError = new Error ( "@@iterator override error" ) ;
@@ -520,24 +599,6 @@ test(() => {
520
599
} , "from(): Rethrows the error when Converting an object whose @@iterator " +
521
600
"method *getter* throws an error" ) ;
522
601
523
- test ( ( ) => {
524
- const obj = { } ;
525
- // Non-undefined & non-null values of the `@@iterator` property are not
526
- // allowed. Specifically they fail the the `IsCallable()` test, which fails
527
- // Observable conversion.
528
- obj [ Symbol . iterator ] = 10 ;
529
-
530
- try {
531
- Observable . from ( obj ) ;
532
- assert_unreached ( "from() conversion throws" ) ;
533
- } catch ( e ) {
534
- assert_true ( e instanceof TypeError ) ;
535
- assert_equals ( e . message ,
536
- "Failed to execute 'from' on 'Observable': @@iterator must be a callable." ) ;
537
- }
538
- } , "from(): Throws 'callable' error when @@iterator property is a " +
539
- "non-callable primitive" ) ;
540
-
541
602
// This test exercises the line of spec prose that says:
542
603
//
543
604
// "If |asyncIteratorMethodRecord|'s [[Value]] is undefined or null, then jump
0 commit comments