How to use NotEmpty method of td Package

Best Go-testdeep code snippet using td.NotEmpty

td_json_test.go

Source:td_json_test.go Github

copy

Full Screen

...50 checkOK(t, got,51 td.JSON(`{"name":"$1","age":$2,"gender":"$3"}`,52 td.Re(`^Bob`),53 td.Between(40, 45),54 td.NotEmpty()))55 // Same using Flatten56 checkOK(t, got,57 td.JSON(`{"name":"$1","age":$2,"gender":"$3"}`,58 td.Re(`^Bob`),59 td.Flatten([]td.TestDeep{td.Between(40, 45), td.NotEmpty()}),60 ))61 // Operators are not JSON marshallable62 checkOK(t, got,63 td.JSON(`$1`, map[string]any{64 "name": td.Re(`^Bob`),65 "age": 42,66 "gender": td.NotEmpty(),67 }))68 // Placeholder + unmarshal before comparison69 checkOK(t, json.RawMessage(`[1,2,3]`), td.JSON(`$1`, []int{1, 2, 3}))70 checkOK(t, json.RawMessage(`{"foo":[1,2,3]}`),71 td.JSON(`{"foo":$1}`, []int{1, 2, 3}))72 checkOK(t, json.RawMessage(`[1,2,3]`),73 td.JSON(`$1`, []any{1, td.Between(1, 3), 3}))74 // Tag placeholders75 checkOK(t, got,76 td.JSON(`{"name":"$name","age":$age,"gender":$gender}`,77 // raw values78 td.Tag("name", "Bob"), td.Tag("age", 42), td.Tag("gender", "male")))79 checkOK(t, got,80 td.JSON(`{"name":"$name","age":$age,"gender":"$gender"}`,81 td.Tag("name", td.Re(`^Bo`)),82 td.Tag("age", td.Between(40, 45)),83 td.Tag("gender", td.NotEmpty())))84 // Tag placeholders + operators are not JSON marshallable85 checkOK(t, got,86 td.JSON(`$all`, td.Tag("all", map[string]any{87 "name": td.Re(`^Bob`),88 "age": 42,89 "gender": td.NotEmpty(),90 })))91 // Tag placeholders + nil92 checkOK(t, nil, td.JSON(`$all`, td.Tag("all", nil)))93 // Mixed placeholders + operator94 for _, op := range []string{95 "NotEmpty",96 "NotEmpty()",97 "$^NotEmpty",98 "$^NotEmpty()",99 `"$^NotEmpty"`,100 `"$^NotEmpty()"`,101 `r<$^NotEmpty>`,102 `r<$^NotEmpty()>`,103 } {104 checkOK(t, got,105 td.JSON(`{"name":"$name","age":$1,"gender":`+op+`}`,106 td.Tag("age", td.Between(40, 45)),107 td.Tag("name", td.Re(`^Bob`))),108 "using operator %s", op)109 }110 checkOK(t, got,111 td.JSON(`{"name":Re("^Bo\\w"),"age":Between(40,45),"gender":NotEmpty()}`))112 checkOK(t, got,113 td.JSON(`114{115 "name": All(Re("^Bo\\w"), HasPrefix("Bo"), HasSuffix("ob")),116 "age": Between(40,45),117 "gender": NotEmpty()118}`))119 checkOK(t, got,120 td.JSON(`121{122 "name": All(Re("^Bo\\w"), HasPrefix("Bo"), HasSuffix("ob")),123 "age": Between(40,45),124 "gender": NotEmpty125}`))126 // Same but operators in strings using "$^"127 checkOK(t, got,128 td.JSON(`{"name":Re("^Bo\\w"),"age":"$^Between(40,45)","gender":"$^NotEmpty()"}`))129 checkOK(t, got, // using classic "" string, so each \ has to be escaped130 td.JSON(`131{132 "name": "$^All(Re(\"^Bo\\\\w\"), HasPrefix(\"Bo\"), HasSuffix(\"ob\"))",133 "age": "$^Between(40,45)",134 "gender": "$^NotEmpty()",135}`))136 checkOK(t, got, // using raw strings, no escape needed137 td.JSON(`138{139 "name": "$^All(Re(r(^Bo\\w)), HasPrefix(r{Bo}), HasSuffix(r'ob'))",140 "age": "$^Between(40,45)",141 "gender": "$^NotEmpty()",142}`))143 // …with comments…144 checkOK(t, got,145 td.JSON(`146// This should be the JSON representation of MyStruct struct147{148 // A person:149 "name": "$name", // The name of this person150 "age": $1, /* The age of this person:151 - placeholder unquoted, but could be without152 any change153 - to demonstrate a multi-lines comment */154 "gender": $^NotEmpty // Operator NotEmpty155}`,156 td.Tag("age", td.Between(40, 45)),157 td.Tag("name", td.Re(`^Bob`))))158 before := time.Now()159 timeGot := map[string]time.Time{"created_at": time.Now()}160 checkOK(t, timeGot,161 td.JSON(`{"created_at": Between($1, $2)}`, before, time.Now()))162 checkOK(t, timeGot,163 td.JSON(`{"created_at": $1}`, td.Between(before, time.Now())))164 // Len165 checkOK(t, []int{1, 2, 3}, td.JSON(`Len(3)`))166 //167 // []byte168 checkOK(t, got,169 td.JSON([]byte(`{"name":"$name","age":$1,"gender":"male"}`),170 td.Tag("age", td.Between(40, 45)),171 td.Tag("name", td.Re(`^Bob`))))172 //173 // nil++174 checkOK(t, nil, td.JSON(`$1`, nil))175 checkOK(t, (*int)(nil), td.JSON(`$1`, td.Nil()))176 checkOK(t, nil, td.JSON(`$x`, td.Tag("x", nil)))177 checkOK(t, (*int)(nil), td.JSON(`$x`, td.Tag("x", nil)))178 checkOK(t, json.RawMessage(`{"foo": null}`), td.JSON(`{"foo": null}`))179 checkOK(t,180 json.RawMessage(`{"foo": null}`),181 td.JSON(`{"foo": $1}`, nil))182 checkOK(t,183 json.RawMessage(`{"foo": null}`),184 td.JSON(`{"foo": $1}`, td.Nil()))185 checkOK(t,186 json.RawMessage(`{"foo": null}`),187 td.JSON(`{"foo": $x}`, td.Tag("x", nil)))188 checkOK(t,189 json.RawMessage(`{"foo": null}`),190 td.JSON(`{"foo": $x}`, td.Tag("x", td.Nil())))191 //192 // Loading a file193 tmpDir := t.TempDir()194 filename := tmpDir + "/test.json"195 err := os.WriteFile(196 filename, []byte(`{"name":$name,"age":$1,"gender":$^NotEmpty}`), 0644)197 if err != nil {198 t.Fatal(err)199 }200 checkOK(t, got,201 td.JSON(filename,202 td.Tag("age", td.Between(40, 45)),203 td.Tag("name", td.Re(`^Bob`))))204 //205 // Reading (a file)206 tmpfile, err := os.Open(filename)207 if err != nil {208 t.Fatal(err)209 }210 checkOK(t, got,211 td.JSON(tmpfile,212 td.Tag("age", td.Between(40, 45)),213 td.Tag("name", td.Re(`^Bob`))))214 tmpfile.Close()215 //216 // Escaping $ in strings217 checkOK(t, "$test", td.JSON(`"$$test"`))218 //219 // Errors220 checkError(t, func() {}, td.JSON(`null`),221 expectedError{222 Message: mustBe("json.Marshal failed"),223 Summary: mustContain("json: unsupported type"),224 })225 checkError(t, map[string]string{"zip": "pipo"},226 td.All(td.JSON(`SuperMapOf({"zip":$1})`, "bingo")),227 expectedError{228 Path: mustBe(`DATA`),229 Message: mustBe("compared (part 1 of 1)"),230 Got: mustBe(`(map[string]string) (len=1) {231 (string) (len=3) "zip": (string) (len=4) "pipo"232}`),233 Expected: mustBe(`JSON(SuperMapOf(map[string]interface {}{234 "zip": "bingo",235 }))`),236 Origin: &expectedError{237 Path: mustBe(`DATA<All#1/1>["zip"]`),238 Message: mustBe(`values differ`),239 Got: mustBe(`"pipo"`),240 Expected: mustBe(`"bingo"`),241 },242 })243 //244 // Fatal errors245 checkError(t, "never tested",246 td.JSON("uNkNoWnFiLe.json"),247 expectedError{248 Message: mustBe("bad usage of JSON operator"),249 Path: mustBe("DATA"),250 Summary: mustContain("JSON file uNkNoWnFiLe.json cannot be read: "),251 })252 checkError(t, "never tested",253 td.JSON(42),254 expectedError{255 Message: mustBe("bad usage of JSON operator"),256 Path: mustBe("DATA"),257 Summary: mustBe("usage: JSON(STRING_JSON|STRING_FILENAME|[]byte|io.Reader, ...), but received int as 1st parameter"),258 })259 checkError(t, "never tested",260 td.JSON(errReader{}),261 expectedError{262 Message: mustBe("bad usage of JSON operator"),263 Path: mustBe("DATA"),264 Summary: mustBe("JSON read error: an error occurred"),265 })266 checkError(t, "never tested",267 td.JSON(`pipo`),268 expectedError{269 Message: mustBe("bad usage of JSON operator"),270 Path: mustBe("DATA"),271 Summary: mustContain("JSON unmarshal error: "),272 })273 checkError(t, "never tested",274 td.JSON(`[$foo]`,275 td.Tag("foo", td.Ignore()),276 td.Tag("foo", td.Ignore())),277 expectedError{278 Message: mustBe("bad usage of JSON operator"),279 Path: mustBe("DATA"),280 Summary: mustBe(`2 params have the same tag "foo"`),281 })282 checkError(t, []int{42},283 td.JSON(`[$1]`, func() {}),284 expectedError{285 Message: mustBe("an error occurred while unmarshalling JSON into func()"),286 Path: mustBe("DATA[0]"),287 Summary: mustBe("json: cannot unmarshal number into Go value of type func()"),288 })289 checkError(t, []int{42},290 td.JSON(`[$foo]`, td.Tag("foo", func() {})),291 expectedError{292 Message: mustBe("an error occurred while unmarshalling JSON into func()"),293 Path: mustBe("DATA[0]"),294 Summary: mustBe("json: cannot unmarshal number into Go value of type func()"),295 })296 // numeric placeholders297 checkError(t, "never tested",298 td.JSON(`[1, "$123bad"]`),299 expectedError{300 Message: mustBe("bad usage of JSON operator"),301 Path: mustBe("DATA"),302 Summary: mustBe(`JSON unmarshal error: invalid numeric placeholder at line 1:5 (pos 5)`),303 })304 checkError(t, "never tested",305 td.JSON(`[1, $000]`),306 expectedError{307 Message: mustBe("bad usage of JSON operator"),308 Path: mustBe("DATA"),309 Summary: mustBe(`JSON unmarshal error: invalid numeric placeholder "$000", it should start at "$1" at line 1:4 (pos 4)`),310 })311 checkError(t, "never tested",312 td.JSON(`[1, $1]`),313 expectedError{314 Message: mustBe("bad usage of JSON operator"),315 Path: mustBe("DATA"),316 Summary: mustBe(`JSON unmarshal error: numeric placeholder "$1", but no params given at line 1:4 (pos 4)`),317 })318 checkError(t, "never tested",319 td.JSON(`[1, 2, $3]`, td.Ignore()),320 expectedError{321 Message: mustBe("bad usage of JSON operator"),322 Path: mustBe("DATA"),323 Summary: mustBe(`JSON unmarshal error: numeric placeholder "$3", but only one param given at line 1:7 (pos 7)`),324 })325 // $^Operator326 checkError(t, "never tested",327 td.JSON(`[1, $^bad%]`),328 expectedError{329 Message: mustBe("bad usage of JSON operator"),330 Path: mustBe("DATA"),331 Summary: mustBe(`JSON unmarshal error: $^ must be followed by an operator name at line 1:4 (pos 4)`),332 })333 checkError(t, "never tested",334 td.JSON(`[1, "$^bad%"]`),335 expectedError{336 Message: mustBe("bad usage of JSON operator"),337 Path: mustBe("DATA"),338 Summary: mustBe(`JSON unmarshal error: $^ must be followed by an operator name at line 1:5 (pos 5)`),339 })340 // named placeholders341 checkError(t, "never tested",342 td.JSON(`[343 1,344 "$bad%"345]`),346 expectedError{347 Message: mustBe("bad usage of JSON operator"),348 Path: mustBe("DATA"),349 Summary: mustBe(`JSON unmarshal error: bad placeholder "$bad%" at line 3:3 (pos 10)`),350 })351 checkError(t, "never tested",352 td.JSON(`[1, $unknown]`),353 expectedError{354 Message: mustBe("bad usage of JSON operator"),355 Path: mustBe("DATA"),356 Summary: mustBe(`JSON unmarshal error: unknown placeholder "$unknown" at line 1:4 (pos 4)`),357 })358 //359 // Stringification360 test.EqualStr(t, td.JSON(`1`).String(),361 `JSON(1)`)362 test.EqualStr(t, td.JSON(`[ 1, 2, 3 ]`).String(),363 `364JSON([365 1,366 2,367 3368 ])`[1:])369 test.EqualStr(t, td.JSON(` null `).String(), `JSON(null)`)370 test.EqualStr(t,371 td.JSON(`[ $1, $name, $2, Nil(), $nil, 26, Between(5, 6), Len(34), Len(Between(5, 6)), 28 ]`,372 td.Between(12, 20),373 "test",374 td.Tag("name", td.Code(func(s string) bool { return len(s) > 0 })),375 td.Tag("nil", nil),376 14,377 ).String(),378 `379JSON([380 "$1" /* 12 ≤ got ≤ 20 */,381 "$name" /* Code(func(string) bool) */,382 "test",383 nil,384 null,385 26,386 5.0 ≤ got ≤ 6.0,387 len=34,388 len: 5.0 ≤ got ≤ 6.0,389 28390 ])`[1:])391 test.EqualStr(t,392 td.JSON(`[ $1, $name, $2, $^Nil, $nil ]`,393 td.Between(12, 20),394 "test",395 td.Tag("name", td.Code(func(s string) bool { return len(s) > 0 })),396 td.Tag("nil", nil),397 ).String(),398 `399JSON([400 "$1" /* 12 ≤ got ≤ 20 */,401 "$name" /* Code(func(string) bool) */,402 "test",403 nil,404 null405 ])`[1:])406 test.EqualStr(t,407 td.JSON(`{"label": $value, "zip": $^NotZero}`,408 td.Tag("value", td.Bag(409 td.JSON(`{"name": $1,"age":$2,"surname":$3}`,410 td.HasPrefix("Bob"),411 td.Between(12, 24),412 "captain",413 ),414 td.JSON(`{"name": $1}`, td.HasPrefix("Alice")),415 )),416 ).String(),417 `418JSON({419 "label": "$value" /* Bag(JSON({420 "age": "$2" /* 12 ≤ got ≤ 24 */,421 "name": "$1" /* HasPrefix("Bob") */,422 "surname": "captain"423 }),424 JSON({425 "name": "$1" /* HasPrefix("Alice") */426 })) */,427 "zip": NotZero()428 })`[1:])429 test.EqualStr(t,430 td.JSON(`431{432 "label": {"name": HasPrefix("Bob"), "age": Between(12,24)},433 "zip": NotZero()434}`).String(),435 `436JSON({437 "label": {438 "age": 12.0 ≤ got ≤ 24.0,439 "name": HasPrefix("Bob")440 },441 "zip": NotZero()442 })`[1:])443 // Erroneous op444 test.EqualStr(t, td.JSON(`[`).String(), "JSON(<ERROR>)")445}446func TestJSONInside(t *testing.T) {447 // Between448 t.Run("Between", func(t *testing.T) {449 got := map[string]int{"val1": 1, "val2": 2}450 checkOK(t, got,451 td.JSON(`{"val1": Between(0, 2), "val2": Between(2, 3, "[[")}`))452 checkOK(t, got,453 td.JSON(`{"val1": Between(0, 2), "val2": Between(2, 3, "BoundsInOut")}`))454 for _, bounds := range []string{"[[", "BoundsInOut"} {455 checkError(t, got,456 td.JSON(`{"val1": Between(0, 2), "val2": Between(1, 2, $1)}`, bounds),457 expectedError{458 Message: mustBe("values differ"),459 Path: mustBe(`DATA["val2"]`),460 Got: mustBe("2.0"),461 Expected: mustBe("1.0 ≤ got < 2.0"),462 })463 }464 checkOK(t, got,465 td.JSON(`{"val1": Between(1, 1), "val2": Between(2, 2, "[]")}`))466 checkOK(t, got,467 td.JSON(`{"val1": Between(1, 1), "val2": Between(2, 2, "BoundsInIn")}`))468 checkOK(t, got,469 td.JSON(`{"val1": Between(0, 1, "]]"), "val2": Between(1, 3, "][")}`))470 checkOK(t, got,471 td.JSON(`{"val1": Between(0, 1, "BoundsOutIn"), "val2": Between(1, 3, "BoundsOutOut")}`))472 for _, bounds := range []string{"]]", "BoundsOutIn"} {473 checkError(t, got,474 td.JSON(`{"val1": 1, "val2": Between(2, 3, $1)}`, bounds),475 expectedError{476 Message: mustBe("values differ"),477 Path: mustBe(`DATA["val2"]`),478 Got: mustBe("2.0"),479 Expected: mustBe("2.0 < got ≤ 3.0"),480 })481 }482 for _, bounds := range []string{"][", "BoundsOutOut"} {483 checkError(t, got,484 td.JSON(`{"val1": 1, "val2": Between(2, 3, $1)}`, bounds),485 expectedError{486 Message: mustBe("values differ"),487 Path: mustBe(`DATA["val2"]`),488 Got: mustBe("2.0"),489 Expected: mustBe("2.0 < got < 3.0"),490 },491 "using bounds %q", bounds)492 checkError(t, got,493 td.JSON(`{"val1": 1, "val2": Between(1, 2, $1)}`, bounds),494 expectedError{495 Message: mustBe("values differ"),496 Path: mustBe(`DATA["val2"]`),497 Got: mustBe("2.0"),498 Expected: mustBe("1.0 < got < 2.0"),499 },500 "using bounds %q", bounds)501 }502 // Bad 3rd parameter503 checkError(t, "never tested",504 td.JSON(`{505 "val2": Between(1, 2, "<>")506}`),507 expectedError{508 Message: mustBe("bad usage of JSON operator"),509 Path: mustBe("DATA"),510 Summary: mustBe(`JSON unmarshal error: Between() bad 3rd parameter, use "[]", "[[", "]]" or "][" at line 2:10 (pos 12)`),511 })512 checkError(t, "never tested",513 td.JSON(`{514 "val2": Between(1, 2, 125)515}`),516 expectedError{517 Message: mustBe("bad usage of JSON operator"),518 Path: mustBe("DATA"),519 Summary: mustBe(`JSON unmarshal error: Between() bad 3rd parameter, use "[]", "[[", "]]" or "][" at line 2:10 (pos 12)`),520 })521 checkError(t, "never tested",522 td.JSON(`{"val2": Between(1)}`),523 expectedError{524 Message: mustBe("bad usage of JSON operator"),525 Path: mustBe("DATA"),526 Summary: mustBe(`JSON unmarshal error: Between() requires 2 or 3 parameters at line 1:9 (pos 9)`),527 })528 checkError(t, "never tested",529 td.JSON(`{"val2": Between(1,2,3,4)}`),530 expectedError{531 Message: mustBe("bad usage of JSON operator"),532 Path: mustBe("DATA"),533 Summary: mustBe(`JSON unmarshal error: Between() requires 2 or 3 parameters at line 1:9 (pos 9)`),534 })535 })536 // N537 t.Run("N", func(t *testing.T) {538 got := map[string]float32{"val": 2.1}539 checkOK(t, got, td.JSON(`{"val": N(2.1)}`))540 checkOK(t, got, td.JSON(`{"val": N(2, 0.1)}`))541 checkError(t, "never tested",542 td.JSON(`{"val2": N()}`),543 expectedError{544 Message: mustBe("bad usage of JSON operator"),545 Path: mustBe("DATA"),546 Summary: mustBe(`JSON unmarshal error: N() requires 1 or 2 parameters at line 1:9 (pos 9)`),547 })548 checkError(t, "never tested",549 td.JSON(`{"val2": N(1,2,3)}`),550 expectedError{551 Message: mustBe("bad usage of JSON operator"),552 Path: mustBe("DATA"),553 Summary: mustBe(`JSON unmarshal error: N() requires 1 or 2 parameters at line 1:9 (pos 9)`),554 })555 })556 // Re557 t.Run("Re", func(t *testing.T) {558 got := map[string]string{"val": "Foo bar"}559 checkOK(t, got, td.JSON(`{"val": Re("^Foo")}`))560 checkOK(t, got, td.JSON(`{"val": Re("^(\\w+)", ["Foo"])}`))561 checkOK(t, got, td.JSON(`{"val": Re("^(\\w+)", Bag("Foo"))}`))562 checkError(t, "never tested",563 td.JSON(`{"val2": Re()}`),564 expectedError{565 Message: mustBe("bad usage of JSON operator"),566 Path: mustBe("DATA"),567 Summary: mustBe(`JSON unmarshal error: Re() requires 1 or 2 parameters at line 1:9 (pos 9)`),568 })569 checkError(t, "never tested",570 td.JSON(`{"val2": Re(1,2,3)}`),571 expectedError{572 Message: mustBe("bad usage of JSON operator"),573 Path: mustBe("DATA"),574 Summary: mustBe(`JSON unmarshal error: Re() requires 1 or 2 parameters at line 1:9 (pos 9)`),575 })576 })577 // SubMapOf578 t.Run("SubMapOf", func(t *testing.T) {579 got := []map[string]int{{"val1": 1, "val2": 2}}580 checkOK(t, got, td.JSON(`[ SubMapOf({"val1":1, "val2":2, "xxx": "yyy"}) ]`))581 checkError(t, "never tested",582 td.JSON(`[ SubMapOf() ]`),583 expectedError{584 Message: mustBe("bad usage of JSON operator"),585 Path: mustBe("DATA"),586 Summary: mustBe(`JSON unmarshal error: SubMapOf() requires only one parameter at line 1:2 (pos 2)`),587 })588 checkError(t, "never tested",589 td.JSON(`[ SubMapOf(1, 2) ]`),590 expectedError{591 Message: mustBe("bad usage of JSON operator"),592 Path: mustBe("DATA"),593 Summary: mustBe(`JSON unmarshal error: SubMapOf() requires only one parameter at line 1:2 (pos 2)`),594 })595 })596 // SuperMapOf597 t.Run("SuperMapOf", func(t *testing.T) {598 got := []map[string]int{{"val1": 1, "val2": 2}}599 checkOK(t, got, td.JSON(`[ SuperMapOf({"val1":1}) ]`))600 checkError(t, "never tested",601 td.JSON(`[ SuperMapOf() ]`),602 expectedError{603 Message: mustBe("bad usage of JSON operator"),604 Path: mustBe("DATA"),605 Summary: mustBe(`JSON unmarshal error: SuperMapOf() requires only one parameter at line 1:2 (pos 2)`),606 })607 checkError(t, "never tested",608 td.JSON(`[ SuperMapOf(1, 2) ]`),609 expectedError{610 Message: mustBe("bad usage of JSON operator"),611 Path: mustBe("DATA"),612 Summary: mustBe(`JSON unmarshal error: SuperMapOf() requires only one parameter at line 1:2 (pos 2)`),613 })614 })615 // errors616 t.Run("Errors", func(t *testing.T) {617 checkError(t, "never tested",618 td.JSON(`[ UnknownOp() ]`),619 expectedError{620 Message: mustBe("bad usage of JSON operator"),621 Path: mustBe("DATA"),622 Summary: mustBe(`JSON unmarshal error: unknown operator UnknownOp() at line 1:2 (pos 2)`),623 })624 checkError(t, "never tested",625 td.JSON(`[ Catch() ]`),626 expectedError{627 Message: mustBe("bad usage of JSON operator"),628 Path: mustBe("DATA"),629 Summary: mustBe(`JSON unmarshal error: Catch() is not usable in JSON() at line 1:2 (pos 2)`),630 })631 checkError(t, "never tested",632 td.JSON(`[ JSON() ]`),633 expectedError{634 Message: mustBe("bad usage of JSON operator"),635 Path: mustBe("DATA"),636 Summary: mustBe(`JSON unmarshal error: JSON() is not usable in JSON(), use literal JSON instead at line 1:2 (pos 2)`),637 })638 checkError(t, "never tested",639 td.JSON(`[ All() ]`),640 expectedError{641 Message: mustBe("bad usage of JSON operator"),642 Path: mustBe("DATA"),643 Summary: mustBe(`JSON unmarshal error: All() requires at least one parameter at line 1:2 (pos 2)`),644 })645 checkError(t, "never tested",646 td.JSON(`[ Empty(12) ]`),647 expectedError{648 Message: mustBe("bad usage of JSON operator"),649 Path: mustBe("DATA"),650 Summary: mustBe(`JSON unmarshal error: Empty() requires no parameters at line 1:2 (pos 2)`),651 })652 checkError(t, "never tested",653 td.JSON(`[ HasPrefix() ]`),654 expectedError{655 Message: mustBe("bad usage of JSON operator"),656 Path: mustBe("DATA"),657 Summary: mustBe(`JSON unmarshal error: HasPrefix() requires only one parameter at line 1:2 (pos 2)`),658 })659 checkError(t, "never tested",660 td.JSON(`[ JSONPointer(1, 2, 3) ]`),661 expectedError{662 Message: mustBe("bad usage of JSON operator"),663 Path: mustBe("DATA"),664 Summary: mustBe(`JSON unmarshal error: JSONPointer() requires 2 parameters at line 1:2 (pos 2)`),665 })666 checkError(t, "never tested",667 td.JSON(`[ JSONPointer(1, 2) ]`),668 expectedError{669 Message: mustBe("bad usage of JSON operator"),670 Path: mustBe("DATA"),671 Summary: mustBe(`JSON unmarshal error: JSONPointer() bad #1 parameter type: string required but float64 received at line 1:2 (pos 2)`),672 })673 // This one is not caught by JSON, but by Re itself, as the number674 // of parameters is correct675 checkError(t, json.RawMessage(`"never tested"`),676 td.JSON(`Re(1)`),677 expectedError{678 Message: mustBe("bad usage of Re operator"),679 Path: mustBe("DATA"),680 Summary: mustBe(`usage: Re(STRING|*regexp.Regexp[, NON_NIL_CAPTURE]), but received float64 as 1st parameter`),681 })682 })683}684func TestJSONTypeBehind(t *testing.T) {685 equalTypes(t, td.JSON(`false`), true)686 equalTypes(t, td.JSON(`"foo"`), "")687 equalTypes(t, td.JSON(`42`), float64(0))688 equalTypes(t, td.JSON(`[1,2,3]`), ([]any)(nil))689 equalTypes(t, td.JSON(`{"a":12}`), (map[string]any)(nil))690 // operator at the root → delegate it TypeBehind() call691 equalTypes(t, td.JSON(`$1`, td.SuperMapOf(map[string]any{"x": 1}, nil)), (map[string]any)(nil))692 equalTypes(t, td.JSON(`SuperMapOf({"x":1})`), (map[string]any)(nil))693 equalTypes(t, td.JSON(`$1`, 123), 42)694 nullType := td.JSON(`null`).TypeBehind()695 if nullType != reflect.TypeOf((*any)(nil)).Elem() {696 t.Errorf("Failed test: got %s intead of interface {}", nullType)697 }698 // Erroneous op699 equalTypes(t, td.JSON(`[`), nil)700}701func TestSubJSONOf(t *testing.T) {702 type MyStruct struct {703 Name string `json:"name"`704 Age uint `json:"age"`705 Gender string `json:"gender"`706 }707 //708 // struct709 //710 got := MyStruct{Name: "Bob", Age: 42, Gender: "male"}711 // No placeholder712 checkOK(t, got,713 td.SubJSONOf(`714{715 "name": "Bob",716 "age": 42,717 "gender": "male",718 "details": { // ← we don't want to test this field719 "city": "Test City",720 "zip": 666721 }722}`))723 // Numeric placeholders724 checkOK(t, got,725 td.SubJSONOf(`{"name":"$1","age":$2,"gender":$3,"details":{}}`,726 "Bob", 42, "male")) // raw values727 checkOK(t, got,728 td.SubJSONOf(`{"name":"$1","age":$2,"gender":$3,"details":{}}`,729 td.Re(`^Bob`),730 td.Between(40, 45),731 td.NotEmpty()))732 // Same using Flatten733 checkOK(t, got,734 td.SubJSONOf(`{"name":"$1","age":$2,"gender":$3,"details":{}}`,735 td.Re(`^Bob`),736 td.Flatten([]td.TestDeep{td.Between(40, 45), td.NotEmpty()}),737 ))738 // Tag placeholders739 checkOK(t, got,740 td.SubJSONOf(741 `{"name":"$name","age":$age,"gender":"$gender","details":{}}`,742 td.Tag("name", td.Re(`^Bob`)),743 td.Tag("age", td.Between(40, 45)),744 td.Tag("gender", td.NotEmpty())))745 // Mixed placeholders + operator746 for _, op := range []string{747 "NotEmpty",748 "NotEmpty()",749 "$^NotEmpty",750 "$^NotEmpty()",751 `"$^NotEmpty"`,752 `"$^NotEmpty()"`,753 `r<$^NotEmpty>`,754 `r<$^NotEmpty()>`,755 } {756 checkOK(t, got,757 td.SubJSONOf(758 `{"name":"$name","age":$1,"gender":`+op+`,"details":{}}`,759 td.Tag("age", td.Between(40, 45)),760 td.Tag("name", td.Re(`^Bob`))),761 "using operator %s", op)762 }763 //764 // Errors765 checkError(t, func() {}, td.SubJSONOf(`{}`),766 expectedError{767 Message: mustBe("json.Marshal failed"),768 Summary: mustContain("json: unsupported type"),769 })770 for i, n := range []any{771 nil,772 (map[string]any)(nil),773 (map[string]bool)(nil),774 ([]int)(nil),775 } {776 checkError(t, n, td.SubJSONOf(`{}`),777 expectedError{778 Message: mustBe("values differ"),779 Got: mustBe("null"),780 Expected: mustBe("non-null"),781 },782 "nil test #%d", i)783 }784 //785 // Fatal errors786 checkError(t, "never tested",787 td.SubJSONOf(`[1, "$123bad"]`),788 expectedError{789 Message: mustBe("bad usage of SubJSONOf operator"),790 Path: mustBe("DATA"),791 Summary: mustBe(`JSON unmarshal error: invalid numeric placeholder at line 1:5 (pos 5)`),792 })793 checkError(t, "never tested",794 td.SubJSONOf(`[1, $000]`),795 expectedError{796 Message: mustBe("bad usage of SubJSONOf operator"),797 Path: mustBe("DATA"),798 Summary: mustBe(`JSON unmarshal error: invalid numeric placeholder "$000", it should start at "$1" at line 1:4 (pos 4)`),799 })800 checkError(t, "never tested",801 td.SubJSONOf(`[1, $1]`),802 expectedError{803 Message: mustBe("bad usage of SubJSONOf operator"),804 Path: mustBe("DATA"),805 Summary: mustBe(`JSON unmarshal error: numeric placeholder "$1", but no params given at line 1:4 (pos 4)`),806 })807 checkError(t, "never tested",808 td.SubJSONOf(`[1, 2, $3]`, td.Ignore()),809 expectedError{810 Message: mustBe("bad usage of SubJSONOf operator"),811 Path: mustBe("DATA"),812 Summary: mustBe(`JSON unmarshal error: numeric placeholder "$3", but only one param given at line 1:7 (pos 7)`),813 })814 // $^Operator815 checkError(t, "never tested",816 td.SubJSONOf(`[1, $^bad%]`),817 expectedError{818 Message: mustBe("bad usage of SubJSONOf operator"),819 Path: mustBe("DATA"),820 Summary: mustBe(`JSON unmarshal error: $^ must be followed by an operator name at line 1:4 (pos 4)`),821 })822 checkError(t, "never tested",823 td.SubJSONOf(`[1, "$^bad%"]`),824 expectedError{825 Message: mustBe("bad usage of SubJSONOf operator"),826 Path: mustBe("DATA"),827 Summary: mustBe(`JSON unmarshal error: $^ must be followed by an operator name at line 1:5 (pos 5)`),828 })829 // named placeholders830 checkError(t, "never tested",831 td.SubJSONOf(`[1, "$bad%"]`),832 expectedError{833 Message: mustBe("bad usage of SubJSONOf operator"),834 Path: mustBe("DATA"),835 Summary: mustBe(`JSON unmarshal error: bad placeholder "$bad%" at line 1:5 (pos 5)`),836 })837 checkError(t, "never tested",838 td.SubJSONOf(`[1, $unknown]`),839 expectedError{840 Message: mustBe("bad usage of SubJSONOf operator"),841 Path: mustBe("DATA"),842 Summary: mustBe(`JSON unmarshal error: unknown placeholder "$unknown" at line 1:4 (pos 4)`),843 })844 checkError(t, "never tested",845 td.SubJSONOf("null"),846 expectedError{847 Message: mustBe("bad usage of SubJSONOf operator"),848 Path: mustBe("DATA"),849 Summary: mustBe("SubJSONOf() only accepts JSON objects {…}"),850 })851 //852 // Stringification853 test.EqualStr(t, td.SubJSONOf(`{}`).String(), `SubJSONOf({})`)854 test.EqualStr(t, td.SubJSONOf(`{"foo":1, "bar":2}`).String(),855 `856SubJSONOf({857 "bar": 2,858 "foo": 1859 })`[1:])860 test.EqualStr(t,861 td.SubJSONOf(`{"label": $value, "zip": $^NotZero}`,862 td.Tag("value", td.Bag(863 td.SubJSONOf(`{"name": $1,"age":$2}`,864 td.HasPrefix("Bob"),865 td.Between(12, 24),866 ),867 td.SubJSONOf(`{"name": $1}`, td.HasPrefix("Alice")),868 )),869 ).String(),870 `871SubJSONOf({872 "label": "$value" /* Bag(SubJSONOf({873 "age": "$2" /* 12 ≤ got ≤ 24 */,874 "name": "$1" /* HasPrefix("Bob") */875 }),876 SubJSONOf({877 "name": "$1" /* HasPrefix("Alice") */878 })) */,879 "zip": NotZero()880 })`[1:])881 // Erroneous op882 test.EqualStr(t, td.SubJSONOf(`123`).String(), "SubJSONOf(<ERROR>)")883}884func TestSubJSONOfTypeBehind(t *testing.T) {885 equalTypes(t, td.SubJSONOf(`{"a":12}`), (map[string]any)(nil))886 // Erroneous op887 equalTypes(t, td.SubJSONOf(`123`), nil)888}889func TestSuperJSONOf(t *testing.T) {890 type MyStruct struct {891 Name string `json:"name"`892 Age uint `json:"age"`893 Gender string `json:"gender"`894 Details string `json:"details"`895 }896 //897 // struct898 //899 got := MyStruct{Name: "Bob", Age: 42, Gender: "male", Details: "Nice"}900 // No placeholder901 checkOK(t, got, td.SuperJSONOf(`{"name": "Bob"}`))902 // Numeric placeholders903 checkOK(t, got,904 td.SuperJSONOf(`{"name":"$1","age":$2}`,905 "Bob", 42)) // raw values906 checkOK(t, got,907 td.SuperJSONOf(`{"name":"$1","age":$2}`,908 td.Re(`^Bob`),909 td.Between(40, 45)))910 // Same using Flatten911 checkOK(t, got,912 td.SuperJSONOf(`{"name":"$1","age":$2}`,913 td.Flatten([]td.TestDeep{td.Re(`^Bob`), td.Between(40, 45)}),914 ))915 // Tag placeholders916 checkOK(t, got,917 td.SuperJSONOf(`{"name":"$name","gender":"$gender"}`,918 td.Tag("name", td.Re(`^Bob`)),919 td.Tag("gender", td.NotEmpty())))920 // Mixed placeholders + operator921 for _, op := range []string{922 "NotEmpty",923 "NotEmpty()",924 "$^NotEmpty",925 "$^NotEmpty()",926 `"$^NotEmpty"`,927 `"$^NotEmpty()"`,928 `r<$^NotEmpty>`,929 `r<$^NotEmpty()>`,930 } {931 checkOK(t, got,932 td.SuperJSONOf(933 `{"name":"$name","age":$1,"gender":`+op+`}`,934 td.Tag("age", td.Between(40, 45)),935 td.Tag("name", td.Re(`^Bob`))),936 "using operator %s", op)937 }938 // …with comments…939 checkOK(t, got,940 td.SuperJSONOf(`941// This should be the JSON representation of MyStruct struct942{943 // A person:944 "name": "$name", // The name of this person945 "age": $1, /* The age of this person:946 - placeholder unquoted, but could be without947 any change948 - to demonstrate a multi-lines comment */949 "gender": $^NotEmpty // Shortcut to operator NotEmpty950}`,951 td.Tag("age", td.Between(40, 45)),952 td.Tag("name", td.Re(`^Bob`))))953 //954 // Errors955 checkError(t, func() {}, td.SuperJSONOf(`{}`),956 expectedError{957 Message: mustBe("json.Marshal failed"),958 Summary: mustContain("json: unsupported type"),959 })960 for i, n := range []any{961 nil,962 (map[string]any)(nil),963 (map[string]bool)(nil),...

