From 0e3d56e71779590a6bb7d43f3f0aaf78d9638aea Mon Sep 17 00:00:00 2001 From: Marco Castelluccio Date: Fri, 6 Sep 2024 08:27:52 +0000 Subject: [PATCH] Bug 1915764 [wpt PR 47881] - DOM: Fix `Observable#from()` [Symbol.iterator] semantics (1/2), a=testonly Automatic update from web-platform-tests DOM: Fix `Observable#from()` [Symbol.iterator] semantics (1/2) See https://github.com/WICG/observable/pull/160, which specs the `Observable#from()` semantics, matching these tests. See also https://crbug.com/363015168 which describes the ways in which our current implementation of `Observable#from()`'s detection semantics are overbroad. This CL makes the implementation of the "detection semantics" match the desired behavior outlined in that issue, and adds a bunch of tests. R=masonfchromium.org Bug: 363015168, 40282760 Change-Id: Id6cfdd45c44286b298e107635e4283b018f50aaf Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5824955 Commit-Queue: Dominic Farolino Reviewed-by: Mason Freed Cr-Commit-Position: refs/heads/main{#1349019} -- wpt-commits: e74098ed10aa0a4fc6ad6b8d6e354895addd00d1 wpt-pr: 47881 UltraBlame original commit: ef2aac8ce3d8e023f93a55c7593551be16b64d72 --- .../tentative/observable-from.any.js | 3688 ++++++++++++----- 1 file changed, 2560 insertions(+), 1128 deletions(-) diff --git a/testing/web-platform/tests/dom/observable/tentative/observable-from.any.js b/testing/web-platform/tests/dom/observable/tentative/observable-from.any.js index 58c4cdd2a7e0..890b71f5f835 100644 --- a/testing/web-platform/tests/dom/observable/tentative/observable-from.any.js +++ b/testing/web-platform/tests/dom/observable/tentative/observable-from.any.js @@ -688,71 +688,156 @@ Observable ; / / -The -result +This +test +and +the +variants +below +it +test +the +web +- +observable +side +- +effects of +/ +/ +converting +an +iterable +object +to +an +Observable +. +Specifically +it +tracks +/ +/ +exactly +when the +% +Symbol +. iterator +% method -of +is +* +retrieved +* +from the -converted object -is -called -: / / -1 +invoked +and +what +its +error +- +throwing +side +- +effects +are . -Once -on -conversion -( -to -test +/ +/ +/ +/ +Even +more +specifically +we +assert that the -value -is -an -iterable -) +% +Symbol . +iterator +% +method +is / / -2 -. -Once -on -subscription +retrieved +a +single +time +when +converting to -re -- -pull -the -iterator -implementation -from -the +an +Observable +and +then +again +when / / -raw -JS -object -that +subscribing +to the +converted Observable -owns -once -synchronous -iteration -is +. +This +makes +it +possible +for +the / / -about +% +Symbol +. +iterator +% +method +getter to -begin +change +return +values +in +between +conversion +/ +/ +and +subscription +. +See +https +: +/ +/ +github +. +com +/ +WICG +/ +observable +/ +issues +/ +127 +for +/ +/ +related +discussion . test ( @@ -761,20 +846,17 @@ test = > { -let -numTimesSymbolIteratorCalled -= -0 -; -let -numTimesNextCalled +const +results = -0 +[ +] ; const iterable = { +get [ Symbol . @@ -783,19 +865,76 @@ iterator ( ) { -numTimesSymbolIteratorCalled -+ -+ +results +. +push +( +" +[ +Symbol +. +iterator +] +method +GETTER +" +) +; +return +function +( +) +{ +results +. +push +( +" +[ +Symbol +. +iterator +implementation +] +" +) ; return { +get next ( ) { -numTimesNextCalled -+ -+ +results +. +push +( +" +next +( +) +method +GETTER +" +) +; +return +function +( +) +{ +results +. +push +( +" +next +( +) +implementation +" +) ; return { @@ -808,6 +947,10 @@ true } ; } +; +} +} +; } ; } @@ -823,237 +966,77 @@ from iterable ) ; -assert_equals +assert_array_equals ( -numTimesSymbolIteratorCalled -1 +results +[ " -Observable +[ +Symbol . -from -( -iterable -) -invokes -the iterator +] method -getter -once +GETTER " +] ) ; -assert_equals -( -numTimesNextCalled -0 -" -Iterator -next +let +thrownError += +null +; +observable +. +subscribe ( ) -is -not -called -until -subscription -" -) ; -/ -/ -Override -iterable -' -s +assert_array_equals +( +results [ -Symbol +" +[ +Symbol . iterator ] -protocol -with -an -error -- -throwing -/ -/ -function -. -We -assert -that -on -subscription -this method -( -the -new -iterator -/ -/ -implementation -) -is -called -because -only -the -raw -JS -object -gets -stored -in -/ -/ -the -Observable -that -results -in -conversion -. -This -raw -value -must -get -/ -/ -re -- -converted -to -an -iterable -once -iteration -is -about -to -start +GETTER +" +" +[ +Symbol . -const -customError -= -new -Error -( -' iterator -override -error -' -) -; -iterable +] +method +GETTER +" +" [ Symbol . iterator +implementation ] -= -( -) -= -> -{ -throw -customError -; -} -; -let -thrownError -= -null -; -observable -. -subscribe -( -{ -error -: -e -= -> -thrownError -= -e -} -) -; -assert_equals -( -thrownError -customError " -Error -thrown -from +" next ( ) -is -passed -to -the -error -( -) -handler -" -) -; -assert_equals -( -numTimesSymbolIteratorCalled -1 -" -Subscription -re -- -invokes -iterator -method -which -now -is -a -different -" -+ -" method -that -does -* -not -* -increment -our -assertion -value +GETTER " -) -; -assert_equals -( -numTimesNextCalled -0 " -Iterator next ( ) -is -never -called +implementation " +] ) ; } @@ -1079,16 +1062,72 @@ observable ; / / -Similar +This +tests +that +once +Observable +. +from +( +) +detects +a +non +- +null +and +non +- +undefined +/ +/ +[ +Symbol +. +iterator +] +property +we +' +ve +committed to +converting +as +an +iterable +. +/ +/ +If the -above -test -but -with -more -Observables -! +value +of +that +property +is +not +callable +we +don +' +t +silently +move +on +to +/ +/ +the +next +conversion +type +we +throw +a +TypeError +; test ( ( @@ -1097,14 +1136,10 @@ test > { let -numTimesSymbolIteratorCalled -= -0 -; -let -numTimesNextCalled +results = -0 +[ +] ; const iterable @@ -1115,324 +1150,427 @@ Symbol . iterator ] +: +10 +} +; +let +errorThrown += +null +; +try +{ +Observable +. +from ( +iterable ) -{ -numTimesSymbolIteratorCalled -+ -+ ; -return -{ -next +} +catch ( +e ) { -numTimesNextCalled -+ -+ -; -return -{ -value -: -undefined -done -: -true -} -; -} -} +errorThrown += +e ; } -} -; -const -obs1 -= -Observable -. -from +assert_true ( -iterable +errorThrown +instanceof +TypeError ) ; -const -obs2 -= -Observable -. -from +assert_equals ( -iterable -) -; -const -obs3 -= -Observable +errorThrown . +message +" +Failed +to +execute +' from -( -iterable -) -; -const -obs4 -= +' +on +' Observable -. -from -( -obs3 -) -; -assert_equals -( -numTimesSymbolIteratorCalled -3 +' +: +iterator +must +be +a " -Observable ++ +" +callable . -from -( -iterable -) -invokes -the -iterator -method -getter -once " ) ; -assert_equals -( -numTimesNextCalled -0 +} " -Iterator -next +from ( ) -is -not -called -until -subscription -" -) -; -iterable +: [ Symbol . iterator ] -= +not +callable +" +) +; +test +( ( ) = > { -throw +let +results += +[ +] +; +const +customError += new Error ( -' -Symbol -. +" iterator override error -' +" ) ; -} -; -let -errorCount -= -0 -; const -observer +iterable = { -error +numTimesCalled : -e -= -> -errorCount -+ -+ -} -; -obs1 +0 +/ +/ +The +first +time +this +getter +is +called +it +returns +a +legitimate +function +/ +/ +that +when +called +returns +an +iterator . -subscribe +Every +other +time +it +returns +an +/ +/ +error +- +throwing +function +that +does +not +return +an +iterator +. +get +[ +Symbol +. +iterator +] ( -observer ) +{ +this +. +numTimesCalled ++ ++ ; -obs2 +results . -subscribe +push ( -observer +" +[ +Symbol +. +iterator +] +method +GETTER +" ) ; -obs3 -. -subscribe +if ( -observer +this +. +numTimesCalled += += += +1 ) +{ +return +this +. +validIteratorImplementation ; -obs4 +} +else +{ +return +this . -subscribe +errorThrowingIteratorImplementation +; +} +} +validIteratorImplementation +: +function ( -observer ) -; -assert_equals +{ +results +. +push ( -errorCount -4 " -Error -- -throwing +[ +Symbol +. iterator implementation -is -called -once -per -" -+ -" -subscription +] " ) ; -assert_equals +return +{ +get +next +( +) +{ +results +. +push ( -numTimesSymbolIteratorCalled -3 " -Subscription -re -- -invokes -the -iterator +next +( +) method -getter -once +GETTER " ) ; -assert_equals +return +function +( +) +{ +results +. +push ( -numTimesNextCalled -0 " -Iterator next ( ) -is -never -called +implementation " ) ; +return +{ +value +: +undefined +done +: +true +} +; } -" -from +} +} +; +} +errorThrowingIteratorImplementation +: +function ( ) -: +{ +results +. +push +( +" +Error +- +throwing [ Symbol . iterator ] -side -- -effects -( -many -observables -) +implementation " ) ; -test -( -( -) -= -> -{ -const +throw customError +; +} +} +; +const +observable = -new -Error -( -' -iterator -next +Observable +. +from ( +iterable ) -error -' +; +assert_array_equals +( +results +[ +" +[ +Symbol +. +iterator +] +method +GETTER +" +] ) ; -const +/ +/ +Override iterable -= -{ +' +s [ Symbol . iterator ] +protocol +with +an +error +- +throwing +/ +/ +function +. +We +assert +that +on +subscription +this +method ( +the +new +iterator +/ +/ +implementation ) -{ -return -{ -next -( -) -{ -throw -customError -; -} -} -; -} -} -; +is +called +because +only +the +raw +JS +object +gets +stored +in +/ +/ +the +Observable +that +results +in +conversion +. +This +raw +value +must +get +/ +/ +re +- +converted +to +an +iterable +once +iteration +is +about +to +start +. let thrownError = null ; -Observable -. -from -( -iterable -) +observable . subscribe ( @@ -1470,6 +1608,50 @@ handler " ) ; +assert_array_equals +( +results +[ +/ +/ +Old +: +" +[ +Symbol +. +iterator +] +method +GETTER +" +/ +/ +New +: +" +[ +Symbol +. +iterator +] +method +GETTER +" +" +Error +- +throwing +[ +Symbol +. +iterator +] +implementation +" +] +) +; } " from @@ -1481,648 +1663,1688 @@ Symbol . iterator ] -next -( -) -throws -error +is +not +cached " ) ; -promise_test +/ +/ +Similar +to +the +above +test +but +with +more +Observables +! +test ( -async ( ) = > { const -promise +results = -Promise -. -resolve -( -' -value -' -) +[ +] +; +let +numTimesSymbolIteratorCalled += +0 +; +let +numTimesNextCalled += +0 ; const -observable +iterable = -Observable +{ +get +[ +Symbol . -from +iterator +] ( -promise ) -; -assert_true +{ +results +. +push ( -observable -instanceof -Observable " -Converts -to -Observable +[ +Symbol +. +iterator +] +method +GETTER " ) ; -const -results -= -[ -] +return +this +. +internalIteratorImplementation ; -observable +} +set +[ +Symbol . -subscribe +iterator +] ( +func +) { -next +this +. +internalIteratorImplementation += +func +; +} +internalIteratorImplementation : +function ( -value ) -= -> +{ results . push ( -value +" +[ +Symbol +. +iterator +] +implementation +" ) -error -: +; +return +{ +get +next ( ) -= -> -assert_unreached +{ +results +. +push ( " -error +next ( ) -is -not -called +method +GETTER " ) -complete -: +; +return +function ( ) -= -> +{ results . push ( " -complete +next ( ) +implementation " ) +; +return +{ +value +: +undefined +done +: +true +} +; +} +; +} } +; +} +} +; +const +obs1 += +Observable +. +from +( +iterable ) ; -assert_array_equals +const +obs2 += +Observable +. +from ( -results -[ -] -" +iterable +) +; +const +obs3 += Observable -does -not -emit -synchronously -" +. +from +( +iterable ) ; -await -promise +const +obs4 += +Observable +. +from +( +obs3 +) +; +assert_equals +( +obs3 +obs4 +) ; assert_array_equals ( results [ " -value -" +[ +Symbol +. +iterator +] +method +GETTER " -complete -( -) " +[ +Symbol +. +iterator ] +method +GETTER " -Observable -emits -and -completes -after -Promise -resolves " +[ +Symbol +. +iterator +] +method +GETTER +" +] ) ; -} -" -from +obs1 +. +subscribe ( ) -: -Converts -Promise -to -Observable -" -) ; -promise_test +assert_array_equals ( -async -t -= -> -{ -let -unhandledRejectionHandlerCalled -= -false -; -const -unhandledRejectionHandler -= -( -) -= -> -{ -unhandledRejectionHandlerCalled -= -true -; -} -; -self +results +[ +/ +/ +Old +: +" +[ +Symbol . -addEventListener -( +iterator +] +method +GETTER " -unhandledrejection " -unhandledRejectionHandler -) -; -t -. -add_cleanup -( -( -) -= -> -self +[ +Symbol . -removeEventListener -( +iterator +] +method +GETTER " -unhandledrejection " -unhandledRejectionHandler -) -) -; -const -promise -= -Promise +[ +Symbol . -reject -( +iterator +] +method +GETTER " -reason +/ +/ +New +: " -) -; -const -observable -= -Observable +[ +Symbol . -from -( -promise -) -; -assert_true -( -observable -instanceof -Observable +iterator +] +method +GETTER " -Converts -to -Observable " -) -; -const -results -= [ -] -; -observable +Symbol . -subscribe -( -{ +iterator +] +implementation +" +" next -: ( -value ) -= -> -assert_unreached -( +method +GETTER +" " next ( ) -not -called +implementation " +] ) -error -: +; +iterable +[ +Symbol +. +iterator +] += ( -error ) = > +{ results . push ( -error -) -complete -: -( -) -= -> -assert_unreached -( " -complete -( -) -not -called +Error +- +throwing +[ +Symbol +. +iterator +] +implementation " ) -} -) ; -assert_array_equals +throw +new +Error ( -results -[ -] -" -Observable -does -not -emit -synchronously -" +' +Symbol +. +iterator +override +error +' ) ; +} +; let -catchBlockEntered +errorCount = -false -; -try -{ -await -promise +0 ; -} -catch +const +observer += { -catchBlockEntered +error +: +e = -true -; +> +errorCount ++ ++ } -assert_true +; +obs2 +. +subscribe ( -catchBlockEntered -" -Catch -block -entered -" +observer ) ; -assert_false +obs3 +. +subscribe ( -unhandledRejectionHandlerCalled -" -No -unhandledrejection -event -" +observer ) ; -assert_array_equals -( -results -[ -" -reason -" -] -" -Observable -emits -error +obs4 +. +subscribe ( +observer ) -after -Promise -rejects -" +; +assert_equals +( +errorCount +3 +" +Error +- +throwing +iterator +implementation +is +called +once +per +" ++ +" +subscription +" +) +; +assert_array_equals +( +results +[ +/ +/ +Old +: +" +[ +Symbol +. +iterator +] +method +GETTER +" +" +[ +Symbol +. +iterator +] +method +GETTER +" +" +[ +Symbol +. +iterator +] +method +GETTER +" +" +[ +Symbol +. +iterator +] +method +GETTER +" +" +[ +Symbol +. +iterator +] +implementation +" +" +next +( +) +method +GETTER +" +" +next +( +) +implementation +" +/ +/ +New +: +" +[ +Symbol +. +iterator +] +method +GETTER +" +" +Error +- +throwing +[ +Symbol +. +iterator +] +implementation +" +" +[ +Symbol +. +iterator +] +method +GETTER +" +" +Error +- +throwing +[ +Symbol +. +iterator +] +implementation +" +" +[ +Symbol +. +iterator +] +method +GETTER +" +" +Error +- +throwing +[ +Symbol +. +iterator +] +implementation +" +] +) +; +} +" +from +( +) +: +[ +Symbol +. +iterator +] +side +- +effects +( +many +observables +) +" +) +; +test +( +( +) += +> +{ +const +customError += +new +Error +( +' +iterator +next +( +) +error +' +) +; +const +iterable += +{ +[ +Symbol +. +iterator +] +( +) +{ +return +{ +next +( +) +{ +throw +customError +; +} +} +; +} +} +; +let +thrownError += +null +; +Observable +. +from +( +iterable +) +. +subscribe +( +{ +error +: +e += +> +thrownError += +e +} +) +; +assert_equals +( +thrownError +customError +" +Error +thrown +from +next +( +) +is +passed +to +the +error +( +) +handler +" +) +; +} +" +from +( +) +: +[ +Symbol +. +iterator +] +next +( +) +throws +error +" +) +; +promise_test +( +async +( +) += +> +{ +const +promise += +Promise +. +resolve +( +' +value +' +) +; +const +observable += +Observable +. +from +( +promise +) +; +assert_true +( +observable +instanceof +Observable +" +Converts +to +Observable +" +) +; +const +results += +[ +] +; +observable +. +subscribe +( +{ +next +: +( +value +) += +> +results +. +push +( +value +) +error +: +( +) += +> +assert_unreached +( +" +error +( +) +is +not +called +" +) +complete +: +( +) += +> +results +. +push +( +" +complete +( +) +" +) +} +) +; +assert_array_equals +( +results +[ +] +" +Observable +does +not +emit +synchronously +" +) +; +await +promise +; +assert_array_equals +( +results +[ +" +value +" +" +complete +( +) +" +] +" +Observable +emits +and +completes +after +Promise +resolves +" +) +; +} +" +from +( +) +: +Converts +Promise +to +Observable +" +) +; +promise_test +( +async +t += +> +{ +let +unhandledRejectionHandlerCalled += +false +; +const +unhandledRejectionHandler += +( +) += +> +{ +unhandledRejectionHandlerCalled += +true +; +} +; +self +. +addEventListener +( +" +unhandledrejection +" +unhandledRejectionHandler +) +; +t +. +add_cleanup +( +( +) += +> +self +. +removeEventListener +( +" +unhandledrejection +" +unhandledRejectionHandler +) +) +; +const +promise += +Promise +. +reject +( +" +reason +" +) +; +const +observable += +Observable +. +from +( +promise +) +; +assert_true +( +observable +instanceof +Observable +" +Converts +to +Observable +" +) +; +const +results += +[ +] +; +observable +. +subscribe +( +{ +next +: +( +value +) += +> +assert_unreached +( +" +next +( +) +not +called +" +) +error +: +( +error +) += +> +results +. +push +( +error +) +complete +: +( +) += +> +assert_unreached +( +" +complete +( +) +not +called +" +) +} +) +; +assert_array_equals +( +results +[ +] +" +Observable +does +not +emit +synchronously +" +) +; +let +catchBlockEntered += +false +; +try +{ +await +promise +; +} +catch +{ +catchBlockEntered += +true +; +} +assert_true +( +catchBlockEntered +" +Catch +block +entered +" +) +; +assert_false +( +unhandledRejectionHandlerCalled +" +No +unhandledrejection +event +" +) +; +assert_array_equals +( +results +[ +" +reason +" +] +" +Observable +emits +error +( +) +after +Promise +rejects +" +) +; +} +" +from +( +) +: +Converts +rejected +Promise +to +Observable +. +No +" ++ +" +unhandledrejection +event +when +error +is +handled +by +subscription +" +) +; +promise_test +( +async +t += +> +{ +let +unhandledRejectionHandlerCalled += +false +; +const +unhandledRejectionHandler += +( ) += +> +{ +unhandledRejectionHandlerCalled += +true ; } +; +self +. +addEventListener +( " -from +unhandledrejection +" +unhandledRejectionHandler +) +; +t +. +add_cleanup +( +( +) += +> +self +. +removeEventListener ( +" +unhandledrejection +" +unhandledRejectionHandler +) ) +; +let +errorReported += +null +; +self +. +addEventListener +( +" +error +" +e += +> +errorReported += +e +{ +once : -Converts -rejected +true +} +) +; +let +catchBlockEntered += +false +; +try +{ +const +promise += Promise +. +reject +( +" +custom +reason +" +) +; +const +observable += +Observable +. +from +( +promise +) +; +observable +. +subscribe +( +) +; +await +promise +; +} +catch +{ +catchBlockEntered += +true +; +} +assert_true +( +catchBlockEntered +" +Catch +block +entered +" +) +; +assert_false +( +unhandledRejectionHandlerCalled +" +No +unhandledrejection +event +because +error +got +reported +to +global +" +) +; +assert_not_equals +( +errorReported +null +" +Error +was +reported +to +the +global +" +) +; +assert_true +( +errorReported +. +message +. +includes +( +" +custom +reason +" +) +" +Error +message +matches +" +) +; +assert_equals +( +errorReported +. +lineno +0 +" +Error +lineno +is +0 +" +) +; +assert_equals +( +errorReported +. +colno +0 +" +Error +lineno +is +0 +" +) +; +assert_equals +( +errorReported +. +error +" +custom +reason +" +" +Error +object +is +equivalent +" +) +; +} +" +from +( +) +: +Rejections +not +handled +by +subscription +are +reported to -Observable -. -No +the " + " +global +and +still +not +sent +as +an unhandledrejection event -when -error -is -handled -by -subscription " ) ; -promise_test +test ( -async -t +( +) = > { -let -unhandledRejectionHandlerCalled +const +results = -false +[ +] ; const -unhandledRejectionHandler +observable = +new +Observable ( -) +subscriber = > { -unhandledRejectionHandlerCalled +subscriber +. +next +( +' +from +Observable +' +) +; +subscriber +. +complete +( +) +; +} +) +; +observable +[ +Symbol +. +iterator +] += +( +) = +> +{ +results +. +push +( +' +Symbol +. +iterator +( +) +called +' +) +; +return +{ +next +( +) +{ +return +{ +value +: +' +from +iterator +' +done +: true +} +; +} +} ; } ; -self +Observable . -addEventListener +from ( -" -unhandledrejection -" -unhandledRejectionHandler +observable ) -; -t . -add_cleanup +subscribe +( +{ +next +: +v += +> +results +. +push ( +v +) +complete +: ( ) = > -self +results . -removeEventListener +push ( " -unhandledrejection +complete " -unhandledRejectionHandler ) +} +) +; +assert_array_equals +( +results +[ +" +from +Observable +" +" +complete +" +] +) +; +} +" +from +( +) +: +Observable +that +implements +iterator +protocol +gets +converted +" ++ +" +as +an +Observable +not +iterator +" +) +; +test +( +( +) += +> +{ +const +results += +[ +] +; +const +promise += +new +Promise +( +resolve += +> +{ +resolve +( +' +from +Promise +' +) +; +} ) ; +promise +[ +Symbol +. +iterator +] += +( +) += +> +{ let -errorReported +done = -null +false +; +return +{ +next +( +) +{ +if +( +! +done +) +{ +done += +true +; +return +{ +value +: +' +from +iterator +' +done +: +false +} ; -self -. -addEventListener -( -" -error -" -e -= -> -errorReported -= -e +} +else { -once +return +{ +value +: +undefined +done : true } -) ; -let -catchBlockEntered -= -false +} +} +} ; -try -{ -const -promise -= -Promise -. -reject -( -" -custom -reason -" -) +} ; -const -observable -= Observable . from ( promise ) -; -observable . subscribe ( -) -; -await -promise -; -} -catch { -catchBlockEntered +next +: +v = -true -; -} -assert_true -( -catchBlockEntered -" -Catch -block -entered -" -) -; -assert_false +> +results +. +push ( -unhandledRejectionHandlerCalled -" -No -unhandledrejection -event -because -error -got -reported -to -global -" +v ) -; -assert_not_equals +complete +: ( -errorReported -null -" -Error -was -reported -to -the -global -" ) -; -assert_true -( -errorReported -. -message += +> +results . -includes -( -" -custom -reason -" -) -" -Error -message -matches -" -) -; -assert_equals +push ( -errorReported -. -lineno -0 " -Error -lineno -is -0 +complete " ) -; -assert_equals -( -errorReported -. -colno -0 -" -Error -lineno -is -0 -" +} ) ; -assert_equals +assert_array_equals ( -errorReported -. -error +results +[ " -custom -reason +from +iterator " " -Error -object -is -equivalent +complete " +] ) ; } @@ -2131,195 +3353,397 @@ from ( ) : -Rejections +Promise +that +implements +iterator +protocol +gets +converted +as +" ++ +" +an +iterable not -handled -by -subscription -are -reported +Promise +" +) +; +/ +/ +When +the +[ +Symbol +. +iterator +] +method +on +a +given +object +is +undefined +we +don +' +t +/ +/ +try +to +convert +the +object +to +an +Observable +via +the +iterable +protocol +. +The +/ +/ +Observable +specification +* +also +* +does +the +same +thing +if +the +[ +Symbol +. +iterator +] +/ +/ +method +is +* +null +* +. +That +is +in +that +case +we +also +skip +the +conversion +via +/ +/ +iterable +protocol +and +continue to -the -" -+ -" -global +try and -still -not -sent +convert +the +object as -an -unhandledrejection -event -" +another +type +/ +/ +( +in +this +case +a +Promise ) -; -test +. +promise_test ( +async ( ) = > { const -results -= -[ -] -; -const -observable +promise = new -Observable +Promise ( -subscriber +resolve = > -{ -subscriber -. -next +resolve ( ' from -Observable +Promise ' ) -; -subscriber -. -complete -( ) ; -} +assert_equals +( +promise +[ +Symbol +. +iterator +] +undefined ) ; -observable +promise [ Symbol . iterator ] = +null +; +assert_equals ( +promise +[ +Symbol +. +iterator +] +null ) +; +const +value += +await +new +Promise +( +resolve = > { -results +Observable . -push +from ( -' -Symbol +promise +) . -iterator +subscribe +( +value += +> +resolve ( +value ) -called -' ) ; -return -{ -next -( +} ) -{ -return -{ +; +assert_equals +( value -: ' from -iterator +Promise ' -done -: -true -} -; -} -} +) ; } +" +from +( +) +: +Promise +whose +[ +Symbol +. +iterator +] +returns +null +converts +as +Promise +" +) ; -Observable +/ +/ +This +is +a +more +sensitive +test +which +asserts +that +even +just +trying +to +reach +/ +/ +for +the +[ +Symbol +. +iterator +] +method +on +an +object +whose +* +getter +* +for +the +/ +/ +[ +Symbol . +iterator +] +method +throws +an +error +results +in +Observable +# from ( -observable ) +/ +/ +rethrowing +that +error . -subscribe +test ( -{ -next -: -v -= -> -results -. -push ( -v ) -complete -: += +> +{ +const +error += +new +Error ( +' +thrown +from +iterator +getter +' ) +; +const +obj = -> -results +{ +get +[ +Symbol . -push +iterator +] ( -" -complete -" ) +{ +throw +error +; } +} +try +{ +Observable +. +from +( +obj ) ; -assert_array_equals +assert_unreached ( -results -[ " from -Observable -" -" -complete +( +) +conversion +throws " -] ) ; } +catch +( +e +) +{ +assert_equals +( +e +error +) +; +} +} " from ( ) : -Observable -that -implements +Rethrows +the +error +when +Converting +an +object +whose iterator -protocol -gets -converted " + " -as +method +* +getter +* +throws an -Observable -not -iterator +error " ) ; @@ -2331,170 +3755,178 @@ test > { const -results -= -[ -] -; -const -promise -= -new -Promise -( -resolve +obj = -> { -resolve -( -' -from -Promise -' -) -; } -) ; -promise +/ +/ +Non +- +undefined +& +non +- +null +values +of +the +iterator +property +are +not +/ +/ +allowed +. +Specifically +they +fail +the +the +IsCallable +( +) +test +which +fails +/ +/ +Observable +conversion +. +obj [ Symbol . iterator ] = -( -) -= -> -{ -let -done -= -false -; -return -{ -next -( -) -{ -if -( -! -done -) -{ -done -= -true -; -return -{ -value -: -' -from -iterator -' -done -: -false -} +10 ; -} -else -{ -return +try { -value -: -undefined -done -: -true -} -; -} -} -} -; -} -; Observable . from ( -promise +obj ) -. -subscribe -( -{ -next -: -v -= -> -results -. -push +; +assert_unreached ( -v -) -complete -: +" +from ( ) -= -> -results -. -push -( -" -complete +conversion +throws " ) +; } +catch +( +e +) +{ +assert_true +( +e +instanceof +TypeError ) ; -assert_array_equals +assert_equals ( -results -[ +e +. +message " +Failed +to +execute +' from +' +on +' +Observable +' +: iterator +must +be +a +callable +. " -" -complete -" -] ) ; } +} " from ( ) : -Promise -that -implements +Throws +' +callable +' +error +when iterator -protocol -gets -converted -as +property +is +a " + " -an -iterable -not -Promise +non +- +callable +primitive " ) ; +/ +/ +TODO +( +dom +chromium +. +org +) +: +Add +another +test +like +the +above +but +for +/ +/ +[ +Symbol +. +asyncIterator +] += +null +falling +back +to +[ +Symbol +. +iterator +] +/ +/ +conversion +.