@@ -133,8 +133,14 @@ test(() => {
133133
134134// This tests that once `Observable.from()` detects a non-null and non-undefined
135135// `[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
138144test ( ( ) => {
139145 let results = [ ] ;
140146 const iterable = {
@@ -149,11 +155,84 @@ test(() => {
149155 }
150156
151157 assert_true ( errorThrown instanceof TypeError ) ;
152- assert_equals ( errorThrown . message ,
153- "Failed to execute 'from' on 'Observable': @@iterator must be a " +
154- "callable." ) ;
155158} , "from(): [Symbol.iterator] not callable" ) ;
156159
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+
157236test ( ( ) => {
158237 let results = [ ] ;
159238 const customError = new Error ( "@@iterator override error" ) ;
@@ -520,24 +599,6 @@ test(() => {
520599} , "from(): Rethrows the error when Converting an object whose @@iterator " +
521600 "method *getter* throws an error" ) ;
522601
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-
541602// This test exercises the line of spec prose that says:
542603//
543604// "If |asyncIteratorMethodRecord|'s [[Value]] is undefined or null, then jump
0 commit comments