Full Screen

Full Screen

td_empty_test.go

Source:td_empty_test.go Github

copy

Full Screen

...94 //95 // String96 test.EqualStr(t, td.Empty().String(), "Empty()")97}98func TestNotEmpty(t *testing.T) {99 checkOK(t, "foobar", td.NotEmpty())100 checkOK(t, []int{1}, td.NotEmpty())101 checkOK(t, map[string]bool{"foo": true}, td.NotEmpty())102 checkOK(t, [3]int{}, td.NotEmpty())103 ch := make(chan int, 1)104 ch <- 42105 checkOK(t, ch, td.NotEmpty())106 type MySlice []int107 checkOK(t, MySlice{1}, td.NotEmpty())108 checkOK(t, &MySlice{1}, td.NotEmpty())109 l1 := &MySlice{1}110 l2 := &l1111 l3 := &l2112 checkOK(t, &l3, td.NotEmpty())113 checkError(t, 12, td.NotEmpty(),114 expectedError{115 Message: mustBe("bad kind"),116 Path: mustBe("DATA"),117 Got: mustBe("int"),118 Expected: mustBe("array OR chan OR map OR slice OR string OR pointer(s) on them"),119 })120 checkError(t, nil, td.NotEmpty(),121 expectedError{122 Message: mustBe("empty"),123 Path: mustBe("DATA"),124 Got: mustContain("nil"),125 Expected: mustBe("not empty"),126 })127 checkError(t, "", td.NotEmpty(),128 expectedError{129 Message: mustBe("empty"),130 Path: mustBe("DATA"),131 Got: mustContain(`""`),132 Expected: mustBe("not empty"),133 })134 checkError(t, ([]int)(nil), td.NotEmpty(),135 expectedError{136 Message: mustBe("empty"),137 Path: mustBe("DATA"),138 Got: mustBe("([]int) <nil>"),139 Expected: mustBe("not empty"),140 })141 checkError(t, []int{}, td.NotEmpty(),142 expectedError{143 Message: mustBe("empty"),144 Path: mustBe("DATA"),145 Got: mustContain("([]int)"),146 Expected: mustBe("not empty"),147 })148 checkError(t, (map[string]bool)(nil), td.NotEmpty(),149 expectedError{150 Message: mustBe("empty"),151 Path: mustBe("DATA"),152 Got: mustContain("(map[string]bool) <nil>"),153 Expected: mustBe("not empty"),154 })155 checkError(t, map[string]bool{}, td.NotEmpty(),156 expectedError{157 Message: mustBe("empty"),158 Path: mustBe("DATA"),159 Got: mustContain("(map[string]bool)"),160 Expected: mustBe("not empty"),161 })162 checkError(t, (chan int)(nil), td.NotEmpty(),163 expectedError{164 Message: mustBe("empty"),165 Path: mustBe("DATA"),166 Got: mustContain("(chan int) <nil>"),167 Expected: mustBe("not empty"),168 })169 checkError(t, make(chan int), td.NotEmpty(),170 expectedError{171 Message: mustBe("empty"),172 Path: mustBe("DATA"),173 Got: mustContain("(chan int)"),174 Expected: mustBe("not empty"),175 })176 checkError(t, [0]int{}, td.NotEmpty(),177 expectedError{178 Message: mustBe("empty"),179 Path: mustBe("DATA"),180 Got: mustContain("([0]int)"),181 Expected: mustBe("not empty"),182 })183 //184 // String185 test.EqualStr(t, td.NotEmpty().String(), "NotEmpty()")186}187func TestEmptyTypeBehind(t *testing.T) {188 equalTypes(t, td.Empty(), nil)189 equalTypes(t, td.NotEmpty(), nil)190}...

