github.com/MontFerret/ferret@v0.18.0/pkg/drivers/cdp/eval/function_test.go (about)

     1  package eval
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/mafredri/cdp/protocol/runtime"
     7  	. "github.com/smartystreets/goconvey/convey"
     8  
     9  	"github.com/MontFerret/ferret/pkg/drivers"
    10  	"github.com/MontFerret/ferret/pkg/runtime/values"
    11  )
    12  
    13  func TestFunction(t *testing.T) {
    14  	Convey("Function", t, func() {
    15  		Convey(".AsAsync", func() {
    16  			Convey("Should set async=true", func() {
    17  				f := F("return 'foo'").AsAsync()
    18  				args := f.eval(EmptyExecutionContextID)
    19  
    20  				So(*args.AwaitPromise, ShouldBeTrue)
    21  			})
    22  		})
    23  
    24  		Convey(".AsSync", func() {
    25  			Convey("Should set async=false", func() {
    26  				f := F("return 'foo'").AsAsync()
    27  				args := f.eval(EmptyExecutionContextID)
    28  
    29  				So(*args.AwaitPromise, ShouldBeTrue)
    30  
    31  				args = f.AsSync().eval(EmptyExecutionContextID)
    32  
    33  				So(*args.AwaitPromise, ShouldBeFalse)
    34  			})
    35  		})
    36  
    37  		Convey(".AsNamed", func() {
    38  			Convey("When without args", func() {
    39  				Convey("Should generate a wrapper with a given function name", func() {
    40  					name := "getFoo"
    41  					exp := "return 'foo'"
    42  					f := F(exp).AsNamed(name)
    43  
    44  					So(f.name, ShouldEqual, name)
    45  
    46  					call := f.eval(EmptyExecutionContextID)
    47  
    48  					expected := "function " + name + "() {\n" + exp + "\n}"
    49  
    50  					So(call.FunctionDeclaration, ShouldEqual, expected)
    51  				})
    52  			})
    53  
    54  			Convey("When with args", func() {
    55  				Convey("When a declaration is an expression", func() {
    56  					Convey("Should generate a wrapper with a given function name", func() {
    57  						name := "getFoo"
    58  						exp := "return 'foo'"
    59  						f := F(exp).
    60  							AsNamed(name).
    61  							WithArg("bar").
    62  							WithArg(1)
    63  
    64  						So(f.name, ShouldEqual, name)
    65  
    66  						call := f.eval(EmptyExecutionContextID)
    67  
    68  						expected := "function " + name + "(arg1,arg2) {\n" + exp + "\n}"
    69  
    70  						So(call.FunctionDeclaration, ShouldEqual, expected)
    71  					})
    72  				})
    73  
    74  				Convey("When a declaration is an arrow function", func() {
    75  					Convey("Should generate a wrapper with a given function name", func() {
    76  						name := "getValue"
    77  						exp := "(el) => el.value"
    78  						f := F(exp).
    79  							AsNamed(name).
    80  							WithArgRef("my_element").
    81  							WithArg(1)
    82  
    83  						So(f.name, ShouldEqual, name)
    84  
    85  						call := f.eval(EmptyExecutionContextID)
    86  
    87  						expected := "function " + name + "() {\n" +
    88  							"const $exp = " + exp + ";\n" +
    89  							"return $exp.apply(this, arguments);\n" +
    90  							"}"
    91  
    92  						So(call.FunctionDeclaration, ShouldEqual, expected)
    93  					})
    94  				})
    95  
    96  				Convey("When a declaration is a plain function", func() {
    97  					Convey("Should generate a wrapper with a given function name", func() {
    98  						name := "getValue"
    99  						exp := "function getElementValue(el) => el.value"
   100  						f := F(exp).
   101  							AsNamed(name).
   102  							WithArgRef("my_element").
   103  							WithArg(1)
   104  
   105  						So(f.name, ShouldEqual, name)
   106  
   107  						call := f.eval(EmptyExecutionContextID)
   108  
   109  						expected := "function " + name + "() {\n" +
   110  							"const $exp = " + exp + ";\n" +
   111  							"return $exp.apply(this, arguments);\n" +
   112  							"}"
   113  
   114  						So(call.FunctionDeclaration, ShouldEqual, expected)
   115  					})
   116  				})
   117  			})
   118  		})
   119  
   120  		Convey(".AsAnonymous", func() {
   121  			Convey("When without args", func() {
   122  				Convey("Should generate an anonymous wrapper", func() {
   123  					name := ""
   124  					exp := "return 'foo'"
   125  					f := F(exp).AsNamed("getFoo").AsAnonymous()
   126  
   127  					So(f.name, ShouldEqual, name)
   128  
   129  					call := f.eval(EmptyExecutionContextID)
   130  
   131  					expected := "function() {\n" + exp + "\n}"
   132  
   133  					So(call.FunctionDeclaration, ShouldEqual, expected)
   134  				})
   135  			})
   136  
   137  			Convey("When with args", func() {
   138  				Convey("When a declaration is an expression", func() {
   139  					Convey("Should generate an anonymous wrapper", func() {
   140  						name := ""
   141  						exp := "return 'foo'"
   142  						f := F(exp).
   143  							AsNamed("getFoo").
   144  							AsAnonymous().
   145  							WithArg("bar").
   146  							WithArg(1)
   147  
   148  						So(f.name, ShouldEqual, name)
   149  
   150  						call := f.eval(EmptyExecutionContextID)
   151  
   152  						expected := "function(arg1,arg2) {\n" + exp + "\n}"
   153  
   154  						So(call.FunctionDeclaration, ShouldEqual, expected)
   155  					})
   156  				})
   157  
   158  				Convey("When a declaration is an arrow function", func() {
   159  					Convey("Should NOT generate a wrapper", func() {
   160  						name := ""
   161  						exp := "(el) => el.value"
   162  						f := F(exp).
   163  							AsNamed("getValue").
   164  							AsAnonymous().
   165  							WithArgRef("my_element").
   166  							WithArg(1)
   167  
   168  						So(f.name, ShouldEqual, name)
   169  
   170  						call := f.eval(EmptyExecutionContextID)
   171  
   172  						So(call.FunctionDeclaration, ShouldEqual, exp)
   173  					})
   174  				})
   175  
   176  				Convey("When a declaration is a plain function", func() {
   177  					Convey("Should NOT generate a wrapper", func() {
   178  						name := ""
   179  						exp := "function(el) => el.value"
   180  						f := F(exp).
   181  							AsNamed("getValue").
   182  							AsAnonymous().
   183  							WithArgRef("my_element").
   184  							WithArg(1)
   185  
   186  						So(f.name, ShouldEqual, name)
   187  
   188  						call := f.eval(EmptyExecutionContextID)
   189  
   190  						So(call.FunctionDeclaration, ShouldEqual, exp)
   191  					})
   192  				})
   193  			})
   194  		})
   195  
   196  		Convey(".CallOn", func() {
   197  			Convey("It should use a given ownerID over ContextID", func() {
   198  				ownerID := runtime.RemoteObjectID("foo")
   199  				contextID := runtime.ExecutionContextID(42)
   200  
   201  				f := F("return 'foo'").CallOn(ownerID)
   202  				call := f.eval(contextID)
   203  
   204  				So(call.ExecutionContextID, ShouldBeNil)
   205  				So(call.ObjectID, ShouldNotBeNil)
   206  				So(*call.ObjectID, ShouldEqual, ownerID)
   207  			})
   208  
   209  			Convey("It should use a given ContextID when ownerID is empty or nil", func() {
   210  				ownerID := runtime.RemoteObjectID("")
   211  				contextID := runtime.ExecutionContextID(42)
   212  
   213  				f := F("return 'foo'").CallOn(ownerID)
   214  				call := f.eval(contextID)
   215  
   216  				So(call.ExecutionContextID, ShouldNotBeNil)
   217  				So(call.ObjectID, ShouldBeNil)
   218  				So(*call.ExecutionContextID, ShouldEqual, contextID)
   219  			})
   220  		})
   221  
   222  		Convey(".WithArgRef", func() {
   223  			Convey("Should add argument with a given RemoteObjectID", func() {
   224  				f := F("return 'foo'")
   225  				id1 := runtime.RemoteObjectID("foo")
   226  				id2 := runtime.RemoteObjectID("bar")
   227  				id3 := runtime.RemoteObjectID("baz")
   228  
   229  				f.WithArgRef(id1).WithArgRef(id2).WithArgRef(id3)
   230  
   231  				So(f.Length(), ShouldEqual, 3)
   232  
   233  				arg1 := f.args[0]
   234  				arg2 := f.args[1]
   235  				arg3 := f.args[2]
   236  
   237  				So(*arg1.ObjectID, ShouldEqual, id1)
   238  				So(arg1.Value, ShouldBeNil)
   239  				So(arg1.UnserializableValue, ShouldBeNil)
   240  
   241  				So(*arg2.ObjectID, ShouldEqual, id2)
   242  				So(arg2.Value, ShouldBeNil)
   243  				So(arg2.UnserializableValue, ShouldBeNil)
   244  
   245  				So(*arg3.ObjectID, ShouldEqual, id3)
   246  				So(arg3.Value, ShouldBeNil)
   247  				So(arg3.UnserializableValue, ShouldBeNil)
   248  			})
   249  		})
   250  
   251  		Convey(".WithArgValue", func() {
   252  			Convey("Should add argument with a given Value", func() {
   253  				f := F("return 'foo'")
   254  				val1 := values.NewString("foo")
   255  				val2 := values.NewInt(1)
   256  				val3 := values.NewBoolean(true)
   257  
   258  				f.WithArgValue(val1).WithArgValue(val2).WithArgValue(val3)
   259  
   260  				So(f.Length(), ShouldEqual, 3)
   261  
   262  				arg1 := f.args[0]
   263  				arg2 := f.args[1]
   264  				arg3 := f.args[2]
   265  
   266  				So(arg1.ObjectID, ShouldBeNil)
   267  				So(arg1.Value, ShouldResemble, values.MustMarshal(val1))
   268  				So(arg1.UnserializableValue, ShouldBeNil)
   269  
   270  				So(arg2.ObjectID, ShouldBeNil)
   271  				So(arg2.Value, ShouldResemble, values.MustMarshal(val2))
   272  				So(arg2.UnserializableValue, ShouldBeNil)
   273  
   274  				So(arg3.ObjectID, ShouldBeNil)
   275  				So(arg3.Value, ShouldResemble, values.MustMarshal(val3))
   276  				So(arg3.UnserializableValue, ShouldBeNil)
   277  			})
   278  		})
   279  
   280  		Convey(".WithArg", func() {
   281  			Convey("Should add argument with a given any type", func() {
   282  				f := F("return 'foo'")
   283  				val1 := "foo"
   284  				val2 := 1
   285  				val3 := true
   286  
   287  				f.WithArg(val1).WithArg(val2).WithArg(val3)
   288  
   289  				So(f.Length(), ShouldEqual, 3)
   290  
   291  				arg1 := f.args[0]
   292  				arg2 := f.args[1]
   293  				arg3 := f.args[2]
   294  
   295  				So(arg1.ObjectID, ShouldBeNil)
   296  				So(arg1.Value, ShouldResemble, values.MustMarshalAny(val1))
   297  				So(arg1.UnserializableValue, ShouldBeNil)
   298  
   299  				So(arg2.ObjectID, ShouldBeNil)
   300  				So(arg2.Value, ShouldResemble, values.MustMarshalAny(val2))
   301  				So(arg2.UnserializableValue, ShouldBeNil)
   302  
   303  				So(arg3.ObjectID, ShouldBeNil)
   304  				So(arg3.Value, ShouldResemble, values.MustMarshalAny(val3))
   305  				So(arg3.UnserializableValue, ShouldBeNil)
   306  			})
   307  		})
   308  
   309  		Convey(".WithArgSelector", func() {
   310  			Convey("Should add argument with a given QuerySelector", func() {
   311  				f := F("return 'foo'")
   312  				val1 := drivers.NewCSSSelector(".foo-bar")
   313  				val2 := drivers.NewCSSSelector("#submit")
   314  				val3 := drivers.NewXPathSelector("//*[@id='q']")
   315  
   316  				f.WithArgSelector(val1).WithArgSelector(val2).WithArgSelector(val3)
   317  
   318  				So(f.Length(), ShouldEqual, 3)
   319  
   320  				arg1 := f.args[0]
   321  				arg2 := f.args[1]
   322  				arg3 := f.args[2]
   323  
   324  				So(arg1.ObjectID, ShouldBeNil)
   325  				So(arg1.Value, ShouldResemble, values.MustMarshalAny(val1.String()))
   326  				So(arg1.UnserializableValue, ShouldBeNil)
   327  
   328  				So(arg2.ObjectID, ShouldBeNil)
   329  				So(arg2.Value, ShouldResemble, values.MustMarshalAny(val2.String()))
   330  				So(arg2.UnserializableValue, ShouldBeNil)
   331  
   332  				So(arg3.ObjectID, ShouldBeNil)
   333  				So(arg3.Value, ShouldResemble, values.MustMarshalAny(val3.String()))
   334  				So(arg3.UnserializableValue, ShouldBeNil)
   335  			})
   336  		})
   337  
   338  		Convey(".String", func() {
   339  			Convey("It should return a function expression", func() {
   340  				exp := "return 'foo'"
   341  				f := F(exp)
   342  
   343  				So(f.String(), ShouldEqual, exp)
   344  			})
   345  		})
   346  
   347  		Convey(".returnNothing", func() {
   348  			Convey("It should set return by value to false", func() {
   349  				f := F("return 'foo'").returnNothing()
   350  				call := f.eval(EmptyExecutionContextID)
   351  
   352  				So(*call.ReturnByValue, ShouldBeFalse)
   353  			})
   354  		})
   355  
   356  		Convey(".returnValue", func() {
   357  			Convey("It should set return by value to true", func() {
   358  				f := F("return 'foo'").returnValue()
   359  				call := f.eval(EmptyExecutionContextID)
   360  
   361  				So(*call.ReturnByValue, ShouldBeTrue)
   362  			})
   363  		})
   364  
   365  		Convey(".returnRef", func() {
   366  			Convey("It should set return by value to false", func() {
   367  				f := F("return 'foo'").returnValue()
   368  				call := f.eval(EmptyExecutionContextID)
   369  
   370  				So(*call.ReturnByValue, ShouldBeTrue)
   371  
   372  				f.returnRef()
   373  
   374  				call = f.eval(EmptyExecutionContextID)
   375  
   376  				So(*call.ReturnByValue, ShouldBeFalse)
   377  			})
   378  		})
   379  
   380  		Convey(".compile", func() {
   381  			Convey("When Anonymous", func() {
   382  				Convey("When without args", func() {
   383  					Convey("Should generate an expression", func() {
   384  						name := ""
   385  						exp := "return 'foo'"
   386  						f := F(exp)
   387  
   388  						So(f.name, ShouldEqual, name)
   389  
   390  						call := f.compile(EmptyExecutionContextID)
   391  
   392  						expected := "const args = [];\n" +
   393  							"const " + compiledExpName + " = function() {\n" + exp + "\n};\n" +
   394  							compiledExpName + ".apply(this, args);\n"
   395  
   396  						So(call.Expression, ShouldEqual, expected)
   397  					})
   398  
   399  					Convey("When a function is given", func() {
   400  						Convey("Should generate an expression", func() {
   401  							name := ""
   402  							exp := "() => return 'foo'"
   403  							f := F(exp)
   404  
   405  							So(f.name, ShouldEqual, name)
   406  
   407  							call := f.compile(EmptyExecutionContextID)
   408  
   409  							expected := "const args = [];\n" +
   410  								"const " + compiledExpName + " = " + exp + ";\n" +
   411  								compiledExpName + ".apply(this, args);\n"
   412  
   413  							So(call.Expression, ShouldEqual, expected)
   414  						})
   415  					})
   416  				})
   417  
   418  				Convey("When with args", func() {
   419  					Convey("Should generate an expression", func() {
   420  						name := ""
   421  						exp := "return 'foo'"
   422  						f := F(exp).WithArg(1).WithArg("test").WithArg([]int{1, 2})
   423  
   424  						So(f.name, ShouldEqual, name)
   425  
   426  						call := f.compile(EmptyExecutionContextID)
   427  
   428  						expected := "const args = [\n" +
   429  							"1,\n" +
   430  							"\"test\",\n" +
   431  							"[1,2],\n" +
   432  							"];\n" +
   433  							"const " + compiledExpName + " = function(arg1,arg2,arg3) {\n" + exp + "\n};\n" +
   434  							compiledExpName + ".apply(this, args);\n"
   435  
   436  						So(call.Expression, ShouldEqual, expected)
   437  					})
   438  
   439  					Convey("When a function is given", func() {
   440  						Convey("Should generate an expression", func() {
   441  							name := ""
   442  							exp := "() => return 'foo'"
   443  							f := F(exp).WithArg(1).WithArg("test").WithArg([]int{1, 2})
   444  
   445  							So(f.name, ShouldEqual, name)
   446  
   447  							call := f.compile(EmptyExecutionContextID)
   448  
   449  							expected := "const args = [\n" +
   450  								"1,\n" +
   451  								"\"test\",\n" +
   452  								"[1,2],\n" +
   453  								"];\n" +
   454  								"const " + compiledExpName + " = " + exp + ";\n" +
   455  								compiledExpName + ".apply(this, args);\n"
   456  
   457  							So(call.Expression, ShouldEqual, expected)
   458  						})
   459  					})
   460  				})
   461  			})
   462  		})
   463  	})
   464  }