Full Screen

Full Screen

productImageService_test.go

Source:productImageService_test.go Github

copy

Full Screen

...34 result, err := imageService.ProductImagesGet()35 if err != nil {36 t.Error(err)37 }38 assert.NotEmpty(t, result)39}40func TestDefaultProductImageService_ProductImageGet(t *testing.T) {41 td := productImageSetup(t)42 defer td()43 mockImageRepo.EXPECT().GetById(ProductImagesFakeData[0].Id).Return(&ProductImagesFakeData[0], nil)44 result, err := imageService.GetImageById(ProductImagesFakeData[0].Id)45 if err != nil {46 t.Error(err)47 }48 assert.NotEmpty(t, result)49}50func TestDefaultProductImageService_ProductImageInsert(t *testing.T) {51 td := productImageSetup(t)52 defer td()53 mockImageRepo.EXPECT().Insert(ProductImagesFakeData[0]).Return(ProductImagesFakeData[0].Id, nil)54 result, err := imageService.ProductImageInsert(ProductImagesFakeData[0])55 if err != nil {56 t.Error(err)57 }58 assert.NotEmpty(t, result)59}60func TestDefaultProductImageService_ProductImageDelete(t *testing.T) {61 td := productImageSetup(t)62 defer td()63 mockImageRepo.EXPECT().Delete(ProductImagesFakeData[0].Id).Return(true, nil)64 result, err := imageService.ProductImageDelete(ProductImagesFakeData[0].Id)65 if err != nil {66 t.Error(err)67 }68 assert.NotEmpty(t, result)69}70func TestDefaultProductImageService_ProductImageUpdate(t *testing.T) {71 td := productImageSetup(t)72 defer td()73 mockImageRepo.EXPECT().Update(ProductImagesFakeData[0].Id, ProductImagesFakeData[0]).Return(true, nil)74 result, err := imageService.ProductImageUpdate(ProductImagesFakeData[0].Id, ProductImagesFakeData[0])75 if err != nil {76 t.Error(err)77 }78 assert.NotEmpty(t, result)79}...

Full Screen

Full Screen

NotEmpty

Using AI Code Generation

copy

Full Screen

1import (2type td struct {3}4func (t td) NotEmpty() bool {5}6func main() {7 t := td{1, 2}8 v := reflect.ValueOf(t)9 m := v.MethodByName("NotEmpty")10 fmt.Println(m.Call(nil)[0].Bool())11}12import (13type td struct {14}15func (t td) NotEmpty() bool {16}17func main() {18 t := td{1, 2}19 v := reflect.ValueOf(t)20 m := v.MethodByName("NotEmpty")21 fmt.Println(m.Call(nil)[0].Bool())22}23import (24type td struct {25}26func (t td) NotEmpty() bool {27}28func main() {29 t := td{1, 2}30 v := reflect.ValueOf(t)31 m := v.MethodByName("NotEmpty")32 fmt.Println(m.Call(nil)[0].Bool())33}34import (35type td struct {36}37func (t td) NotEmpty() bool {38}39func main() {40 t := td{1, 2}41 v := reflect.ValueOf(t)42 m := v.MethodByName("NotEmpty")43 fmt.Println(m.Call(nil)[0].Bool())44}45import (46type td struct {47}48func (t td) NotEmpty() bool {49}50func main() {51 t := td{1, 2}

Full Screen

Full Screen

NotEmpty

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 fmt.Println("Enter a string")4 fmt.Scanln(&str)5 if td.NotEmpty(str) {6 fmt.Println("String is not empty")7 } else {8 fmt.Println("String is empty")9 }10}11import (12func main() {13 fmt.Println("Enter a string")14 fmt.Scanln(&str)15 if td.NotEmpty(str) {16 fmt.Println("String is not empty")17 } else {18 fmt.Println("String is empty")19 }20}21import (22func main() {23 fmt.Println("Enter a string")24 fmt.Scanln(&str)25 if td.NotEmpty(str) {26 fmt.Println("String is not empty")27 } else {28 fmt.Println("String is empty")29 }30}31import (32func main() {33 fmt.Println("Enter a string")34 fmt.Scanln(&str)35 if td.NotEmpty(str) {36 fmt.Println("String is not empty")37 } else {38 fmt.Println("String is empty")39 }40}41import (42func main() {43 fmt.Println("Enter a string")44 fmt.Scanln(&str)45 if td.NotEmpty(str) {46 fmt.Println("String is not empty")47 } else {48 fmt.Println("String is empty")49 }50}51import (52func main() {53 fmt.Println("Enter a string")54 fmt.Scanln(&str)55 if td.NotEmpty(str) {56 fmt.Println("String is not empty")57 } else {58 fmt.Println("String is empty")59 }60}

Full Screen

Full Screen

NotEmpty

Using AI Code Generation

copy

Full Screen

1import (2func TestNotEmpty(t *testing.T) {3 assert.NotEmpty(t, "Hello")4 assert.NotEmpty(t, "Hello", "String is not empty")5 assert.NotEmpty(t, "Hello", "String is not empty: %s", "Hello")6}7func main() {8 fmt.Println("Hello World!")9}10--- PASS: TestNotEmpty (0.00s)

Full Screen

Full Screen

NotEmpty

Using AI Code Generation

copy

Full Screen

1import (2func TestDiff(t *testing.T) {3 assert.Equal(t, "abc", "abc")4}5--- FAIL: TestDiff (0.00s)6--- FAIL: TestDiff (0.00s)7import (8func TestDiff(t *testing.T) {9 assert.Equal(t, "abc", "abc")10}11--- FAIL: TestDiff (0.00s)

Full Screen

Full Screen

NotEmpty

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 assert.NotEmpty(a, "a is empty")4 fmt.Println("a is not empty")5}6import (7func main() {8 assert.NotEmpty(a, "a is empty")9 fmt.Println("a is not empty")10}11--- FAIL: TestEmpty (0.00s)12panic(0x4b5c00, 0xc4200520d0)13testing.tRunner.func1(0xc4200c8000)14panic(0x4b5c00, 0xc4200520d0)15github.com/stretchr/testify/assert.NotEmpty(0x4c6d20, 0xc4200c8000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4c77e0, 0x7, 0x0, ...)16main.TestEmpty(0xc4200c8000)17testing.tRunner(0xc4200c8000, 0x4d2a90)18created by testing.(*T).Run

Full Screen

Full Screen

NotEmpty

Using AI Code Generation

copy

Full Screen

1import "fmt"2import "github.com/serenize/snaker"3func main() {4 td := snaker.New()5 td.NotEmpty()6 fmt.Println(td.CamelToSnake("helloWorld"))7}8import "fmt"9import "github.com/serenize/snaker"10func main() {11 td := snaker.New()12 td.SetDelimiter(" ")13 fmt.Println(td.CamelToSnake("helloWorld"))14}15import "fmt"16import "github.com/serenize/snaker"17func main() {18 td := snaker.New()19 fmt.Println(td.Split("helloWorld"))20}21import "fmt"22import "github.com/serenize/snaker"23func main() {24 td := snaker.New()25 fmt.Println(td.Split("helloWorld"))26}27import "fmt"28import "github.com/serenize/snaker"29func main() {30 td := snaker.New()31 fmt.Println(td.Split("helloWorld"))32}33import "fmt"34import "github.com/serenize/snaker"35func main() {36 td := snaker.New()37 fmt.Println(td.Split("helloWorld"))38}39import "fmt"40import "github.com/serenize/snaker"41func main() {42 td := snaker.New()43 fmt.Println(td.Split("helloWorld"))44}45import "fmt"46import "github.com/serenize/snaker"47func main() {48 td := snaker.New()49 fmt.Println(td.Split("helloWorld"))50}

Full Screen

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run Go-testdeep automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Most used method in

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful