diff --git a/web/libs/editor/tests/e2e/examples/data/sample-sin.json b/web/libs/editor/tests/e2e/examples/data/sample-sin.json new file mode 100644 index 000000000000..9b21e1609309 --- /dev/null +++ b/web/libs/editor/tests/e2e/examples/data/sample-sin.json @@ -0,0 +1,3010 @@ +{ + "timeseries": { + "time": [ + 1602707685290, + 1602707695271, + 1602707705259, + 1602707715317, + 1602707725263, + 1602707735312, + 1602707745282, + 1602707755319, + 1602707765270, + 1602707775244, + 1602707785251, + 1602707795280, + 1602707805276, + 1602707815269, + 1602707825292, + 1602707835287, + 1602707845268, + 1602707855242, + 1602707865313, + 1602707875299, + 1602707885260, + 1602707895270, + 1602707905238, + 1602707915252, + 1602707925257, + 1602707935303, + 1602707945258, + 1602707955262, + 1602707965268, + 1602707975307, + 1602707985294, + 1602707995296, + 1602708005259, + 1602708015238, + 1602708025239, + 1602708035277, + 1602708045278, + 1602708055242, + 1602708065278, + 1602708075280, + 1602708085319, + 1602708095281, + 1602708105263, + 1602708115254, + 1602708125246, + 1602708135232, + 1602708145233, + 1602708155327, + 1602708165276, + 1602708175246, + 1602708185240, + 1602708195247, + 1602708205272, + 1602708215251, + 1602708225279, + 1602708235247, + 1602708245232, + 1602708255325, + 1602708265242, + 1602708275272, + 1602708285276, + 1602708295279, + 1602708305310, + 1602708315255, + 1602708325232, + 1602708335271, + 1602708345305, + 1602708355253, + 1602708365241, + 1602708375312, + 1602708385229, + 1602708395260, + 1602708405272, + 1602708415296, + 1602708425307, + 1602708435274, + 1602708445276, + 1602708455288, + 1602708465323, + 1602708475229, + 1602708485239, + 1602708495238, + 1602708505281, + 1602708515255, + 1602708525319, + 1602708535270, + 1602708545279, + 1602708555323, + 1602708565308, + 1602708575299, + 1602708585300, + 1602708595278, + 1602708605267, + 1602708615236, + 1602708625268, + 1602708635261, + 1602708645253, + 1602708655288, + 1602708665293, + 1602708675237, + 1602708685322, + 1602708695229, + 1602708705296, + 1602708715311, + 1602708725264, + 1602708735275, + 1602708745296, + 1602708755266, + 1602708765263, + 1602708775254, + 1602708785315, + 1602708795247, + 1602708805312, + 1602708815248, + 1602708825304, + 1602708835302, + 1602708845256, + 1602708855280, + 1602708865328, + 1602708875305, + 1602708885298, + 1602708895274, + 1602708905287, + 1602708915249, + 1602708925267, + 1602708935284, + 1602708945252, + 1602708955269, + 1602708965314, + 1602708975281, + 1602708985277, + 1602708995294, + 1602709005273, + 1602709015288, + 1602709025268, + 1602709035251, + 1602709045308, + 1602709055325, + 1602709065294, + 1602709075286, + 1602709085229, + 1602709095324, + 1602709105242, + 1602709115271, + 1602709125262, + 1602709135314, + 1602709145261, + 1602709155314, + 1602709165230, + 1602709175274, + 1602709185303, + 1602709195261, + 1602709205293, + 1602709215301, + 1602709225315, + 1602709235249, + 1602709245251, + 1602709255321, + 1602709265234, + 1602709275245, + 1602709285325, + 1602709295276, + 1602709305267, + 1602709315294, + 1602709325297, + 1602709335318, + 1602709345243, + 1602709355325, + 1602709365238, + 1602709375276, + 1602709385252, + 1602709395273, + 1602709405245, + 1602709415271, + 1602709425258, + 1602709435278, + 1602709445244, + 1602709455281, + 1602709465270, + 1602709475299, + 1602709485292, + 1602709495257, + 1602709505303, + 1602709515297, + 1602709525292, + 1602709535274, + 1602709545231, + 1602709555277, + 1602709565273, + 1602709575285, + 1602709585230, + 1602709595230, + 1602709605252, + 1602709615291, + 1602709625302, + 1602709635235, + 1602709645243, + 1602709655262, + 1602709665267, + 1602709675291, + 1602709685255, + 1602709695321, + 1602709705314, + 1602709715239, + 1602709725242, + 1602709735286, + 1602709745293, + 1602709755284, + 1602709765264, + 1602709775265, + 1602709785275, + 1602709795296, + 1602709805283, + 1602709815328, + 1602709825268, + 1602709835315, + 1602709845266, + 1602709855255, + 1602709865289, + 1602709875320, + 1602709885281, + 1602709895279, + 1602709905241, + 1602709915294, + 1602709925236, + 1602709935292, + 1602709945266, + 1602709955234, + 1602709965242, + 1602709975255, + 1602709985231, + 1602709995300, + 1602710005241, + 1602710015288, + 1602710025260, + 1602710035317, + 1602710045289, + 1602710055292, + 1602710065251, + 1602710075312, + 1602710085307, + 1602710095290, + 1602710105327, + 1602710115293, + 1602710125318, + 1602710135228, + 1602710145292, + 1602710155239, + 1602710165267, + 1602710175305, + 1602710185320, + 1602710195281, + 1602710205274, + 1602710215316, + 1602710225266, + 1602710235293, + 1602710245325, + 1602710255267, + 1602710265299, + 1602710275278, + 1602710285255, + 1602710295300, + 1602710305261, + 1602710315301, + 1602710325269, + 1602710335308, + 1602710345284, + 1602710355304, + 1602710365284, + 1602710375234, + 1602710385231, + 1602710395267, + 1602710405289, + 1602710415303, + 1602710425269, + 1602710435261, + 1602710445266, + 1602710455260, + 1602710465288, + 1602710475245, + 1602710485328, + 1602710495308, + 1602710505316, + 1602710515262, + 1602710525301, + 1602710535298, + 1602710545323, + 1602710555235, + 1602710565300, + 1602710575244, + 1602710585267, + 1602710595275, + 1602710605262, + 1602710615280, + 1602710625323, + 1602710635254, + 1602710645286, + 1602710655324, + 1602710665310, + 1602710675324, + 1602710685231, + 1602710695309, + 1602710705290, + 1602710715310, + 1602710725286, + 1602710735298, + 1602710745236, + 1602710755245, + 1602710765313, + 1602710775257, + 1602710785298, + 1602710795292, + 1602710805276, + 1602710815230, + 1602710825262, + 1602710835257, + 1602710845264, + 1602710855249, + 1602710865291, + 1602710875268, + 1602710885242, + 1602710895232, + 1602710905267, + 1602710915299, + 1602710925319, + 1602710935265, + 1602710945236, + 1602710955294, + 1602710965238, + 1602710975258, + 1602710985233, + 1602710995296, + 1602711005234, + 1602711015322, + 1602711025250, + 1602711035252, + 1602711045264, + 1602711055248, + 1602711065326, + 1602711075283, + 1602711085255, + 1602711095234, + 1602711105250, + 1602711115323, + 1602711125237, + 1602711135232, + 1602711145250, + 1602711155250, + 1602711165256, + 1602711175317, + 1602711185287, + 1602711195275, + 1602711205290, + 1602711215282, + 1602711225313, + 1602711235290, + 1602711245287, + 1602711255231, + 1602711265229, + 1602711275269, + 1602711285255, + 1602711295298, + 1602711305321, + 1602711315306, + 1602711325250, + 1602711335267, + 1602711345315, + 1602711355270, + 1602711365265, + 1602711375302, + 1602711385309, + 1602711395267, + 1602711405261, + 1602711415302, + 1602711425308, + 1602711435312, + 1602711445324, + 1602711455322, + 1602711465280, + 1602711475246, + 1602711485243, + 1602711495307, + 1602711505282, + 1602711515273, + 1602711525318, + 1602711535245, + 1602711545259, + 1602711555250, + 1602711565326, + 1602711575235, + 1602711585325, + 1602711595299, + 1602711605273, + 1602711615262, + 1602711625273, + 1602711635277, + 1602711645259, + 1602711655317, + 1602711665284, + 1602711675233, + 1602711685316, + 1602711695308, + 1602711705324, + 1602711715313, + 1602711725276, + 1602711735236, + 1602711745327, + 1602711755288, + 1602711765247, + 1602711775255, + 1602711785264, + 1602711795320, + 1602711805240, + 1602711815324, + 1602711825228, + 1602711835316, + 1602711845236, + 1602711855277, + 1602711865235, + 1602711875303, + 1602711885247, + 1602711895297, + 1602711905254, + 1602711915285, + 1602711925273, + 1602711935285, + 1602711945311, + 1602711955316, + 1602711965241, + 1602711975254, + 1602711985281, + 1602711995326, + 1602712005299, + 1602712015283, + 1602712025317, + 1602712035233, + 1602712045303, + 1602712055287, + 1602712065262, + 1602712075291, + 1602712085325, + 1602712095313, + 1602712105268, + 1602712115303, + 1602712125315, + 1602712135299, + 1602712145279, + 1602712155278, + 1602712165318, + 1602712175287, + 1602712185297, + 1602712195308, + 1602712205246, + 1602712215242, + 1602712225327, + 1602712235265, + 1602712245322, + 1602712255249, + 1602712265266, + 1602712275320, + 1602712285235, + 1602712295242, + 1602712305328, + 1602712315278, + 1602712325303, + 1602712335237, + 1602712345291, + 1602712355293, + 1602712365241, + 1602712375327, + 1602712385311, + 1602712395294, + 1602712405263, + 1602712415262, + 1602712425235, + 1602712435232, + 1602712445307, + 1602712455243, + 1602712465269, + 1602712475276, + 1602712485254, + 1602712495310, + 1602712505282, + 1602712515292, + 1602712525250, + 1602712535324, + 1602712545295, + 1602712555310, + 1602712565325, + 1602712575228, + 1602712585286, + 1602712595228, + 1602712605323, + 1602712615281, + 1602712625229, + 1602712635270, + 1602712645325, + 1602712655277, + 1602712665311, + 1602712675320, + 1602712685234, + 1602712695294, + 1602712705288, + 1602712715298, + 1602712725244, + 1602712735308, + 1602712745322, + 1602712755305, + 1602712765238, + 1602712775280, + 1602712785286, + 1602712795270, + 1602712805266, + 1602712815296, + 1602712825271, + 1602712835256, + 1602712845286, + 1602712855327, + 1602712865305, + 1602712875236, + 1602712885235, + 1602712895320, + 1602712905240, + 1602712915286, + 1602712925256, + 1602712935281, + 1602712945274, + 1602712955319, + 1602712965277, + 1602712975270, + 1602712985280, + 1602712995272, + 1602713005239, + 1602713015296, + 1602713025229, + 1602713035314, + 1602713045235, + 1602713055266, + 1602713065289, + 1602713075313, + 1602713085291, + 1602713095322, + 1602713105285, + 1602713115283, + 1602713125276, + 1602713135232, + 1602713145287, + 1602713155289, + 1602713165315, + 1602713175273, + 1602713185301, + 1602713195283, + 1602713205273, + 1602713215242, + 1602713225285, + 1602713235240, + 1602713245273, + 1602713255281, + 1602713265230, + 1602713275235, + 1602713285238, + 1602713295244, + 1602713305299, + 1602713315232, + 1602713325260, + 1602713335277, + 1602713345241, + 1602713355297, + 1602713365283, + 1602713375292, + 1602713385231, + 1602713395241, + 1602713405269, + 1602713415285, + 1602713425237, + 1602713435276, + 1602713445249, + 1602713455253, + 1602713465300, + 1602713475323, + 1602713485294, + 1602713495285, + 1602713505231, + 1602713515277, + 1602713525272, + 1602713535315, + 1602713545238, + 1602713555248, + 1602713565253, + 1602713575229, + 1602713585267, + 1602713595237, + 1602713605286, + 1602713615260, + 1602713625308, + 1602713635298, + 1602713645327, + 1602713655242, + 1602713665318, + 1602713675231, + 1602713685233, + 1602713695317, + 1602713705237, + 1602713715279, + 1602713725288, + 1602713735323, + 1602713745231, + 1602713755293, + 1602713765241, + 1602713775256, + 1602713785313, + 1602713795313, + 1602713805300, + 1602713815232, + 1602713825246, + 1602713835268, + 1602713845312, + 1602713855260, + 1602713865274, + 1602713875281, + 1602713885233, + 1602713895308, + 1602713905239, + 1602713915268, + 1602713925259, + 1602713935241, + 1602713945317, + 1602713955270, + 1602713965290, + 1602713975267, + 1602713985324, + 1602713995286, + 1602714005306, + 1602714015266, + 1602714025325, + 1602714035252, + 1602714045298, + 1602714055248, + 1602714065324, + 1602714075315, + 1602714085266, + 1602714095264, + 1602714105297, + 1602714115326, + 1602714125240, + 1602714135296, + 1602714145299, + 1602714155245, + 1602714165243, + 1602714175277, + 1602714185277, + 1602714195272, + 1602714205235, + 1602714215250, + 1602714225283, + 1602714235253, + 1602714245316, + 1602714255236, + 1602714265248, + 1602714275312, + 1602714285258, + 1602714295232, + 1602714305285, + 1602714315313, + 1602714325270, + 1602714335273, + 1602714345324, + 1602714355262, + 1602714365314, + 1602714375292, + 1602714385235, + 1602714395321, + 1602714405321, + 1602714415257, + 1602714425237, + 1602714435259, + 1602714445327, + 1602714455304, + 1602714465282, + 1602714475246, + 1602714485328, + 1602714495285, + 1602714505254, + 1602714515261, + 1602714525229, + 1602714535311, + 1602714545294, + 1602714555261, + 1602714565316, + 1602714575318, + 1602714585258, + 1602714595275, + 1602714605279, + 1602714615306, + 1602714625246, + 1602714635315, + 1602714645322, + 1602714655240, + 1602714665244, + 1602714675274, + 1602714685279, + 1602714695237, + 1602714705281, + 1602714715242, + 1602714725327, + 1602714735289, + 1602714745242, + 1602714755251, + 1602714765293, + 1602714775258, + 1602714785275, + 1602714795309, + 1602714805316, + 1602714815236, + 1602714825246, + 1602714835272, + 1602714845320, + 1602714855276, + 1602714865259, + 1602714875276, + 1602714885247, + 1602714895311, + 1602714905290, + 1602714915231, + 1602714925324, + 1602714935321, + 1602714945238, + 1602714955328, + 1602714965280, + 1602714975282, + 1602714985229, + 1602714995256, + 1602715005274, + 1602715015230, + 1602715025313, + 1602715035258, + 1602715045322, + 1602715055275, + 1602715065259, + 1602715075301, + 1602715085259, + 1602715095291, + 1602715105269, + 1602715115278, + 1602715125307, + 1602715135251, + 1602715145270, + 1602715155243, + 1602715165264, + 1602715175327, + 1602715185262, + 1602715195265, + 1602715205301, + 1602715215289, + 1602715225316, + 1602715235272, + 1602715245237, + 1602715255299, + 1602715265292, + 1602715275309, + 1602715285268, + 1602715295248, + 1602715305302, + 1602715315269, + 1602715325235, + 1602715335313, + 1602715345295, + 1602715355280, + 1602715365245, + 1602715375311, + 1602715385328, + 1602715395270, + 1602715405276, + 1602715415295, + 1602715425283, + 1602715435255, + 1602715445282, + 1602715455265, + 1602715465304, + 1602715475326, + 1602715485328, + 1602715495249, + 1602715505286, + 1602715515266, + 1602715525231, + 1602715535281, + 1602715545243, + 1602715555302, + 1602715565229, + 1602715575305, + 1602715585320, + 1602715595277, + 1602715605323, + 1602715615274, + 1602715625247, + 1602715635261, + 1602715645263, + 1602715655264, + 1602715665287, + 1602715675268, + 1602715685232, + 1602715695296, + 1602715705271, + 1602715715265, + 1602715725260, + 1602715735264, + 1602715745290, + 1602715755255, + 1602715765299, + 1602715775285, + 1602715785302, + 1602715795264, + 1602715805294, + 1602715815318, + 1602715825280, + 1602715835313, + 1602715845316, + 1602715855246, + 1602715865234, + 1602715875243, + 1602715885312, + 1602715895309, + 1602715905289, + 1602715915300, + 1602715925307, + 1602715935273, + 1602715945322, + 1602715955295, + 1602715965306, + 1602715975294, + 1602715985257, + 1602715995243, + 1602716005312, + 1602716015276, + 1602716025301, + 1602716035259, + 1602716045269, + 1602716055249, + 1602716065286, + 1602716075267, + 1602716085249, + 1602716095274, + 1602716105303, + 1602716115234, + 1602716125316, + 1602716135318, + 1602716145253, + 1602716155244, + 1602716165261, + 1602716175238, + 1602716185268, + 1602716195255, + 1602716205268, + 1602716215278, + 1602716225242, + 1602716235305, + 1602716245255, + 1602716255249, + 1602716265263, + 1602716275298, + 1602716285275, + 1602716295267, + 1602716305287, + 1602716315272, + 1602716325317, + 1602716335242, + 1602716345247, + 1602716355294, + 1602716365303, + 1602716375244, + 1602716385234, + 1602716395279, + 1602716405318, + 1602716415323, + 1602716425290, + 1602716435298, + 1602716445316, + 1602716455269, + 1602716465275, + 1602716475307, + 1602716485306, + 1602716495323, + 1602716505247, + 1602716515240, + 1602716525248, + 1602716535234, + 1602716545301, + 1602716555318, + 1602716565274, + 1602716575256, + 1602716585304, + 1602716595269, + 1602716605321, + 1602716615254, + 1602716625270, + 1602716635281, + 1602716645312, + 1602716655260, + 1602716665258, + 1602716675328, + 1602716685233, + 1602716695307, + 1602716705298, + 1602716715310, + 1602716725243, + 1602716735242, + 1602716745291, + 1602716755298, + 1602716765260, + 1602716775240, + 1602716785274, + 1602716795310, + 1602716805324, + 1602716815251, + 1602716825282, + 1602716835268, + 1602716845308, + 1602716855317, + 1602716865240, + 1602716875264, + 1602716885270, + 1602716895327, + 1602716905264, + 1602716915245, + 1602716925266, + 1602716935273, + 1602716945233, + 1602716955320, + 1602716965271, + 1602716975268, + 1602716985281, + 1602716995308, + 1602717005294, + 1602717015291, + 1602717025240, + 1602717035305, + 1602717045266, + 1602717055300, + 1602717065268, + 1602717075324, + 1602717085257, + 1602717095266, + 1602717105233, + 1602717115258, + 1602717125290, + 1602717135251, + 1602717145247, + 1602717155230, + 1602717165265, + 1602717175275, + 1602717185250, + 1602717195270, + 1602717205278, + 1602717215252, + 1602717225241, + 1602717235288, + 1602717245264, + 1602717255280, + 1602717265309, + 1602717275262, + 1602717285261, + 1602717295327, + 1602717305304, + 1602717315310, + 1602717325246, + 1602717335245, + 1602717345255, + 1602717355231, + 1602717365261, + 1602717375308, + 1602717385258, + 1602717395277, + 1602717405280, + 1602717415283, + 1602717425290, + 1602717435290, + 1602717445251, + 1602717455285, + 1602717465240, + 1602717475301, + 1602717485229, + 1602717495323, + 1602717505262, + 1602717515254, + 1602717525311, + 1602717535275, + 1602717545254, + 1602717555284, + 1602717565325, + 1602717575301, + 1602717585258, + 1602717595315, + 1602717605310, + 1602717615282, + 1602717625244, + 1602717635306, + 1602717645266, + 1602717655274, + 1602717665280, + 1602717675273 + ], + "one": [ + 37.03620982977559, + 16.8961637786484, + 49.29810756459162, + 66.41300313283884, + 34.77100583247356, + 58.11684303722001, + 84.35846790031925, + 50.727889108984215, + 90.56566064825813, + 93.10636411810053, + 99.25549063297521, + 94.90613396884045, + 114.33633602827135, + 95.83111506644275, + 83.48146950170867, + 103.61445992488154, + 146.06931528855873, + 144.48361191286608, + 149.59479594730706, + 154.20929908058707, + 165.39135728978562, + 123.83270419031122, + 161.40287948278828, + 129.2083612075148, + 185.373455128006, + 171.6384434542441, + 173.80071440011466, + 161.09072281077206, + 160.10006569000004, + 171.92741097359263, + 184.03989131424015, + 165.7452092442041, + 205.40153222875813, + 184.2415239843076, + 164.69151590761925, + 172.56631635558531, + 193.58977203042156, + 164.9529133641715, + 219.9107443415817, + 204.8812683532066, + 209.5000983832279, + 189.06696340286788, + 215.19805569917747, + 211.91195073388596, + 174.25700854649656, + 189.33533464774382, + 174.85465556768068, + 228.7372593810509, + 188.73144633280356, + 227.6571454806826, + 180.08383918343824, + 178.65981295212205, + 174.29443425653452, + 174.9538312071245, + 197.56047498714494, + 195.42225877687437, + 167.78613932762613, + 206.14839910167393, + 168.60699992102533, + 169.11465797498047, + 191.33602973577771, + 181.871157263041, + 207.80457709887122, + 184.29555559160255, + 172.59328591380597, + 161.37011349649774, + 164.79598423325328, + 188.2481830379336, + 192.5711015009636, + 200.56759657948237, + 169.60107719421944, + 150.57796828257545, + 173.61599355332075, + 155.85135087747912, + 158.66778126316206, + 173.39359507274892, + 185.0815740680075, + 179.48573585362377, + 172.19990147400537, + 171.49876003156433, + 138.28388010176616, + 179.82863027910494, + 178.91710248085684, + 161.25210131895778, + 158.7418643121636, + 121.24075198340927, + 118.83482999410825, + 147.77276076683043, + 122.88558103236045, + 143.44302089304676, + 112.41446648684244, + 141.17594014253856, + 147.31630754511247, + 127.41896802261135, + 125.24894478755083, + 141.28979620351234, + 146.8474598497446, + 143.32431747777525, + 155.37724615575425, + 142.7490361686633, + 119.92760940131483, + 107.05447353171692, + 126.71656776178429, + 137.48467671185654, + 115.7345726665841, + 154.34556335422963, + 101.52504447156892, + 112.39499999177751, + 104.99423993324147, + 149.30570151410467, + 106.6517649378346, + 150.26601350226935, + 133.74797568254357, + 135.97987206024143, + 101.62795509240237, + 142.50793419054088, + 141.5369822077268, + 137.23845045158643, + 129.34468335856388, + 127.76456802515216, + 109.311240264339, + 129.41308517549925, + 155.19411309575645, + 144.92931541892526, + 140.0553113711135, + 137.00825067593087, + 149.58472556753472, + 152.48285277949637, + 153.93671129214954, + 136.51212374131836, + 119.64652356572955, + 125.73439970137842, + 135.53361119285282, + 108.26319237266961, + 122.61051389434184, + 137.81987511013278, + 129.78985880244568, + 161.30496870788636, + 140.3733400195162, + 146.92263437593206, + 104.99617824863171, + 136.63854870776737, + 158.47340372179374, + 106.0446989838948, + 140.7521759965705, + 102.85489835310128, + 122.64136529226928, + 114.96366261271923, + 105.87503400073706, + 111.40985615256243, + 107.08045338991971, + 121.38972169184161, + 113.51238421532739, + 143.01689140447476, + 123.60761347406782, + 119.99812499524461, + 100.71972716897332, + 99.5163945446057, + 143.1120586489253, + 133.30108178475905, + 132.89379804709614, + 83.37783827481934, + 109.89769144152183, + 117.44289736716598, + 119.17566308868481, + 119.92657011426579, + 78.69510835446546, + 94.12567314877718, + 65.13964540111374, + 108.05979123139656, + 114.15277617034228, + 58.988423735500675, + 89.54796260375758, + 101.78778332797506, + 60.43235749926134, + 69.17704305488604, + 62.312990407280424, + 90.78387731222799, + 84.61437368218691, + 75.12302227911084, + 62.088045911657474, + 66.49677421488457, + 35.12570004940379, + 44.828590994037235, + 17.836661711066615, + 36.37229221395488, + 21.238619654773366, + 52.40801076004285, + 43.364293869657594, + 18.529441630534905, + 15.34307848649093, + -1.8454189533429926, + 14.172331748694468, + 32.35045829787427, + -9.272967458495902, + 24.64619318375083, + 16.16564111842803, + -17.26538253146252, + -15.464674805384874, + 15.213959978372323, + 14.790591752149197, + 5.253293068278076, + -6.566243684750333, + -14.505745534656615, + 17.45828780611518, + 13.93987208314168, + -41.26591091347484, + -0.17478275300927493, + 0.8632635380559535, + -9.521304851379647, + 0.5516823576515506, + 7.962911617163236, + -19.949359831624616, + -5.772697517030437, + -39.73193856629793, + -50.68352861987721, + -18.219593994212985, + -23.139899620420273, + -31.91796828918963, + -8.592271131257725, + -20.338985562631336, + -16.91781331752267, + 2.288291438688667, + 12.337276311041848, + -10.93160373190402, + 14.627937056361631, + 4.073936585970436, + -29.082382786776403, + -8.681959635946093, + 24.018732235080464, + -10.506983406915325, + 25.74730100566084, + 12.763014656403264, + 32.95417006482374, + 31.79161849008122, + -11.17799047780355, + 24.10809189187118, + 0.6344873707933942, + -3.3987857482314014, + 3.934274340875774, + 37.13276477370155, + 6.905315230293392, + 55.431069010645565, + 68.7941698799516, + 73.83093619436103, + 22.710685575725453, + 77.55811643006547, + 66.7710548846955, + 60.044689848155585, + 57.034995024501576, + 82.38810744729071, + 56.06673058437107, + 62.47577749938577, + 59.67202529656878, + 63.18449211838183, + 80.27801229621852, + 117.06009833647803, + 113.95469569648515, + 98.48051748922529, + 78.42215740322227, + 84.48336260660759, + 115.88997728294774, + 115.32604472875315, + 98.97172465509628, + 107.06649373557877, + 117.20623938616967, + 138.12149889339548, + 115.0685889114704, + 130.89332153087807, + 115.074590790773, + 165.1210479848923, + 153.8300498654595, + 150.8047420184761, + 166.9799341482218, + 166.62165208905668, + 155.37191892107123, + 130.51967048411336, + 189.30495166823, + 157.03931456016738, + 192.95933338991654, + 184.90526400892378, + 151.61765416982368, + 158.58923931393974, + 184.60214891055116, + 165.52305102709576, + 184.14361412139772, + 200.486923707275, + 176.6422523741312, + 156.85579927212535, + 200.22459209003298, + 181.88754942894002, + 169.78224768512732, + 170.1578006229115, + 191.89138626831704, + 192.503666533134, + 171.93510446734854, + 151.982509372278, + 150.19532447719763, + 149.9359593988638, + 183.63849798066684, + 159.7000652770664, + 176.02340868187127, + 179.8176391080522, + 180.77596712458387, + 175.5610819429371, + 155.76911505411437, + 149.3133842628528, + 141.95718886560414, + 131.0680972731796, + 113.5963164056549, + 147.1738055201289, + 152.34372809687235, + 123.05025839769478, + 137.4282259001729, + 136.38659874156002, + 142.91473318697436, + 133.04175416846923, + 146.51444562642462, + 98.0974337487937, + 100.89523164524557, + 108.35182105847065, + 95.04757003548215, + 118.76378378961488, + 125.0754551621323, + 79.44083160631332, + 106.60954271449964, + 76.48802846520358, + 72.66107495860933, + 54.08094807926947, + 89.15655259967272, + 83.48611285944318, + 91.14689839854748, + 81.95468468105555, + 40.36831166977962, + 75.70439158511631, + 64.269420917759, + 36.23205734223097, + 73.25604089580284, + 53.16954534876516, + 33.110040995902786, + 35.26943459105837, + 56.367977661663375, + 63.574588943940256, + 19.451865016666513, + 58.5940505599862, + 56.030171788553396, + 36.036359123227605, + 32.71482911959865, + 16.09237137468998, + 28.18765643191366, + 32.011417592294514, + 10.600671643587503, + 22.880390390389273, + 13.096309360754244, + 9.607538154468969, + -0.7604935301237727, + -2.3762818833525188, + -15.338179239447495, + -14.845983382852653, + 12.593979599749588, + 14.765549745607633, + 22.830965903437775, + 13.020406432275692, + 20.758424737218267, + -14.107573317905924, + -12.86911418702007, + 7.793152421689573, + 2.5627432447345306, + 6.63471280389324, + 18.822671834159813, + 8.995727831216634, + 20.643134960839262, + -5.035279673808567, + -14.249089725765568, + -4.185126472740954, + 11.89870108752553, + 13.778914297462485, + 10.122893927049894, + 19.235688000894946, + -23.11495849450944, + -30.37801901286177, + 19.741993944462585, + -21.411058825099033, + -6.563311544496152, + -13.542727119885527, + 11.778464595564976, + -22.089150403921607, + -24.29893155292889, + -11.90040008285792, + -20.856837880839347, + 2.6090057821249673, + -20.463974837644326, + -34.80042266337493, + -46.28185386070532, + -25.12881720007126, + -17.455583730873386, + -13.778287704616105, + -3.1131832256002667, + -43.94859565008565, + -50.702670560208105, + -25.667548630231785, + -36.56471163526952, + -20.123700247532426, + -21.460888810689646, + -43.03024156966646, + -47.30554390676319, + -65.39323502216949, + -65.43333590236432, + -32.319175501513705, + -25.28880464351284, + -41.52106166852366, + -49.88631810381539, + -78.72667107829841, + -73.83312240285166, + -87.87797922987824, + -63.2990682914059, + -79.28097718172785, + -90.51020560842235, + -70.06585379390468, + -79.86788394171573, + -113.8078864913276, + -66.26393707874948, + -115.28719850746006, + -72.49192312499574, + -112.471551197887, + -136.30705944377732, + -98.34071675429107, + -112.27288860186896, + -109.64312890465942, + -127.19460646898966, + -133.75593686863792, + -104.95332226348057, + -125.94287951709315, + -153.98745431987416, + -165.0213869945511, + -137.814725868225, + -157.51167762546297, + -167.58829421418008, + -168.28726500466774, + -131.5876920217247, + -156.42711746603948, + -136.71014098610124, + -139.2999723703883, + -145.6576378994739, + -175.37543539409364, + -181.653580221646, + -166.9565506300392, + -156.64597656783053, + -210.75997817765005, + -202.55666319096488, + -212.6571435863847, + -215.55830462279698, + -180.8814639274485, + -177.30489425581902, + -197.47434510709746, + -187.1739874672055, + -203.91873605298994, + -209.90488595803242, + -180.23256443157445, + -213.8955949105111, + -220.2995256710347, + -183.93998417330351, + -221.71594867616597, + -199.41753781224315, + -191.5711878850026, + -204.0853304505439, + -237.53462114951319, + -216.31111030430588, + -216.93071999849877, + -204.9049516644199, + -226.0569518783836, + -229.4222442584649, + -189.51656383524977, + -215.56057083096707, + -199.42879559594263, + -196.13375496245533, + -183.0279179837235, + -203.9837504405301, + -169.73380794746527, + -199.53660967477873, + -194.82697302885623, + -206.01376986029214, + -161.46146238097307, + -186.43547351094395, + -195.41796084285969, + -194.9838669987035, + -185.02936489124795, + -157.78094788127666, + -190.3418322346723, + -178.9477070901038, + -178.38351749653643, + -141.31141571876796, + -171.14487819582322, + -158.96185317116652, + -119.35608787781379, + -116.31681004845515, + -126.00590460554132, + -133.16844493022117, + -136.1061810404656, + -110.48992640014527, + -126.61438170240021, + -116.29413665920367, + -74.52336613423645, + -83.98518794890848, + -105.8713736062597, + -91.91121264770348, + -50.9753050791814, + -63.738724788767925, + -52.639410361223085, + -63.51591788315696, + -64.50607520558593, + -54.17108393230111, + -26.100048087959706, + -27.67059349650959, + -11.629654079091566, + -53.18023206204362, + -52.892205399162656, + -31.272687591157617, + -15.84121386554181, + -0.4435031191872838, + -11.897533752440445, + -9.689911239600011, + -19.749716215749878, + 12.347412789668489, + -7.629938266891719, + 32.0159838071728, + -2.715068195716576, + 46.40400837671401, + 44.88831620891453, + 36.648600109468624, + 9.37300122281486, + 60.57833051765774, + 35.9911620010534, + 35.80177932156548, + 47.59002593872466, + 34.80655280047216, + 64.10925429234507, + 56.37715209072721, + 82.57283479422571, + 69.31709098745375, + 40.67050215175826, + 75.86373208124229, + 84.486308941667, + 47.36895517891964, + 65.89844976185256, + 81.07353543420365, + 81.88144958320805, + 87.21367173804228, + 93.54117369159349, + 61.61675739253328, + 48.015869650315246, + 40.117586645245865, + 88.89746953558344, + 99.98796558671894, + 51.15497535543708, + 53.25315371285361, + 83.03941150067685, + 58.48137085157417, + 52.900188440297526, + 67.8684289059646, + 85.65389089127706, + 75.71647364159972, + 86.84898984976839, + 61.49407340530006, + 89.51543655378671, + 76.20833655762496, + 55.601645423962566, + 84.56735110113578, + 40.13247181645257, + 77.45550458977658, + 34.55963933491154, + 52.660068978426665, + 86.62886789652327, + 30.40030670786043, + 57.46387348674696, + 83.098847282247, + 40.482125133092225, + 73.16204346388011, + 30.4857007993763, + 22.645336323266026, + 56.0200794863957, + 51.77469250126795, + 67.1546560196787, + 57.87691608694495, + 17.41759720151727, + 54.878109106701125, + 47.493283372059445, + 64.57786770483617, + 48.993636626937906, + 34.1951786665862, + 36.3257696640557, + 14.568419490363965, + 63.23317546125337, + 26.418963234489517, + 15.31591248775866, + 53.99452831401371, + 22.985585125801872, + 15.244261841435254, + 15.23320499798178, + 59.28898624752186, + 48.983103088031285, + 18.450586805496407, + 35.37513701009135, + 35.31874809381071, + 62.220022028913625, + 25.20158903554477, + 72.47043701948445, + 49.47528245085166, + 17.745819537598152, + 57.03771073288418, + 62.3287043719062, + 65.662768941524, + 63.25011497635633, + 23.289204458704578, + 41.393225930912166, + 73.59501176387027, + 41.535832393379785, + 64.37236779550251, + 72.73765654594138, + 45.48900839495211, + 36.31587332826628, + 73.06956417695692, + 55.1598276517079, + 56.29052053695725, + 34.383252772599526, + 43.887287166806885, + 53.070289648631835, + 56.72548828406186, + 66.76859604323485, + 72.38274423960786, + 85.6341943921511, + 67.89554907928215, + 42.3776667606313, + 43.00662936071367, + 73.85951401947919, + 50.97403825523812, + 68.654898648826, + 69.76645870972168, + 91.80786484702291, + 94.20892590318653, + 73.50291525633878, + 63.23291432088306, + 73.88421574602562, + 70.49146714322764, + 102.71380005964126, + 99.42153275576707, + 92.06989401296948, + 111.50301413714867, + 94.66342298368384, + 103.29591018789162, + 91.84402167849802, + 83.94310914943563, + 82.8125796151381, + 73.3562654303183, + 101.80043137225377, + 107.80662349082351, + 95.27722522080565, + 71.81467649892133, + 86.60010093790301, + 84.95639197669398, + 47.8226256516017, + 64.12146255424244, + 45.09154603073557, + 76.42982041356987, + 50.46009054508337, + 78.6166059326094, + 92.86183373911632, + 38.297782341368205, + 58.9299706177996, + 55.89894202222745, + 77.8233515654263, + 33.02241035573655, + 78.62455450093327, + 31.598734151030524, + 21.067593095542144, + 20.191034028285053, + 30.77286098281669, + 12.218727266680862, + 63.65774554204725, + 41.923375149758186, + 58.12104212072491, + 29.605547811809856, + 22.59694723494056, + 15.487356055957473, + 3.974129006086411, + -0.20028201406429247, + 14.490088845662548, + -9.463395126295264, + -9.036101872829681, + -15.991779736900444, + -29.087589733227002, + 24.80789681699087, + 24.08515277928742, + 18.643593559037782, + -10.048052302910243, + -22.296350099365313, + -19.900681526080522, + -40.51956856261426, + 7.995279615065584, + -23.75762059263617, + -36.21280451581157, + -51.30653628838034, + -14.973451850015927, + -12.102776734108886, + -22.92141011390678, + -38.03739604911054, + -43.27241280945676, + -30.319328991987007, + -24.66755064542329, + -50.80561689390146, + -7.482679556753894, + -36.392132826717656, + -44.708312560033676, + -24.58273469245448, + -36.019147755802365, + -9.07348428587322, + -49.734001673806816, + -50.75452558860551, + -40.34966717413306, + -47.7580042804002, + -17.99447417787585, + -11.560948317519355, + -44.83684970566581, + -32.21333876901835, + -59.11691388047349, + -43.53779144648816, + -53.77359463006016, + -34.9652704766076, + -23.07934600270852, + -26.026669192313005, + 2.385634574131913, + -18.70104485669859, + -11.300389325321689, + 6.125584826302301, + 23.37806794844431, + 22.687228094063634, + -12.781593968580395, + 4.024895629623511, + -18.177437536079594, + 0.2663084532167517, + 29.698074030663772, + 34.02883642190601, + 33.43024786117799, + 23.76733022232676, + 54.17568555199081, + 9.443772300891151, + 50.71547323216697, + 30.62246906621458, + 56.47090430219434, + 60.74774647230859, + 58.0637733531682, + 45.639675601221555, + 45.08497419547462, + 82.30094212732857, + 64.96007607663661, + 67.1067875438888, + 101.27912636885212, + 113.44260613045327, + 117.63961207716953, + 76.57700523076957, + 106.78276552404587, + 135.63445748275967, + 125.46532424590501, + 109.41361019604425, + 111.1898571652688, + 154.03133140720854, + 110.16651672260186, + 115.1117379105762, + 151.97073079358177, + 167.2799953757663, + 150.92207831359377, + 189.06944558917516, + 177.98134138898382, + 183.59158986891072, + 168.9854230066182, + 157.19661953986713, + 191.54398019286833, + 185.581256719504, + 216.8584439554592, + 212.03212705829267, + 214.94509266903424, + 184.0472317716919, + 184.45100053169907, + 215.94288942018233, + 221.46986307563412, + 217.31009278486258, + 205.70291032222957, + 191.05597234194158, + 216.56367975266082, + 242.13532768493792, + 198.5688100198425, + 224.47493819269562, + 206.43918139577872, + 195.77914977448964, + 219.7158477209509, + 226.9544745423874, + 247.08763173720297, + 230.1312912650799, + 233.48892202541003, + 226.53187497367105, + 236.7443173336522, + 209.51612565284768, + 246.44058255732867, + 216.6046096558702, + 237.94045335342133, + 204.39929606976298, + 198.8653653946592, + 196.71700144176478, + 208.57619221588976, + 248.6702103768165, + 247.72840295120278, + 213.30271282901322, + 229.53134212998327, + 226.62201260012984, + 222.59591450075877, + 228.00479951463853, + 212.16892618454526, + 225.26419263148503, + 234.77964676970612, + 225.16698144456706, + 210.54173452558967, + 197.62070284837154, + 227.67369671642822, + 192.69235751914007, + 209.37202977785307, + 171.84575020834876, + 204.35901720000254, + 180.710386928622, + 215.93323544468197, + 169.28193247452643, + 160.78259662298672, + 212.11358448981866, + 168.1205288104478, + 165.22595195634003, + 174.00640560164337, + 181.49192271846607, + 201.27697673975803, + 176.3898072199412, + 172.82166493933633, + 199.2119263934316, + 139.7113400862038, + 139.44835065899542, + 167.656089617775, + 163.19864272385533, + 142.13590943908247, + 160.65441254454788, + 132.79006006458502, + 146.8007870451806, + 129.8221440428195, + 154.9981676706181, + 156.239647368755, + 174.37444772112264, + 141.54411202897427, + 126.37902605644943, + 126.63064036066785, + 144.475538193849, + 133.9653096913545, + 126.68079606860756, + 167.2200079640907, + 128.302397128574, + 165.95081194859833, + 155.6886986075325, + 124.0639520337419, + 124.5822340552818, + 159.50629965722086, + 171.86733728017842, + 132.58024511107442, + 149.43199725504644, + 115.28529859850413, + 138.1141289726093, + 162.71643472655472, + 124.63362898737334, + 121.22246289627155, + 132.20688964015653, + 120.051631816624, + 138.17205091830962, + 167.0686961702507, + 112.58890625024416, + 120.8056168337006, + 154.8765892293838, + 118.2925043142191, + 119.33542414059771, + 117.60165284894256, + 124.73164337030329, + 139.39521246705908, + 130.22309574168804, + 158.01501184558757, + 126.6341012253639, + 125.64055822192584, + 149.1414581737555, + 149.1749365230358, + 110.08734856226843, + 148.66243454279754, + 104.62526535773817, + 107.88768216514309, + 99.52703512581778, + 148.1683219154004, + 141.9569617235268, + 146.17232589785766, + 131.21033237251078, + 111.03055071737319, + 124.61430379706525, + 138.95203938686598, + 131.9123936043433, + 104.87510720051091, + 125.57058377977525, + 110.48817921955823, + 74.54514500049592, + 125.03382650808186, + 97.15929624757464, + 97.9433585837035, + 73.0017002826365, + 77.19261849445766, + 87.25230366657115, + 78.55546487918744, + 81.43247977022205, + 99.04188759502969, + 88.51690074505214, + 74.14598371176263, + 40.55976338644172, + 82.3399003384706, + 56.57608109007839, + 71.97539752267141, + 44.49605497703074, + 61.137446214883624, + 26.855959703764693, + 5.759185976780195, + 32.469607474829914, + 10.019461328137524, + 4.2380292782364535, + -1.8170551054217547, + -18.73290516935153, + -0.7243773353155305, + -0.7455892024436537, + -12.506165623330268, + -37.314621455031485, + -50.26847159607498, + -23.684054749044577, + -43.714897249838884, + -27.560834125382158, + -65.92156522987374, + -65.80289160938011, + -75.78156002111317, + -78.37774602993623, + -61.72990301702971, + -55.03187231073241, + -84.4936660460418, + -95.04128748737972, + -107.84100080068161, + -71.86684355142842, + -116.42026333988275, + -115.6197642656727, + -102.30226919293312, + -126.75550054723168, + -103.24100123123502, + -107.29371421953914, + -82.27400557125733, + -104.79244662967926, + -107.5573254870045, + -100.4536883913838, + -119.6513430965621, + -132.76253575875796, + -153.38419853777066, + -161.60708969802897, + -110.62759268400096, + -163.5760629792642, + -148.28160645329808, + -139.6796520256861, + -120.76878034790437, + -117.01145098266258, + -135.32272959100268, + -145.7653120340161, + -175.64702357968375, + -122.84965099640303, + -136.66369209205288, + -141.55679415700865, + -177.0439967777879, + -130.82127859560322, + -153.2307284872943, + -149.5885922447043, + -173.87683551499896, + -128.21427480771, + -167.5073700396225, + -136.4587090769843, + -117.20404582492924, + -115.7483250025667, + -163.08837968081792, + -165.9571799635692, + -151.49876950021172, + -132.75583227211303, + -149.44035340544457, + -131.46093129514352, + -113.67776700524311, + -106.9053290330692, + -150.15662250802313, + -131.3581649406843, + -137.18732218023973, + -126.53294282815509, + -101.34478878028523, + -89.1798376237136 + ], + "two": [ + 0, + -20.140046051127193, + 32.40194378594322, + 17.11489556824722, + -31.641997300365276, + 23.345837204746445, + 26.241624863099247, + -33.63057879133504, + 39.837771539273916, + 2.5407034698423985, + 6.1491265148746805, + -4.34935666413476, + 19.430202059430897, + -18.5052209618286, + -12.349645564734075, + 20.13299042317287, + 42.45485536367718, + -1.5857033756926455, + 5.111184034440981, + 4.61450313328001, + 11.182058209198544, + -41.5586530994744, + 37.57017529247706, + -32.19451827527348, + 56.16509392049119, + -13.735011673761875, + 2.162270945870546, + -12.709991589342593, + -0.9906571207720276, + 11.827345283592592, + 12.112480340647522, + -18.29468207003606, + 39.65632298455404, + -21.160008244450523, + -19.550008076688357, + 7.8748004479660665, + 21.023455674836242, + -28.63685866625005, + 54.9578309774102, + -15.029475988375111, + 4.618830030021314, + -20.433134980360023, + 26.13109229630959, + -3.286104965291514, + -37.654942187389395, + 15.078326101247256, + -14.48067908006314, + 53.882603813370224, + -40.00581304824735, + 38.925699147879044, + -47.57330629724436, + -1.4240262313161907, + -4.3653786955875375, + 0.6593969505899793, + 22.60664378002045, + -2.1382162102705706, + -27.63611944924824, + 38.3622597740478, + -37.5413991806486, + 0.5076580539551401, + 22.22137176079724, + -9.464872472736715, + 25.93341983583022, + -23.50902150726867, + -11.70226967779658, + -11.223172417308234, + 3.4258707367555417, + 23.452198804680307, + 4.322918463030021, + 7.9964950785187625, + -30.966519385262927, + -19.023108911643988, + 23.038025270745294, + -17.76464267584163, + 2.816430385682935, + 14.725813809586867, + 11.68797899525859, + -5.595838214383747, + -7.285834379618393, + -0.7011414424410418, + -33.21487992979817, + 41.544750177338784, + -0.9115277982481018, + -17.665001161899056, + -2.5102370067941706, + -37.501112328754346, + -2.405921989301021, + 28.937930772722183, + -24.88717973446998, + 20.557439860686316, + -31.02855440620432, + 28.761473655696122, + 6.140367402573901, + -19.897339522501113, + -2.170023235060526, + 16.040851415961512, + 5.557663646232271, + -3.5231423719693566, + 12.052928677978997, + -12.62820998709094, + -22.821426767348484, + -12.873135869597903, + 19.662094230067368, + 10.768108950072246, + -21.750104045272437, + 38.610990687645526, + -52.82051888266071, + 10.869955520208592, + -7.4007600585360365, + 44.311461580863195, + -42.65393657627007, + 43.614248564434746, + -16.518037819725777, + 2.231896377697865, + -34.35191696783906, + 40.879979098138506, + -0.9709519828140856, + -4.298531756140363, + -7.8937670930225465, + -1.580115333411726, + -18.453327760813153, + 20.101844911160242, + 25.781027920257202, + -10.264797676831193, + -4.874004047811752, + -3.047060695182637, + 12.576474891603851, + 2.8981272119616506, + 1.45385851265317, + -17.424587550831177, + -16.86560017558881, + 6.0878761356488695, + 9.799211491474395, + -27.27041882018321, + 14.347321521672228, + 15.209361215790949, + -8.030016307687106, + 31.51510990544068, + -20.931628688370154, + 6.5492943564158566, + -41.926456127300355, + 31.642370459135662, + 21.834855014026374, + -52.42870473789894, + 34.70747701267568, + -37.89727764346921, + 19.786466939168008, + -7.677702679550052, + -9.088628611982173, + 5.534822151825367, + -4.329402762642715, + 14.309268301921904, + -7.877337476514228, + 29.504507189147375, + -19.409277930406944, + -3.6094884788232093, + -19.278397826271288, + -1.2033326243676186, + 43.59566410431961, + -9.810976864166264, + -0.40728373766290815, + -49.5159597722768, + 26.51985316670249, + 7.545205925644154, + 1.732765721518831, + 0.7509070255809718, + -41.231461759800325, + 15.43056479431172, + -28.986027747663442, + 42.92014583028282, + 6.092984938945719, + -55.164352434841604, + 30.559538868256908, + 12.23982072421748, + -41.35542582871372, + 8.744685555624699, + -6.864052647605618, + 28.470886904947562, + -6.169503630041078, + -9.491351403076067, + -13.034976367453368, + 4.408728303227093, + -31.371074165480778, + 9.702890944633445, + -26.99192928297062, + 18.535630502888264, + -15.133672559181512, + 31.169391105269483, + -9.043716890385255, + -24.83485223912269, + -3.186363144043975, + -17.188497439833924, + 16.01775070203746, + 18.1781265491798, + -41.62342575637017, + 33.91916064224674, + -8.4805520653228, + -33.43102364989055, + 1.800707726077647, + 30.678634783757197, + -0.42336822622312553, + -9.537298683871121, + -11.819536753028409, + -7.939501849906282, + 31.964033340771795, + -3.5184157229735007, + -55.20578299661652, + 41.091128160465566, + 1.0380462910652284, + -10.3845683894356, + 10.072987209031197, + 7.4112292595116855, + -27.912271448787852, + 14.176662314594179, + -33.95924104926749, + -10.951590053579281, + 32.46393462566422, + -4.920305626207288, + -8.778068668769357, + 23.325697157931906, + -11.746714431373611, + 3.421172245108668, + 19.206104756211335, + 10.04898487235318, + -23.268880042945867, + 25.55954078826565, + -10.554000470391195, + -33.156319372746836, + 20.40042315083031, + 32.70069187102656, + -34.52571564199579, + 36.25428441257617, + -12.984286349257577, + 20.191155408420478, + -1.1625515747425226, + -42.96960896788477, + 35.28608236967473, + -23.473604521077785, + -4.033273119024796, + 7.3330600891071755, + 33.19849043282578, + -30.22744954340816, + 48.525753780352176, + 13.363100869306031, + 5.036766314409434, + -51.12025061863558, + 54.84743085434002, + -10.787061545369966, + -6.726365036539917, + -3.0096948236540086, + 25.35311242278913, + -26.321376862919635, + 6.409046915014699, + -2.8037522028169874, + 3.5124668218130495, + 17.093520177836687, + 36.782086040259514, + -3.1054026399928887, + -15.474178207259854, + -20.05836008600302, + 6.061205203385313, + 31.406614676340155, + -0.5639325541945936, + -16.354320073656865, + 8.094769080482493, + 10.139745650590896, + 20.915259507225812, + -23.052909981925083, + 15.82473261940767, + -15.818730740105067, + 50.0464571941193, + -11.2909981194328, + -3.025307846983395, + 16.17519212974568, + -0.3582820591651057, + -11.249733167985454, + -24.852248436957865, + 58.78528118411663, + -32.265637108062606, + 35.920018829749154, + -8.054069380992757, + -33.2876098391001, + 6.971585144116062, + 26.012909596611422, + -19.079097883455404, + 18.620563094301957, + 16.34330958587728, + -23.844671333143793, + -19.78645310200585, + 43.36879281790763, + -18.337042661092966, + -12.105301743812703, + 0.3755529377841924, + 21.733585645405526, + 0.6122802648169738, + -20.56856206578547, + -19.952595095070535, + -1.7871848950803724, + -0.25936507833384326, + 33.702538581803054, + -23.938432703600455, + 16.323343404804888, + 3.794230426180917, + 0.9583280165316808, + -5.214885181646764, + -19.791966888822742, + -6.455730791261573, + -7.356195397248655, + -10.889091592424535, + -17.4717808675247, + 33.57748911447399, + 5.16992257674346, + -29.293469699177578, + 14.377967502478114, + -1.0416271586128687, + 6.528134445414338, + -9.872979018505134, + 13.472691457955392, + -48.41701187763091, + 2.7977978964518684, + 7.456589413225075, + -13.304251022988495, + 23.716213754132724, + 6.311671372517424, + -45.634623555818976, + 27.168711108186315, + -30.121514249296055, + -3.8269535065942506, + -18.580126879339865, + 35.07560452040325, + -5.67043974022954, + 7.660785539104296, + -9.192213717491924, + -41.58637301127593, + 35.33607991533669, + -11.434970667357305, + -28.037363575528033, + 37.02398355357187, + -20.08649554703768, + -20.059504352862376, + 2.159393595155585, + 21.098543070605004, + 7.2066112822768815, + -44.12272392727374, + 39.142185543319684, + -2.5638787714328046, + -19.99381266532579, + -3.3215300036289577, + -16.62245774490867, + 12.09528505722368, + 3.8237611603808546, + -21.41074594870701, + 12.27971874680177, + -9.78408102963503, + -3.488771206285275, + -10.368031684592742, + -1.615788353228746, + -12.961897356094976, + 0.4921958565948419, + 27.43996298260224, + 2.1715701458580448, + 8.065416157830143, + -9.810559471162083, + 7.738018304942575, + -34.865998055124194, + 1.238459130885854, + 20.662266608709643, + -5.2304091769550425, + 4.07196955915871, + 12.187959030266573, + -9.82694400294318, + 11.647407129622628, + -25.67841463464783, + -9.213810051957001, + 10.063963253024614, + 16.083827560266485, + 1.8802132099369544, + -3.656020370412591, + 9.112794073845052, + -42.35064649540439, + -7.263060518352329, + 50.120012957324356, + -41.15305276956162, + 14.847747280602881, + -6.979415575389375, + 25.321191715450503, + -33.867614999486584, + -2.2097811490072843, + 12.39853147007097, + -8.956437797981426, + 23.465843662964314, + -23.072980619769293, + -14.336447825730605, + -11.481431197330387, + 21.153036660634058, + 7.673233469197875, + 3.677296026257281, + 10.665104479015838, + -40.835412424485384, + -6.754074910122455, + 25.03512192997632, + -10.897163005037733, + 16.441011387737092, + -1.3371885631572198, + -21.569352758976812, + -4.275302337096733, + -18.0876911154063, + -0.04010088019482794, + 33.114160400850615, + 7.030370858000865, + -16.232257025010817, + -8.365256435291734, + -28.840352974483018, + 4.893548675446752, + -14.044856827026578, + 24.578910938472333, + -15.981908890321947, + -11.229228426694505, + 20.444351814517674, + -9.802030147811053, + -33.94000254961186, + 47.543949412578115, + -49.02326142871058, + 42.795275382464325, + -39.97962807289126, + -23.835508245890324, + 37.966342689486254, + -13.93217184757789, + 2.6297596972095363, + -17.55147756433024, + -6.561330399648256, + 28.802614605157345, + -20.989557253612574, + -28.044574802781014, + -11.033932674676947, + 27.20666112632611, + -19.69695175723797, + -10.076616588717116, + -0.6989707904876639, + 36.69957298294304, + -24.83942544431477, + 19.71697647993824, + -2.5898313842870664, + -6.357665529085608, + -29.71779749461973, + -6.278144827552353, + 14.697029591606793, + 10.310574062208673, + -54.114001609819525, + 8.203314986685172, + -10.100480395419822, + -2.901161036412276, + 34.67684069534849, + 3.5765696716294713, + -20.16945085127844, + 10.300357639891956, + -16.744748585784436, + -5.986149905042481, + 29.672321526457978, + -33.66303047893666, + -6.403930760523593, + 36.359541497731186, + -37.77596450286245, + 22.29841086392281, + 7.846349927240567, + -12.514142565541306, + -33.44929069896929, + 21.2235108452073, + -0.6196096941928886, + 12.025768334078862, + -21.152000213963674, + -3.365292380081314, + 39.90568042321513, + -26.044006995717297, + 16.131775235024435, + 3.2950406334873037, + 13.105836978731816, + -20.955832456806576, + 34.24994249306482, + -29.802801727313465, + 4.709636645922501, + -11.186796831435913, + 44.55230747931907, + -24.974011129970876, + -8.982487331915735, + 0.43409384415619456, + 9.95450210745554, + 27.24841700997129, + -32.560884353395636, + 11.394125144568505, + 0.5641895935673631, + 37.07210177776847, + -29.833462477055264, + 12.1830250246567, + 39.60576529335273, + 3.039277829358639, + -9.689094557086165, + -7.162540324679853, + -2.9377361102444297, + 25.616254640320335, + -16.124455302254944, + 10.320245043196536, + 41.77077052496722, + -9.461821814672021, + -21.88618565735122, + 13.960160958556216, + 40.935907568522076, + -12.763419709586522, + 11.09931442754484, + -10.876507521933874, + -0.9901573224289706, + 10.334991273284821, + 28.071035844341402, + -1.570545408549883, + 16.040939417418024, + -41.550577982952056, + 0.28802666288096646, + 21.61951780800504, + 15.431473725615806, + 15.397710746354527, + -11.454030633253161, + 2.207622512840434, + -10.059804976149866, + 32.09712900541837, + -19.97735105656021, + 39.64592207406452, + -34.73105200288938, + 49.119076572430586, + -1.5156921677994788, + -8.239716099445907, + -27.275598886653764, + 51.20532929484288, + -24.587168516604343, + -0.1893826794879132, + 11.788246617159174, + -12.783473138252496, + 29.30270149187291, + -7.732102201617863, + 26.195682703498505, + -13.25574380677196, + -28.646588835695496, + 35.19322992948403, + 8.622576860424715, + -37.11735376274736, + 18.52949458293292, + 15.175085672351088, + 0.8079141490044037, + 5.332222154834227, + 6.327501953551206, + -31.924416299060205, + -13.600887742218035, + -7.898283005069381, + 48.779882890337575, + 11.090496051135503, + -48.83299023128186, + 2.098178357416529, + 29.78625778782324, + -24.55804064910268, + -5.581182411276643, + 14.96824046566708, + 17.785461985312452, + -9.937417249677338, + 11.132516208168667, + -25.35491644446833, + 28.021363148486657, + -13.307099996161753, + -20.606691133662395, + 28.96570567717322, + -44.43487928468321, + 37.32303277332401, + -42.89586525486504, + 18.100429643515128, + 33.96879891809661, + -56.228561188662844, + 27.063566778886532, + 25.63497379550003, + -42.61672214915477, + 32.679918330787885, + -42.67634266450381, + -7.840364476110274, + 33.37474316312968, + -4.245386985127752, + 15.379963518410747, + -9.277739932733745, + -40.45931888542768, + 37.46051190518385, + -7.38482573464168, + 17.08458433277672, + -15.58423107789826, + -14.798457960351705, + 2.130590997469497, + -21.757350173691734, + 48.6647559708894, + -36.81421222676385, + -11.103050746730858, + 38.67861582625505, + -31.008943188211834, + -7.741323284366619, + -0.01105684345347413, + 44.05578124954008, + -10.305883159490577, + -30.53251628253488, + 16.924550204594944, + -0.05638891628063902, + 26.901273935102914, + -37.018432993368855, + 47.26884798393968, + -22.995154568632792, + -31.729462913253506, + 39.29189119528603, + 5.29099363902202, + 3.3340645696178086, + -2.412653965167678, + -39.960910517651755, + 18.104021472207588, + 32.20178583295811, + -32.05917937049049, + 22.836535402122728, + 8.365288750438864, + -27.24864815098927, + -9.17313506668583, + 36.75369084869064, + -17.909736525249016, + 1.1306928852493527, + -21.907267764357726, + 9.504034394207359, + 9.18300248182495, + 3.6551986354300254, + 10.04310775917299, + 5.614148196373009, + 13.251450152543242, + -17.738645312868954, + -25.517882318650848, + 0.6289626000823674, + 30.852884658765525, + -22.88547576424107, + 17.680860393587885, + 1.1115600608956697, + 22.041406137301237, + 2.4010610561636128, + -20.706010646847744, + -10.27000093545572, + 10.651301425142556, + -3.3927486027979796, + 32.22233291641362, + -3.2922673038741834, + -7.35163874279759, + 19.433120124179183, + -16.839591153464823, + 8.632487204207777, + -11.451888509393598, + -7.900912529062396, + -1.1305295342975228, + -9.456314184819803, + 28.444165941935466, + 6.006192118569743, + -12.529398270017865, + -23.462548721884318, + 14.785424438981678, + -1.643708961209029, + -37.13376632509228, + 16.298836902640744, + -19.02991652350687, + 31.3382743828343, + -25.969729868486503, + 28.156515387526035, + 14.245227806506918, + -54.564051397748116, + 20.632188276431393, + -3.0310285955721454, + 21.92440954319884, + -44.800941209689746, + 45.602144145196725, + -47.025820349902745, + -10.53114105548838, + -0.8765590672570909, + 10.581826954531639, + -18.55413371613583, + 51.439018275366394, + -21.734370392289065, + 16.197666970966722, + -28.515494308915052, + -7.008600576869295, + -7.109591178983088, + -11.513227049871062, + -4.174411020150703, + 14.69037085972684, + -23.953483971957812, + 0.42729325346558333, + -6.955677864070763, + -13.095809996326558, + 53.89548655021787, + -0.7227440377034497, + -5.441559220249637, + -28.691645861948025, + -12.24829779645507, + 2.395668573284791, + -20.61888703653374, + 48.514848177679845, + -31.752900207701753, + -12.455183923175401, + -15.093731772568773, + 36.333084438364416, + 2.8706751159070407, + -10.818633379797895, + -15.11598593520376, + -5.23501676034622, + 12.953083817469754, + 5.651778346563717, + -26.138066248478168, + 43.322937337147565, + -28.909453269963763, + -8.31617973331602, + 20.125577867579196, + -11.436413063347885, + 26.945663469929144, + -40.660517387933595, + -1.0205239147986944, + 10.40485841447245, + -7.40833710626714, + 29.76353010252435, + 6.433525860356497, + -33.27590138814646, + 12.623510936647463, + -26.90357511145514, + 15.579122433985333, + -10.235803183572003, + 18.80832415345256, + 11.885924473899081, + -2.947323189604486, + 28.412303766444918, + -21.086679430830504, + 7.400655531376902, + 17.42597415162399, + 17.25248312214201, + -0.6908398543806769, + -35.46882206264403, + 16.806489598203907, + -22.202333165703106, + 18.443745989296346, + 29.43176557744702, + 4.330762391242235, + -0.5985885607280181, + -9.66291763885123, + 30.40835532966405, + -44.731913251099655, + 41.27170093127582, + -20.09300416595239, + 25.84843523597976, + 4.27684217011425, + -2.68397311914039, + -12.424097751946647, + -0.5547014057469326, + 37.215967931853946, + -17.340866050691957, + 2.1467114672521888, + 34.17233882496332, + 12.163479761601153, + 4.19700594671626, + -41.06260684639996, + 30.2057602932763, + 28.8516919587138, + -10.169133236854663, + -16.051714049860763, + 1.7762469692245588, + 42.841474241939736, + -43.864814684606685, + 4.94522118797434, + 36.85899288300557, + 15.30926458218454, + -16.357917062172532, + 38.14736727558139, + -11.088104200191339, + 5.610248479926895, + -14.606166862292525, + -11.788803466751062, + 34.3473606530012, + -5.962723473364321, + 31.277187235955182, + -4.826316897166521, + 2.9129656107415656, + -30.897860897342326, + 0.40376876000715356, + 31.49188888848326, + 5.526973655451798, + -4.159770290771547, + -11.607182462633006, + -14.646937980287987, + 25.507707410719235, + 25.571647932277102, + -43.56651766509543, + 25.90612817285313, + -18.0357567969169, + -10.660031621289079, + 23.936697946461265, + 7.2386268214364975, + 20.133157194815567, + -16.95634047212306, + 3.3576307603301245, + -6.957047051738982, + 10.21244235998114, + -27.228191680804514, + 36.92445690448099, + -29.83597290145846, + 21.335843697551127, + -33.54115728365835, + -5.5339306751037896, + -2.148363952894414, + 11.859190774124983, + 40.09401816092674, + -0.9418074256137174, + -34.425690122189565, + 16.228629300970056, + -2.909329529853437, + -4.026098099371069, + 5.408885013879768, + -15.835873330093278, + 13.095266446939775, + 9.515454138221088, + -9.612665325139062, + -14.625246918977382, + -12.921031677218139, + 30.05299386805669, + -34.98133919728815, + 16.679672258712998, + -37.52627956950431, + 32.51326699165378, + -23.64863027138054, + 35.22284851605997, + -46.65130297015554, + -8.499335851539712, + 51.33098786683195, + -43.993055679370855, + -2.8945768541077825, + 8.78045364530334, + 7.485517116822706, + 19.785054021291955, + -24.887169519816837, + -3.56814228060486, + 26.39026145409528, + -59.50058630722782, + -0.2629894272083675, + 28.207738958779572, + -4.457446893919666, + -21.06273328477286, + 18.518503105465413, + -27.86435247996286, + 14.010726980595592, + -16.97864300236111, + 25.1760236277986, + 1.2414796981368852, + 18.134800352367648, + -32.83033569214837, + -15.165085972524835, + 0.2516143042184211, + 17.844897833181136, + -10.510228502494499, + -7.284513622746928, + 40.53921189548315, + -38.91761083551671, + 37.64841482002433, + -10.262113341065827, + -31.624746573790603, + 0.5182820215399033, + 34.92406560193906, + 12.361037622957554, + -39.287092169104, + 16.85175214397202, + -34.14669865654231, + 22.82883037410518, + 24.602305753945416, + -38.08280573918138, + -3.4111660911017907, + 10.984426743884981, + -12.155257823532537, + 18.120419101685627, + 28.896645251941067, + -54.479789920006525, + 8.216710583456432, + 34.0709723956832, + -36.584084915164695, + 1.0429198263786077, + -1.7337712916551453, + 7.129990521360725, + 14.663569096755793, + -9.17211672537104, + 27.791916103899524, + -31.38091062022366, + -0.993543003438063, + 23.500899951829652, + 0.033478349280301245, + -39.087587960767365, + 38.57508598052911, + -44.03716918505937, + 3.262416807404918, + -8.360647039325315, + 48.64128678958264, + -6.211360191873609, + 4.215364174330858, + -14.961993525346884, + -20.17978165513759, + 13.583753079692059, + 14.33773558980073, + -7.039645782522683, + -27.037286403832383, + 20.695476579264337, + -15.082404560217014, + -35.94303421906231, + 50.48868150758594, + -27.874530260507214, + 0.784062336128855, + -24.941658301066994, + 4.190918211821156, + 10.05968517211349, + -8.696838787383712, + 2.877014891034605, + 17.609407824807647, + -10.524986849977552, + -14.370917033289516, + -33.5862203253209, + 41.780136952028876, + -25.76381924839221, + 15.399316432593025, + -27.479342545640677, + 16.641391237852886, + -34.28148651111893, + -21.096773726984498, + 26.71042149804972, + -22.45014614669239, + -5.7814320499010705, + -6.055084383658208, + -16.915850063929774, + 18.008527834036, + -0.021211867128123174, + -11.760576420886615, + -24.808455831701217, + -12.953850141043496, + 26.584416847030404, + -20.030842500794307, + 16.154063124456727, + -38.36073110449158, + 0.11867362049362384, + -9.978668411733054, + -2.596186008823068, + 16.64784301290652, + 6.698030706297303, + -29.461793735309385, + -10.547621441337924, + -12.799713313301893, + 35.974157249253196, + -44.553419788454335, + 0.8004990742100517, + 13.317495072739575, + -24.453231354298552, + 23.51449931599666, + -4.052712988304123, + 25.019708648281807, + -22.518441058421928, + -2.764878857325243, + 7.103637095620698, + -19.197654705178294, + -13.111192662195862, + -20.621662779012695, + -8.222891160258314, + 50.97949701402801, + -52.94847029526325, + 15.294456525966126, + 8.601954427611986, + 18.910871677781728, + 3.7573293652417874, + -18.311278608340103, + -10.442582443013407, + -29.881711545667656, + 52.79737258328072, + -13.814041095649856, + -4.8931020649557695, + -35.48720262077924, + 46.22271818218468, + -22.409449891691082, + 3.6421362425899986, + -24.288243270294657, + 45.66256070728895, + -39.293095231912474, + 31.048660962638195, + 19.254663252055053, + 1.4557208223625366, + -47.34005467825122, + -2.868800282751266, + 14.458410463357467, + 18.742937228098697, + -16.684521133331543, + 17.979422110301044, + 17.783164289900412, + 6.772437972173918, + -43.25129347495394, + 18.79845756733883, + -5.829157239555428, + 10.654379352084646, + 25.18815404786986, + 12.164951156571632 + ] + } +} \ No newline at end of file diff --git a/web/libs/editor/tests/e2e/examples/image-keypoints.js b/web/libs/editor/tests/e2e/examples/image-keypoints.js index b5b813705cb0..ed7d1cd0b967 100644 --- a/web/libs/editor/tests/e2e/examples/image-keypoints.js +++ b/web/libs/editor/tests/e2e/examples/image-keypoints.js @@ -9,7 +9,7 @@ const config = ` `; const data = { - image: 'https://user.fm/files/v2-901310d5cb3fa90e0616ca10590bacb3/spacexmoon-800x501.jpg', + image: 'https://data.heartex.net/open-images/train_0/mini/0030019819f25b28.jpg', }; const result = [ @@ -18,14 +18,14 @@ const result = [ from_name: 'tag', to_name: 'img', image_rotation: 0, - original_height: 501, - original_width: 800, + original_height: 576, + original_width: 768, type: 'keypointlabels', origin: 'manual', value: { x: 49.60000000000001, y: 52.34042553191488, - width: 0.6471078324314267, + width: 0.6120428759942558, keypointlabels: ['Hello'], }, }, @@ -34,14 +34,14 @@ const result = [ from_name: 'tag', to_name: 'img', image_rotation: 0, - original_height: 501, - original_width: 800, + original_height: 576, + original_width: 768, type: 'keypointlabels', origin: 'manual', value: { x: 47.73333333333334, y: 52.765957446808514, - width: 0.6666666666666666, + width: 0.6305418719211823, keypointlabels: ['World'], }, }, diff --git a/web/libs/editor/tests/e2e/fragments/AtImageView.js b/web/libs/editor/tests/e2e/fragments/AtImageView.js index ad2ccf152eca..ff02528e080a 100644 --- a/web/libs/editor/tests/e2e/fragments/AtImageView.js +++ b/web/libs/editor/tests/e2e/fragments/AtImageView.js @@ -79,6 +79,12 @@ module.exports = { I.waitForVisible('canvas', 5); }, + async getNaturalSize() { + const sizes = await I.executeScript(Helpers.getNaturalSize); + + return sizes; + }, + async getCanvasSize() { const sizes = await I.executeScript(Helpers.getCanvasSize); diff --git a/web/libs/editor/tests/e2e/tests/audio/audio-errors.test.js b/web/libs/editor/tests/e2e/tests/audio/audio-errors.test.js index 74af7d18905d..40380abae70d 100644 --- a/web/libs/editor/tests/e2e/tests/audio/audio-errors.test.js +++ b/web/libs/editor/tests/e2e/tests/audio/audio-errors.test.js @@ -49,7 +49,7 @@ Scenario('Check if audio decoder error handler is showing', async function({ I, annotations: [{ id: 'test', result: annotations }], config, data: { - url: '/public/files/video.mp4', // mp4 is not supported by audio decoder + url: '/files/video.mp4', // mp4 is not supported by audio decoder }, }); diff --git a/web/libs/editor/tests/e2e/tests/helpers.js b/web/libs/editor/tests/e2e/tests/helpers.js index 4ab0c40e1333..ff4caf203bca 100644 --- a/web/libs/editor/tests/e2e/tests/helpers.js +++ b/web/libs/editor/tests/e2e/tests/helpers.js @@ -1,3 +1,5 @@ +const assert = require('assert'); + /** * Load custom example * @param {object} params @@ -157,7 +159,7 @@ const createAddEventListenerScript = (eventName, callback) => { * Wait for the main Image object to be loaded */ const waitForImage = () => { - return new Promise((resolve) => { + return new Promise((resolve, reject) => { const img = document.querySelector('[alt=LS]'); if (!img || img.complete) return resolve(); @@ -165,6 +167,8 @@ const waitForImage = () => { img.onload = () => { setTimeout(resolve, 100); }; + // if image is not loaded in 10 seconds, reject + setTimeout(reject, 10000); }); }; @@ -232,7 +236,7 @@ const convertToFixed = (data, fractionDigits = 2) => { if (['string', 'number'].includes(typeof data)) { const n = Number(data); - return Number.isNaN(n) ? data : Number.isInteger(n) ? n : +Number(n).toFixed(fractionDigits); + return Number.isNaN(n) ? data : Number.isInteger(n) ? n : +n.toFixed(fractionDigits); } if (Array.isArray(data)) { return data.map(n => convertToFixed(n, fractionDigits)); @@ -531,6 +535,14 @@ async function generateImageUrl({ width, height }) { return canvas.toDataURL(); } +const getNaturalSize = () => { + const imageObject = window.Htx.annotationStore.selected.objects.find(o => o.type === 'image'); + + return { + width: imageObject.naturalWidth, + height: imageObject.naturalHeight, + }; +}; const getCanvasSize = () => { const imageObject = window.Htx.annotationStore.selected.objects.find(o => o.type === 'image'); @@ -813,6 +825,16 @@ function hasSelectedRegion() { return !!Htx.annotationStore.selected.highlightedNode; } +async function doDrawingAction(I, { msg, fromX, fromY, toX, toY }) { + I.usePlaywrightTo(msg, async ({ browser, browserContext, page }) => { + await page.mouse.move(fromX, fromY); + await page.mouse.down(); + await page.mouse.move(toX, toY); + await page.mouse.up(); + }); + I.wait(1); // Ensure that the tool is fully finished being created. +} + // `mulberry32` (simple generator with a 32-bit state) function createRandomWithSeed(seed) { return function() { @@ -823,6 +845,7 @@ function createRandomWithSeed(seed) { return ((t ^ t >>> 14) >>> 0) / 4294967296; }; } + function createRandomIntWithSeed(seed) { const random = createRandomWithSeed(seed); @@ -857,6 +880,7 @@ module.exports = { areEqualRGB, hasKonvaPixelColorAtPoint, getKonvaPixelColorFromPoint, + getNaturalSize, getCanvasSize, getImageSize, getImageFrameSize, @@ -883,6 +907,7 @@ module.exports = { omitBy, dumpJSON, + doDrawingAction, createRandomWithSeed, createRandomIntWithSeed, }; diff --git a/web/libs/editor/tests/e2e/tests/image.gestures.test.js b/web/libs/editor/tests/e2e/tests/image.gestures.test.js index bea2c3c614c4..5985f4ca23fb 100644 --- a/web/libs/editor/tests/e2e/tests/image.gestures.test.js +++ b/web/libs/editor/tests/e2e/tests/image.gestures.test.js @@ -36,7 +36,7 @@ const createShape = { byMultipleClicks(x, y, radius, opts = {}) { const points = []; - for (let i = 5; i--; ) { + for (let i = 5; i--;) { points.push([x + Math.sin(((2 * Math.PI) / 5) * i) * radius, y - Math.cos(((2 * Math.PI) / 5) * i) * radius]); points.push([ x + (Math.sin(((2 * Math.PI) / 5) * (i - 0.5)) * radius) / 3, @@ -209,7 +209,7 @@ Scenario('Creating regions by various gestures', async function({ I, AtImageView for (const [idx, region] of Object.entries(regions)) { I.pressKey(region.hotKey); AtImageView[region.action](...region.params); - AtSidebar.seeRegions(+idx+1); + AtSidebar.seeRegions(+idx + 1); } const result = await I.executeScript(serialize); diff --git a/web/libs/editor/tests/e2e/tests/image.magic-wand.test.js b/web/libs/editor/tests/e2e/tests/image.magic-wand.test.js index a012f383a6f6..96cef8ff7b26 100644 --- a/web/libs/editor/tests/e2e/tests/image.magic-wand.test.js +++ b/web/libs/editor/tests/e2e/tests/image.magic-wand.test.js @@ -1,9 +1,9 @@ const { initLabelStudio, + doDrawingAction, hasKonvaPixelColorAtPoint, setKonvaLayersOpacity, serialize, - waitForImage, } = require('./helpers'); const assert = require('assert'); @@ -42,8 +42,6 @@ const annotationEmpty = { result: [], }; -// TODO: Change these URLs to heartex URLs, and ensure the heartex bucket allows CORS access -// for these to work. const data = { 'image': [ 'http://htx-pub.s3.amazonaws.com/samples/magicwand/magic_wand_scale_1_20200902_015806_26_2235_1B_AnalyticMS_00750_00750.jpg', @@ -54,16 +52,6 @@ const data = { 'thumb': 'http://htx-pub.s3.amazonaws.com/samples/magicwand/magic_wand_thumbnail_20200902_015806_26_2235_1B_AnalyticMS_00750_00750.jpg', }; -async function magicWand(I, { msg, fromX, fromY, toX, toY }) { - I.usePlaywrightTo(msg, async ({ page }) => { - await page.mouse.move(fromX, fromY); - await page.mouse.down(); - await page.mouse.move(toX, toY); - await page.mouse.up(); - }); - I.wait(1); // Ensure that the magic wand brush region is fully finished being created. -} - async function assertMagicWandPixel(I, x, y, assertValue, rgbArray, msg) { const hasPixel = await I.executeScript(hasKonvaPixelColorAtPoint, [x, y, rgbArray, 1]); @@ -101,8 +89,8 @@ Scenario('Make sure the magic wand works in a variety of scenarios', async funct AtSidebar.seeRegions(0); I.say('Magic wanding clouds with cloud class in upper left of image'); - await magicWand(I, { msg: 'Fill in clouds upper left', fromX: 258, fromY: 214, toX: 650, toY: 650 }); - await magicWand(I, { msg: 'Fill in clouds lower left', fromX: 337, fromY: 777, toX: 650, toY: 650 }); + await doDrawingAction(I, { msg: 'Fill in clouds upper left', fromX: 258, fromY: 214, toX: 650, toY: 650 }); + await doDrawingAction(I, { msg: 'Fill in clouds lower left', fromX: 337, fromY: 777, toX: 650, toY: 650 }); I.say('Ensuring repeated magic wands back to back with same class collapsed into single region'); AtSidebar.seeRegions(1); @@ -173,7 +161,7 @@ Scenario('Make sure the magic wand works in a variety of scenarios', async funct I.pressKey('2'); I.say('Magic wanding cloud shadows with cloud shadow class in center of zoomed image'); - await magicWand(I, { msg: 'Cloud shadow in middle of image', fromX: 390, fromY: 500, toX: 500, toY: 500 }); + await doDrawingAction(I, { msg: 'Cloud shadow in middle of image', fromX: 390, fromY: 500, toX: 500, toY: 500 }); I.say('Ensuring new cloud shadow magic wand region gets added to sidebar'); AtSidebar.seeRegions(2); diff --git a/web/libs/editor/tests/e2e/tests/image.selected-region.test.js b/web/libs/editor/tests/e2e/tests/image.selected-region.test.js new file mode 100644 index 000000000000..bbe11e435c4b --- /dev/null +++ b/web/libs/editor/tests/e2e/tests/image.selected-region.test.js @@ -0,0 +1,110 @@ +/* global Feature, Scenario */ + +const { + doDrawingAction, + initLabelStudio, + waitForImage, +} = require('./helpers'); +const assert = require('assert'); + +Feature('Test Image Region Stay Selected Between Tools'); + +const PLANET = { + color: '#00FF00', + rgbArray: [0, 255, 0], +}; +const MOONWALKER = { + color: '#0000FF', + rgbArray: [0, 0, 255], +}; + +const config = ` + + + + + + + + + `; + +const annotationEmpty = { + id: '1000', + result: [], +}; + +const data = { + image: + 'https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/images/nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg', +}; + +async function testRegion(testType, toolAccelerator, I, LabelStudio, AtImageView, AtSidebar) { + const params = { + config, + data, + annotations: [annotationEmpty], + }; + + LabelStudio.setFeatureFlags({ + 'fflag_feat_front_dev_4081_magic_wand_tool': true, + }); + + I.amOnPage('/'); + + I.executeScript(initLabelStudio, params); + + AtImageView.waitForImage(); + await AtImageView.lookForStage(); + I.executeScript(waitForImage); + + I.say(`Select ${testType} & planet class`); + I.pressKey(toolAccelerator); + I.pressKey('1'); + + I.say('There should be no regions initially'); + AtSidebar.seeRegions(0); + + I.say(`${testType} initial region`); + await doDrawingAction(I, { msg: `Initial ${testType}`, fromX: 150, fromY: 110, toX: 150+50, toY: 110+50 }); + + I.say('There should now be a single region'); + AtSidebar.seeRegions(1); + + I.say(`Using Eraser on ${testType} region`); + I.pressKey('E'); + I.usePlaywrightTo('Erasing', async ({ browser, browserContext, page }) => { + await page.mouse.move(150, 150); + await page.mouse.down(); + await page.mouse.move(150+100, 150); + await page.mouse.up(); + }); + + I.say(`Doing another ${testType} with same class after erasing`); + I.pressKey(toolAccelerator); + await doDrawingAction(I, { msg: `${testType} after erasing`, fromX: 280, fromY: 480, toX: 280+50, toY: 480+50 }); + + I.say('There should still only be one region'); + AtSidebar.seeRegions(1); + + I.say('Zooming and selecting pan tool'); + I.click('button[aria-label="zoom-in"]'); + I.click('button[aria-label="zoom-in"]'); + I.pressKey('H'); + + I.say(`Doing another ${testType} after zooming and selecting pan tool`); + I.pressKey(toolAccelerator); + await doDrawingAction(I, { msg: `${testType} after zoom and pan selected`, + fromX: 400, fromY: 200, toX: 400+15, toY: 400+15 }); + + I.say('There should still only be one region'); + AtSidebar.seeRegions(1); +} + +Scenario('Selected brush region should stay between tools', async function({ I, LabelStudio, AtImageView, AtSidebar }) { + await testRegion('brush', 'B', I, LabelStudio, AtImageView, AtSidebar); +}); + +Scenario('Selected Magic Wand region should stay between tools', async function({ I, LabelStudio, AtImageView, AtSidebar }) { + await testRegion('magicwand', 'W', I, LabelStudio, AtImageView, AtSidebar); +}); diff --git a/web/libs/editor/tests/e2e/tests/image.test.js b/web/libs/editor/tests/e2e/tests/image.test.js index 6ece71a08ee5..60f7f387c124 100644 --- a/web/libs/editor/tests/e2e/tests/image.test.js +++ b/web/libs/editor/tests/e2e/tests/image.test.js @@ -148,10 +148,10 @@ Scenario('Image with perRegion tags', async function({ I, AtImageView, AtSidebar assert.deepStrictEqual(result[0].value.rectanglelabels, ['Moonwalker']); }); -const outOfBoundsFFs = new DataTable(['FF_DEV_3793']) +const outOfBoundsFFs = new DataTable(['FF_DEV_3793']); outOfBoundsFFs.add([true]); -outOfBoundsFFs.add([false]) +outOfBoundsFFs.add([false]); Data(outOfBoundsFFs) .Scenario('Can\'t create rectangles outside of canvas', async ({ diff --git a/web/libs/editor/tests/e2e/tests/image.transformer.test.js b/web/libs/editor/tests/e2e/tests/image.transformer.test.js index e294e6c1a74e..b8ee2e1c18ce 100644 --- a/web/libs/editor/tests/e2e/tests/image.transformer.test.js +++ b/web/libs/editor/tests/e2e/tests/image.transformer.test.js @@ -1,1269 +1,1269 @@ -const assert = require('assert'); -const Asserts = require('../utils/asserts'); -const Helpers = require('./helpers'); - -Feature('Image transformer'); - -const IMAGE = - 'https://user.fm/files/v2-901310d5cb3fa90e0616ca10590bacb3/spacexmoon-800x501.jpg'; - -const annotationEmpty = { - id: '1000', - result: [], -}; - -const getParamsWithShape = (shape, params = '') => ({ - config: ` - - - <${shape} ${params} name="tag" toName="img" /> - `, - data: { image: IMAGE }, - annotations: [annotationEmpty], -}); - -const getParamsWithLabels = (shape) => ({ - config: ` - - - <${shape}Labels name="tag" toName="img"> - `, - data: { image: IMAGE }, - annotations: [annotationEmpty], -}); - -const shapes = { - Rectangle: { - drawAction: 'drawByDrag', - hasTransformer: true, - hasRotator: true, - hasMoveToolTransformer: true, - hasMultiSelectionTransformer: true, - hasMultiSelectionRotator: true, - hotKey: 'r', - byBBox(x, y, width, height) { - return { - params: [x, y, width, height], - result: { width, height, rotation: 0, x, y }, - }; - }, - }, - Ellipse: { - drawAction: 'drawByDrag', - hasTransformer: true, - hasRotator: true, - hasMoveToolTransformer: true, - hasMultiSelectionTransformer: true, - hasMultiSelectionRotator: true, - hotKey: 'o', - byBBox(x, y, width, height) { - return { - params: [x + width / 2, y + height / 2, width / 2, height / 2], - result: { radiusX: width / 2, radiusY: height / 2, rotation: 0, x: x + width / 2, y: y + height / 2 }, - }; - }, - }, - Polygon: { - drawAction: 'drawByClickingPoints', - hasTransformer: false, - hasRotator: false, - hasMoveToolTransformer: true, - hasMultiSelectionTransformer: true, - hasMultiSelectionRotator: false, - hotKey: 'p', - byBBox(x, y, width, height) { - const points = []; - - points.push([x, y]); - points.push([x + width, y]); - points.push([x + width / 2, y + height / 2]); - points.push([x + width, y + height]); - points.push([x, y + height]); - return { - params: [[...points, points[0]]], - result: { - points, - }, - }; - }, - }, - KeyPoint: { - drawAction: 'clickAt', - hasTransformer: false, - hasRotator: false, - hasMoveToolTransformer: false, - hasMultiSelectionTransformer: true, - hasMultiSelectionRotator: false, - hotKey: 'k', - params: 'strokeWidth="2"', - byBBox(x, y, width, height) { - return { - params: [x + width / 2, y + height / 2], - result: { - x: x + width / 2, - y: y + height / 2, - width: 2, - }, - }; - }, - }, -}; - -function drawShapeByBbox(Shape, x, y, width, height, where) { - where[Shape.drawAction](...Shape.byBBox(x, y, width, height).params); -} - -const shapesTable = new DataTable(['shapeName']); - -for (const shapeName of Object.keys(shapes)) { - shapesTable.add([shapeName]); -} - -Data(shapesTable).Scenario('Check transformer existing for different shapes, their amount and modes.', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => { - const { shapeName } = current; - const Shape = shapes[shapeName]; - - I.amOnPage('/'); - const bbox1 = { - x: 100, - y: 100, - width: 200, - height: 200, - }; - const bbox2 = { - x: 400, - y: 100, - width: 200, - height: 200, - }; - const getCenter = bbox => [bbox.x + bbox.width / 2, bbox.y + bbox.height / 2]; - let isTransformerExist; - - LabelStudio.init(getParamsWithLabels(shapeName)); - AtImageView.waitForImage(); - AtSidebar.seeRegions(0); - await AtImageView.lookForStage(); - - // Draw two regions - I.pressKey('1'); - drawShapeByBbox(Shape, bbox1.x, bbox1.y, bbox1.width, bbox1.height, AtImageView); - AtSidebar.seeRegions(1); - I.pressKey('1'); - drawShapeByBbox(Shape, bbox2.x, bbox2.y, bbox2.width, bbox2.height, AtImageView); - AtSidebar.seeRegions(2); - - // Check that it wasn't a cause to show a transformer - isTransformerExist = await AtImageView.isTransformerExist(); - assert.strictEqual(isTransformerExist, false); - - // Select the first region - AtImageView.clickAt(...getCenter(bbox1)); - AtSidebar.seeSelectedRegion(); - - // Match if transformer exist with expectations in single selected mode - isTransformerExist = await AtImageView.isTransformerExist(); - assert.strictEqual(isTransformerExist, Shape.hasTransformer); - - // Match if rotator at transformer exist with expectations in single selected mode - isTransformerExist = await AtImageView.isRotaterExist(); - assert.strictEqual(isTransformerExist, Shape.hasRotator); - - // Switch to move tool - I.pressKey('v'); - - // Match if rotator at transformer exist with expectations in single selected mode with move tool chosen - isTransformerExist = await AtImageView.isTransformerExist(); - assert.strictEqual(isTransformerExist, Shape.hasMoveToolTransformer); - - // Deselect the previous selected region - I.pressKey(['u']); - - // Select 2 regions - AtImageView.drawThroughPoints([ - [bbox1.x - 5, bbox1.y - 5], - [bbox2.x + bbox2.width + 5, bbox2.y + bbox2.height + 5], - ], 'steps', 10); - - // Match if transformer exist with expectations in multiple selected mode - isTransformerExist = await AtImageView.isTransformerExist(); - assert.strictEqual(isTransformerExist, Shape.hasMultiSelectionTransformer); - - // Match if rotator exist with expectations in multiple selected mode - isTransformerExist = await AtImageView.isRotaterExist(); - assert.strictEqual(isTransformerExist, Shape.hasMultiSelectionRotator); -}); - -Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMoveToolTransformer)) - .Scenario('Resizing a single region', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => { - const { shapeName } = current; - const Shape = shapes[shapeName]; - - I.amOnPage('/'); - LabelStudio.init(getParamsWithShape(shapeName, Shape.params)); - AtImageView.waitForImage(); - AtSidebar.seeRegions(0); - await AtImageView.lookForStage(); - const canvasSize = await AtImageView.getCanvasSize(); - const convertToImageSize = Helpers.getSizeConvertor(canvasSize.width, canvasSize.height); - - // Draw a region in bbox {x1:50,y1:50,x2:150,y2:150} - I.pressKey(Shape.hotKey); - drawShapeByBbox(Shape, 50, 50, 100, 100, AtImageView); - AtSidebar.seeRegions(1); - - // Select the shape - AtImageView.clickAt(100, 100); - AtSidebar.seeSelectedRegion(); - - // Switch to move tool to force appearance of transformer - I.pressKey('v'); - const isTransformerExist = await AtImageView.isTransformerExist(); - - assert.strictEqual(isTransformerExist, true); - - - // Transform the shape - // Move the top anchor up for 50px (limited by image border) => {x1:50,y1:0,x2:150,y2:150} - AtImageView.drawByDrag(100, 50, 0, -100); - // Move the left anchor left for 50px (limited by image border) => {x1:0,y1:0,x2:150,y2:150} - AtImageView.drawByDrag(50, 75, -300, -100); - // Move the right anchor left for 50px => {x1:0,y1:0,x2:100,y2:150} - AtImageView.drawByDrag(150, 75, -50, 0); - // Move the bottom anchor down for 100px => {x1:0,y1:0,x2:100,y2:250} - AtImageView.drawByDrag(50, 150, 10, 100); - // Move the right-bottom anchor right for 200px and down for 50px => {x1:0,y1:0,x2:300,y2:300} - AtImageView.drawByDrag(100, 250, 200, 50); - // Check resulting sizes - const rectangleResult = await LabelStudio.serialize(); - const exceptedResult = Shape.byBBox(0, 0, 300, 300).result; - - Asserts.deepEqualWithTolerance(rectangleResult[0].value, convertToImageSize(exceptedResult)); - }); - -Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMoveToolTransformer)) - .Scenario('Resizing a single region with zoom', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => { - const { shapeName } = current; - const Shape = shapes[shapeName]; - - I.amOnPage('/'); - - LabelStudio.setFeatureFlags({ - 'ff_front_dev_2394_zoomed_transforms_260522_short': true, - }); - - LabelStudio.init(getParamsWithShape(shapeName, Shape.params)); - AtImageView.waitForImage(); - AtSidebar.seeRegions(0); - await AtImageView.lookForStage(); - const canvasSize = await AtImageView.getCanvasSize(); - const convertToImageSize = Helpers.getSizeConvertor(canvasSize.width, canvasSize.height); - - // Draw a region in bbox {x1:50,y1:50,x2:150,y2:150} - I.pressKey(Shape.hotKey); - drawShapeByBbox(Shape, 50, 50, 300, 300, AtImageView); - AtSidebar.seeRegions(1); - - // Select the shape - AtImageView.clickAt(100, 100); - AtSidebar.seeSelectedRegion(); - - // Switch to move tool to force appearance of transformer - I.pressKey('v'); - const isTransformerExist = await AtImageView.isTransformerExist(); - - assert.strictEqual(isTransformerExist, true); - - AtImageView.setZoom(3, 0, 0); - - // Transform the shape - AtImageView.drawByDrag(150, 150, -150, -150); - I.wait(1); - - AtImageView.drawByDrag(0, 0, -300, -100); - I.wait(1); - - AtImageView.drawByDrag(0, 0, 150, 150); - I.wait(1); - - // Check resulting sizes - const rectangleResult = await LabelStudio.serialize(); - - I.wait(10); - - const exceptedResult = Shape.byBBox(50, 50, 300, 300).result; - - Asserts.deepEqualWithTolerance(rectangleResult[0].value, convertToImageSize(exceptedResult), 0); - }); - -Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionRotator)) - .Scenario('Simple rotating', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => { - const { shapeName } = current; - const Shape = shapes[shapeName]; - - I.amOnPage('/'); - LabelStudio.init(getParamsWithShape(shapeName, Shape.params)); - AtImageView.waitForImage(); - AtSidebar.seeRegions(0); - await AtImageView.lookForStage(); - const canvasSize = await AtImageView.getCanvasSize(); - - // Draw a region in bbox {x1:40%,y1:40%,x2:60%,y2:60%} - const rectangle = { - x: canvasSize.width * .4, - y: canvasSize.height * .4, - width: canvasSize.width * .2, - height: canvasSize.height * .2, - }; - const rectangleCenter = { - x: rectangle.x + rectangle.width / 2, - y: rectangle.y + rectangle.height / 2, - }; - - I.pressKey(Shape.hotKey); - drawShapeByBbox(Shape, rectangle.x, rectangle.y, rectangle.width, rectangle.height, AtImageView); - AtSidebar.seeRegions(1); - - - // Select the shape and check that transformer appears - AtImageView.clickAt(rectangleCenter.x, rectangleCenter.y); - AtSidebar.seeSelectedRegion(); - - // Switch to move tool to force appearance of transformer - I.pressKey('v'); - const isTransformerExist = await AtImageView.isTransformerExist(); - - assert.strictEqual(isTransformerExist, true); - - // The rotator anchor must be above top anchor by 50 pixels - const rotatorPosition = { - x: rectangleCenter.x, - y: rectangle.y - 50, - }; - - // Rotate for 45 degrees clockwise - AtImageView.drawThroughPoints( - [ - [rotatorPosition.x, rotatorPosition.y], - [rectangleCenter.x + 500, rectangleCenter.y - 500], - ], 'steps', 5); - - // Check resulting rotation - const rectangleResult = await LabelStudio.serialize(); - - Asserts.deepEqualWithTolerance(Math.round(rectangleResult[0].value.rotation), 45); - - }); - -Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionRotator)) - .Scenario('Rotating of unrotatable region', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => { - const { shapeName } = current; - const Shape = shapes[shapeName]; - - I.amOnPage('/'); - LabelStudio.init(getParamsWithShape(shapeName, Shape.params)); - AtImageView.waitForImage(); - AtSidebar.seeRegions(0); - await AtImageView.lookForStage(); - const canvasSize = await AtImageView.getCanvasSize(); - - // Draw a region which we cannot rotate 'cause of position near the image's border {x1:0,y1:20%,x2:20%,y2:50%} - const rectangle = { - x: 0, - y: canvasSize.height * .2, - width: canvasSize.width * .2, - height: canvasSize.height * .3, - }; - const rectangleCenter = { - x: rectangle.x + rectangle.width / 2, - y: rectangle.y + rectangle.height / 2, - }; - - I.pressKey(Shape.hotKey); - drawShapeByBbox(Shape, rectangle.x, rectangle.y, rectangle.width, rectangle.height, AtImageView); - AtSidebar.seeRegions(1); - - // Select the shape and check that transformer appears - AtImageView.clickAt(rectangleCenter.x, rectangleCenter.y); - AtSidebar.seeSelectedRegion(); - - // Switch to move tool to force appearance of transformer - I.pressKey('v'); - const isTransformerExist = await AtImageView.isTransformerExist(); - - assert.strictEqual(isTransformerExist, true); - - // The rotator anchor must be above top anchor by 50 pixels - const rotatorPosition = { - x: rectangleCenter.x, - y: rectangle.y - 50, - }; - - // Rotate for 45 degrees clockwise - AtImageView.drawByDrag(rotatorPosition.x, rotatorPosition.y, rectangleCenter.y - rotatorPosition.y + 100, -100); - - // Check the region hasn't been rotated - const rectangleResult = await LabelStudio.serialize(); - - Asserts.deepEqualWithTolerance(rectangleResult[0].value.rotation, 0); - }); - -Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionRotator)) - .Scenario('Broke the limits with rotation', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => { - const { shapeName } = current; - const Shape = shapes[shapeName]; - - I.amOnPage('/'); - LabelStudio.init(getParamsWithShape(shapeName, Shape.params)); - AtImageView.waitForImage(); - AtSidebar.seeRegions(0); - await AtImageView.lookForStage(); - const canvasSize = await AtImageView.getCanvasSize(); - - { - // Draw a region which have limitation at rotating by bbox {x1:5,y1:100,x2:305,y2:350} - const rectangle = { - x: 5, - y: 100, - width: 300, - height: 300, - }; - const rectangleCenter = { - x: rectangle.x + rectangle.width / 2, - y: rectangle.y + rectangle.height / 2, - }; - - I.pressKey(Shape.hotKey); - drawShapeByBbox(Shape, rectangle.x, rectangle.y, rectangle.width, rectangle.height, AtImageView); - AtSidebar.seeRegions(1); - - // Select the shape and check that transformer appears - AtImageView.clickAt(rectangleCenter.x, rectangleCenter.y); - AtSidebar.seeSelectedRegion(); - - // Switch to move tool to force appearance of transformer - I.pressKey('v'); - const isTransformerExist = await AtImageView.isTransformerExist(); - - assert.strictEqual(isTransformerExist, true); - - // The rotator anchor must be above top anchor by 50 pixels - const rotatorPosition = { - x: rectangleCenter.x, - y: rectangle.y - 50, - }; - - // Rotate for 45 degrees clockwise - AtImageView.drawThroughPoints([ - [rotatorPosition.x, rotatorPosition.y], - [rectangleCenter.x + 500, rectangleCenter.y - 500], - ], 'steps', 200); - - // Check that we cannot rotate it like this - let rectangleResult = await LabelStudio.serialize(); - - assert.notStrictEqual( - Math.round(rectangleResult[0].value.rotation), - 0, - 'Region must be rotated', - ); - assert.notStrictEqual( - Math.round(rectangleResult[0].value.rotation), - 45, - 'Angle must not be 45 degrees', - ); - - // Undo changes - I.pressKey(['CommandOrControl', 'z']); - - // Rotate for 90 degrees clockwise instead - AtImageView.drawThroughPoints([ - [rotatorPosition.x, rotatorPosition.y], - [rectangle.x + rectangle.width + 100, rectangleCenter.y], - [rectangle.x + rectangle.width + 200, rectangleCenter.y], - ], 'steps', 200); - - // Check the resulted rotation - rectangleResult = await LabelStudio.serialize(); - - Asserts.deepEqualWithTolerance(rectangleResult[0].value.rotation, 90, 'Angle must be 90 degrees'); - // remove region - I.pressKey('Backspace'); - } - - I.say('Check that it works same way with right border'); - - { - // Draw a region which have limitation at rotating by bbox {x1:100% - 305,y1:100,x2:100% - 5,y2:350} - const rectangle = { - x: canvasSize.width - 305, - y: 100, - width: 300, - height: 300, - }; - const rectangleCenter = { - x: rectangle.x + rectangle.width / 2, - y: rectangle.y + rectangle.height / 2, - }; - - I.pressKey(Shape.hotKey); - drawShapeByBbox(Shape, rectangle.x, rectangle.y, rectangle.width, rectangle.height, AtImageView); - AtSidebar.seeRegions(1); - - // Select the shape and check that transformer appears - AtImageView.clickAt(rectangleCenter.x, rectangleCenter.y); - AtSidebar.seeSelectedRegion(); - - // Switch to move tool to force appearance of transformer - I.pressKey('v'); - const isTransformerExist = await AtImageView.isTransformerExist(); - - assert.strictEqual(isTransformerExist, true); - - // The rotator anchor must be above top anchor by 50 pixels - const rotatorPosition = { - x: rectangleCenter.x, - y: rectangle.y - 50, - }; - - // Rotate for 45 degrees clockwise - AtImageView.drawThroughPoints([ - [rotatorPosition.x, rotatorPosition.y], - [rectangleCenter.x + 500, rectangleCenter.y - 500], - ], 'steps', 200); - - // Check the resulted rotation - let rectangleResult = await LabelStudio.serialize(); - - assert.notStrictEqual(Math.round(rectangleResult[0].value.rotation), 0); - assert.notStrictEqual(Math.round(rectangleResult[0].value.rotation), 45); - - // Undo changes - I.pressKey(['CommandOrControl', 'z']); - - // Rotate for 90 degrees clockwise instead - AtImageView.drawThroughPoints([ - [rotatorPosition.x, rotatorPosition.y], - [rectangle.x + rectangle.width + 100, rectangleCenter.y], - [rectangle.x + rectangle.width + 200, rectangleCenter.y], - ], 'steps', 200); - - // Check that we cannot rotate it like this - rectangleResult = await LabelStudio.serialize(); - - Asserts.deepEqualWithTolerance(rectangleResult[0].value.rotation, 90); - } - - }); - -Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionRotator)) - .Scenario('Check the initial rotation of transformer for the single region', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => { - const { shapeName } = current; - const Shape = shapes[shapeName]; - - I.amOnPage('/'); - LabelStudio.init(getParamsWithShape(shapeName, Shape.params)); - AtImageView.waitForImage(); - AtSidebar.seeRegions(0); - await AtImageView.lookForStage(); - - const bbox = { - x: 100, - y: 100, - width: 100, - height: 100, - }; - const bboxCenter = { - x: bbox.x + bbox.width / 2, - y: bbox.y + bbox.height / 2, - }; - - // Draw a region - I.pressKey(Shape.hotKey); - drawShapeByBbox(Shape, bbox.x, bbox.y, bbox.width, bbox.height, AtImageView); - AtSidebar.seeRegions(1); - - // Select it - AtImageView.clickAt(bboxCenter.x, bboxCenter.y); - AtSidebar.seeSelectedRegion(); - - // Switch to move tool to force appearance of transformer - I.pressKey('v'); - const isTransformerExist = await AtImageView.isTransformerExist(); - - assert.strictEqual(isTransformerExist, true); - - // The rotator anchor must be above top anchor by 50 pixels - let rotatorPosition = { - x: bboxCenter.x, - y: bbox.y - 50, - }; - - // Rotate for 90 degrees clockwise - AtImageView.drawThroughPoints([ - [rotatorPosition.x, rotatorPosition.y], - [bbox.x + bbox.width + 100, bboxCenter.y], - [bbox.x + bbox.width + 200, bboxCenter.y], - ], 'steps', 10); - - // Unselect current region - I.pressKey('u'); - AtSidebar.dontSeeSelectedRegion(); - - // Select it again - AtImageView.clickAt(bboxCenter.x, bboxCenter.y); - AtSidebar.seeSelectedRegion(); - - // The trick is that we turn it further, based on the assumption that transformer appears in rotated state on region selection - // So let's try to rotate it - // The rotator anchor must be to the right of the right anchor by 50 pixels - - rotatorPosition = { - x: bbox.x + bbox.width + 50, - y: bboxCenter.y, - }; - - // Rotate for 90 degrees clockwise once again - AtImageView.drawThroughPoints([ - [rotatorPosition.x, rotatorPosition.y], - [bboxCenter.x, bbox.y + bbox.height + 100], - [bboxCenter.x, bbox.y + bbox.height + 200], - ], 'steps', 10); - - // Check that region has been rotated for 180 degrees - const rectangleResult = await LabelStudio.serialize(); - - Asserts.deepEqualWithTolerance(rectangleResult[0].value.rotation, 180); - }); - -Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionRotator)) - .Scenario('Check the initial rotation of transformer for the couple of regions', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => { - const { shapeName } = current; - const Shape = shapes[shapeName]; - - I.amOnPage('/'); - LabelStudio.init(getParamsWithShape(shapeName, Shape.params)); - AtImageView.waitForImage(); - AtSidebar.seeRegions(0); - await AtImageView.lookForStage(); - - const bbox1 = { - x: 100, - y: 100, - width: 40, - height: 40, - }; - - const bbox2 = { - x: 160, - y: 160, - width: 40, - height: 40, - }; - - const transformerBbox = { - x: bbox1.x, - y: bbox1.y, - width: bbox2.x + bbox2.width - bbox1.x, - height: bbox2.y + bbox2.height - bbox1.y, - }; - const transformerBboxCenter = { - x: transformerBbox.x + transformerBbox.width / 2, - y: transformerBbox.y + transformerBbox.height / 2, - }; - - // Draw the first region - I.pressKey(Shape.hotKey); - drawShapeByBbox(Shape, bbox1.x, bbox1.y, bbox1.width, bbox1.height, AtImageView); - AtSidebar.seeRegions(1); - - // Draw the second region - I.pressKey(Shape.hotKey); - drawShapeByBbox(Shape, bbox2.x, bbox2.y, bbox2.width, bbox2.height, AtImageView); - AtSidebar.seeRegions(2); - - // Switch to move tool and select them - I.pressKey('v'); - AtImageView.drawThroughPoints([ - [transformerBbox.x - 20, transformerBbox.y - 20], - [transformerBbox.x + transformerBbox.width + 20, transformerBbox.y + transformerBbox.height + 20], - ]); - AtSidebar.seeSelectedRegion(); - - // The rotator anchor must be above top anchor by 50 pixels - const rotatorPosition = { - x: transformerBboxCenter.x, - y: transformerBbox.y - 50, - }; - - // Rotate for 180 degrees clockwise - AtImageView.drawThroughPoints([ - [rotatorPosition.x, rotatorPosition.y], - [transformerBboxCenter.x + 100, transformerBboxCenter.y + 100], - [transformerBboxCenter.x, transformerBboxCenter.y + 100], - [transformerBboxCenter.x, transformerBboxCenter.y + 200], - ], 'steps', 10); - - // Unselect current regions - I.pressKey('u'); - AtSidebar.dontSeeSelectedRegion(); - - // Select them again - AtImageView.drawThroughPoints([ - [transformerBbox.x - 20, transformerBbox.y - 20], - [transformerBbox.x + transformerBbox.width + 20, transformerBbox.y + transformerBbox.height + 20], - ]); - AtSidebar.seeSelectedRegion(); - - // So we have couple of rotated regions, let's check if rotates still appears above the top anchor - - // Rotate for 90 degrees clockwise - AtImageView.drawThroughPoints([ - [rotatorPosition.x, rotatorPosition.y], - [transformerBboxCenter.x + 100, transformerBboxCenter.y], - [transformerBboxCenter.x + 200, transformerBboxCenter.y], - ], 'steps', 10); - - // Check that region has been rotated for (180 + 90) degrees - const rectangleResult = await LabelStudio.serialize(); - - Asserts.deepEqualWithTolerance(rectangleResult[0].value.rotation, 180 + 90); - }); - -// KeyPoints are transformed unpredictable so for now just skip them -Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionTransformer && shapeName !== 'KeyPoint')) - .Scenario('Transforming of multiple regions', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => { - const { shapeName } = current; - const Shape = shapes[shapeName]; - - I.amOnPage('/'); - LabelStudio.init(getParamsWithShape(shapeName, Shape.params)); - AtImageView.waitForImage(); - AtSidebar.seeRegions(0); - await AtImageView.lookForStage(); - const canvasSize = await AtImageView.getCanvasSize(); - const convertToImageSize = Helpers.getSizeConvertor(canvasSize.width, canvasSize.height); - - const bbox1 = { - x: 100, - y: 100, - width: 50, - height: 50, - }; - - const bbox2 = { - x: 150, - y: 150, - width: 50, - height: 50, - }; - - const transformerBbox = { - x: bbox1.x, - y: bbox1.y, - width: bbox2.x + bbox2.width - bbox1.x, - height: bbox2.y + bbox2.height - bbox1.y, - }; - const transformerBboxCenter = { - get x() { - return transformerBbox.x + transformerBbox.width / 2; - }, - get y() { - return transformerBbox.y + transformerBbox.height / 2; - }, - }; - - // Draw the first region - I.pressKey(Shape.hotKey); - drawShapeByBbox(Shape, bbox1.x, bbox1.y, bbox1.width, bbox1.height, AtImageView); - AtSidebar.seeRegions(1); - - // Draw the second region - I.pressKey(Shape.hotKey); - I.pressKeyDown('Control'); - drawShapeByBbox(Shape, bbox2.x, bbox2.y, bbox2.width, bbox2.height, AtImageView); - I.pressKeyUp('Control'); - AtSidebar.seeRegions(2); - - // Switch to move tool and select them - I.pressKey('v'); - AtImageView.drawThroughPoints([ - [transformerBbox.x - 20, transformerBbox.y - 20], - [transformerBbox.x + transformerBbox.width + 20, transformerBbox.y + transformerBbox.height + 20], - ]); - AtSidebar.seeSelectedRegion(); - // Scale the shapes vertically - AtImageView.drawByDrag(transformerBboxCenter.x, transformerBbox.y + transformerBbox.height, 0, 50); - transformerBbox.height += 50; - AtSidebar.seeSelectedRegion(); - // Scale the shapes horizontally - AtImageView.drawByDrag(transformerBbox.x + transformerBbox.width, transformerBboxCenter.y, 50, 0); - transformerBbox.width += 50; - AtSidebar.seeSelectedRegion(); - // Scale the shapes in both directions - AtImageView.drawByDrag(transformerBbox.x + transformerBbox.width, transformerBbox.y + transformerBbox.height, 50, 50); - transformerBbox.height += 50; - transformerBbox.width += 50; - AtSidebar.seeSelectedRegion(); - - // Check resulting sizes - const rectangleResult = await LabelStudio.serialize(); - const exceptedResult1 = Shape.byBBox(bbox1.x, bbox1.y, bbox1.width + 50, bbox1.height + 50).result; - const exceptedResult2 = Shape.byBBox(bbox2.x + 50, bbox2.y + 50, bbox2.width + 50, bbox2.height + 50).result; - - Asserts.deepEqualWithTolerance(rectangleResult[0].value, convertToImageSize(exceptedResult1)); - Asserts.deepEqualWithTolerance(rectangleResult[1].value, convertToImageSize(exceptedResult2)); - }); - -Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionTransformer)) - .Scenario('Move regions by drag', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => { - const { shapeName } = current; - const Shape = shapes[shapeName]; - - I.amOnPage('/'); - LabelStudio.init(getParamsWithShape(shapeName, Shape.params)); - AtImageView.waitForImage(); - AtSidebar.seeRegions(0); - await AtImageView.lookForStage(); - const canvasSize = await AtImageView.getCanvasSize(); - const convertToImageSize = Helpers.getSizeConvertor(canvasSize.width, canvasSize.height); - - const bbox1 = { - x: 100, - y: 100, - width: 20, - height: 20, - }; - const bbox1Center = { - x: bbox1.x + bbox1.width / 2, - y: bbox1.y + bbox1.height / 2, - }; - - const bbox2 = { - x: 140, - y: 140, - width: 20, - height: 20, - }; - const bbox2Center = { - x: bbox2.x + bbox2.width / 2, - y: bbox2.y + bbox2.height / 2, - }; - - const transformerBbox = { - x: bbox1.x, - y: bbox1.y, - width: bbox2.x + bbox2.width - bbox1.x, - height: bbox2.y + bbox2.height - bbox1.y, - }; - const transformerBboxCenter = { - x: transformerBbox.x + transformerBbox.width / 2, - y: transformerBbox.y + transformerBbox.height / 2, - }; - - // Draw the first region - I.pressKey(Shape.hotKey); - drawShapeByBbox(Shape, bbox1.x, bbox1.y, bbox1.width, bbox1.height, AtImageView); - AtSidebar.seeRegions(1); - - // Draw the second region - I.pressKey(Shape.hotKey); - drawShapeByBbox(Shape, bbox2.x, bbox2.y, bbox2.width, bbox2.height, AtImageView); - AtSidebar.seeRegions(2); - - if (shapeName === 'KeyPoint') { - // Draw more points to get more space in transformer - I.pressKey(Shape.hotKey); - drawShapeByBbox(Shape, bbox1.x, bbox1.y, 0, 0, AtImageView); - AtSidebar.seeRegions(3); - - I.pressKey(Shape.hotKey); - drawShapeByBbox(Shape, bbox2.x + bbox2.width, bbox2.y + bbox2.height, 0, 0, AtImageView); - AtSidebar.seeRegions(4); - } - - // Switch to move tool and select them - I.pressKey('v'); - AtImageView.drawThroughPoints([ - [transformerBbox.x - 20, transformerBbox.y - 20], - [transformerBbox.x + transformerBbox.width + 20, transformerBbox.y + transformerBbox.height + 20], - ]); - AtSidebar.seeSelectedRegion(); - - const dragShapes = (startPoint, shift, rememberShift = true) => { - AtImageView.drawThroughPoints([ - [startPoint.x, startPoint.y], - [startPoint.x + shift.x, startPoint.y + shift.y], - [startPoint.x + shift.x, startPoint.y + shift.y], - ], 'steps', 10); - AtSidebar.seeSelectedRegion(); - - if (rememberShift) { - bbox1Center.x += shift.x; - bbox1Center.y += shift.y; - bbox2Center.x += shift.x; - bbox2Center.y += shift.y; - transformerBboxCenter.x += shift.x; - transformerBboxCenter.y += shift.y; - } - }; - - // Drag shapes by holding onto the first region - dragShapes(bbox1Center, { x: 100, y: 0 }); - // Drag shapes by holding onto the second region - dragShapes(bbox2Center, { x: 0, y: 100 }); - // Drag shapes by holding onto the transformer background - dragShapes(transformerBboxCenter, { x: 150, y: 150 }, false); - // Move back throught history to check that transformer's background moving with it - I.pressKey(['Control', 'z']); - // Drag shapes by holding onto the transformer background again - dragShapes(transformerBboxCenter, { x: 100, y: 100 }, false); - - - // Check that dragging was successful - const rectangleResult = await LabelStudio.serialize(); - const exceptedResult1 = Shape.byBBox(bbox1.x + 200, bbox1.y + 200, bbox1.width, bbox1.height).result; - const exceptedResult2 = Shape.byBBox(bbox2.x + 200, bbox2.y + 200, bbox2.width, bbox2.height).result; - - Asserts.deepEqualWithTolerance(rectangleResult[0].value, convertToImageSize(exceptedResult1)); - Asserts.deepEqualWithTolerance(rectangleResult[1].value, convertToImageSize(exceptedResult2)); - }); - -Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionRotator)) - .Scenario('Limitation of dragging a single rotated region', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => { - const { shapeName } = current; - const Shape = shapes[shapeName]; - - I.amOnPage('/'); - LabelStudio.init(getParamsWithShape(shapeName, Shape.params)); - AtImageView.waitForImage(); - AtSidebar.seeRegions(0); - await AtImageView.lookForStage(); - const canvasSize = await AtImageView.getCanvasSize(); - - const bbox = { - x: canvasSize.width / 2 - 50, - y: canvasSize.height / 2 - 50, - width: 100, - height: 100, - }; - const bboxCenter = { - x: bbox.x + bbox.width / 2, - y: bbox.y + bbox.height / 2, - }; - - // Draw a region - I.pressKey(Shape.hotKey); - drawShapeByBbox(Shape, bbox.x, bbox.y, bbox.width, bbox.height, AtImageView); - AtSidebar.seeRegions(1); - - // Select it - AtImageView.clickAt(bboxCenter.x, bboxCenter.y); - AtSidebar.seeSelectedRegion(); - - // Switch to move tool to force appearance of transformer - I.pressKey('v'); - const isTransformerExist = await AtImageView.isTransformerExist(); - - assert.strictEqual(isTransformerExist, true); - - // The rotator anchor must be above top anchor by 50 pixels - const rotatorPosition = { - x: bboxCenter.x, - y: bbox.y - 50, - }; - - // Rotate for 180 degrees clockwise - AtImageView.drawThroughPoints([ - [rotatorPosition.x, rotatorPosition.y], - [bboxCenter.x + 100, bboxCenter.y], - [bboxCenter.x, bboxCenter.y + 100], - [bboxCenter.x, bboxCenter.y + 200], - ], 'steps', 10); - - // When we have the rotated region, we need to check its behavior when we drag it across the borders of the image - let rectangleResult; - - I.say('Drag the region over the left border'); - AtImageView.drawThroughPoints([ - [bboxCenter.x, bboxCenter.y], - [-500, bboxCenter.y], - ], 'steps', 20); - AtSidebar.seeSelectedRegion(); - // moving of the region should be constrained by borders - rectangleResult = await LabelStudio.serialize(); - Asserts.deepEqualWithTolerance( - rectangleResult[0].value.x * canvasSize.width / 100, - Shape.byBBox(bbox.width, bbox.y + bbox.height, -bbox.width, -bbox.height).result.x, - ); - // reset position by undo - I.pressKey(['Control', 'z']); - - I.say('Drag the region over the top border'); - AtImageView.drawThroughPoints([ - [bboxCenter.x, bboxCenter.y], - [bboxCenter.x, -500], - ], 'steps', 20); - AtSidebar.seeSelectedRegion(); - // moving of the region should be constrained by borders - rectangleResult = await LabelStudio.serialize(); - Asserts.deepEqualWithTolerance( - rectangleResult[0].value.y * canvasSize.height / 100, - Shape.byBBox(bbox.x + bbox.width, bbox.height, -bbox.width, -bbox.height).result.y, - ); - // reset position by undo - I.pressKey(['Control', 'z']); - - I.say('Drag the region over the right border'); - AtImageView.drawThroughPoints([ - [bboxCenter.x, bboxCenter.y], - [canvasSize.width + 500, bboxCenter.y], - ], 'steps', 20); - AtSidebar.seeSelectedRegion(); - // moving of the region should be constrained by borders - rectangleResult = await LabelStudio.serialize(); - - Asserts.deepEqualWithTolerance( - rectangleResult[0].value.x * canvasSize.width / 100, - Shape.byBBox(canvasSize.width, bbox.y + bbox.height, -bbox.width, -bbox.height).result.x, - ); - // reset position by undo - I.pressKey(['Control', 'z']); - - I.say('Drag the region over the bottom border'); - AtImageView.drawThroughPoints([ - [bboxCenter.x, bboxCenter.y], - [bboxCenter.x, canvasSize.height + 500], - ], 'steps', 20); - AtSidebar.seeSelectedRegion(); - // moving of the region should be constrained by borders - rectangleResult = await LabelStudio.serialize(); - Asserts.deepEqualWithTolerance( - rectangleResult[0].value.y * canvasSize.height / 100, - Shape.byBBox(bbox.x + bbox.width, canvasSize.height, -bbox.width, -bbox.height).result.y, - ); - }); - -Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionRotator)) - .Scenario('Limitation of dragging a couple of rotated regions', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => { - const { shapeName } = current; - const Shape = shapes[shapeName]; - - I.amOnPage('/'); - LabelStudio.init(getParamsWithShape(shapeName, Shape.params)); - AtImageView.waitForImage(); - AtSidebar.seeRegions(0); - await AtImageView.lookForStage(); - const canvasSize = await AtImageView.getCanvasSize(); - - const bbox1 = { - x: canvasSize.width / 2 - 50, - y: canvasSize.height / 2 - 50, - width: 50, - height: 50, - }; - - const bbox2 = { - x: canvasSize.width / 2, - y: canvasSize.height / 2, - width: 50, - height: 50, - }; - - const transformerBbox = { - x: bbox1.x, - y: bbox1.y, - width: bbox2.x + bbox2.width - bbox1.x, - height: bbox2.y + bbox2.height - bbox1.y, - }; - const transformerBboxCenter = { - get x() { - return transformerBbox.x + transformerBbox.width / 2; - }, - get y() { - return transformerBbox.y + transformerBbox.height / 2; - }, - }; - - // Draw the first region - I.pressKey(Shape.hotKey); - drawShapeByBbox(Shape, bbox1.x, bbox1.y, bbox1.width, bbox1.height, AtImageView); - AtSidebar.seeRegions(1); - - // Draw the second region - I.pressKey(Shape.hotKey); - I.pressKeyDown('Control'); - drawShapeByBbox(Shape, bbox2.x, bbox2.y, bbox2.width, bbox2.height, AtImageView); - I.pressKeyUp('Control'); - AtSidebar.seeRegions(2); - - // Select them by move tool - I.pressKey('v'); - AtImageView.drawThroughPoints( - [ - [transformerBbox.x - 50, transformerBbox.y - 50], - [transformerBbox.x + transformerBbox.width + 50, transformerBbox.y + transformerBbox.height + 50], - ], 'steps', 10, - ); - AtSidebar.seeSelectedRegion(); - - // The rotator anchor must be above top anchor by 50 pixels - const rotatorPosition = { - x: transformerBboxCenter.x, - y: transformerBbox.y - 50, - }; - - // Rotate for 180 degrees clockwise - AtImageView.drawThroughPoints([ - [rotatorPosition.x, rotatorPosition.y], - [transformerBboxCenter.x + 100, transformerBboxCenter.y], - [transformerBboxCenter.x, transformerBboxCenter.y + 100], - [transformerBboxCenter.x, transformerBboxCenter.y + 200], - ], 'steps', 10); - - // When we have the rotated region, we need to check its behavior when we drag it across the borders of the image - let rectangleResult; - - I.say('Drag the region over the left border'); - AtImageView.drawThroughPoints([ - [transformerBboxCenter.x, transformerBboxCenter.y], - [-500, transformerBboxCenter.y], - ], 'steps', 20); - AtSidebar.seeSelectedRegion(); - // moving of the region should be constrained by borders - rectangleResult = await LabelStudio.serialize(); - Asserts.deepEqualWithTolerance( - rectangleResult[0].value.x * canvasSize.width / 100, - Shape.byBBox(transformerBbox.width, transformerBbox.y + transformerBbox.height, -bbox1.width, -bbox1.height).result.x, - ); - Asserts.deepEqualWithTolerance( - rectangleResult[1].value.x * canvasSize.width / 100, - Shape.byBBox(bbox2.width, transformerBbox.y + bbox2.height, -bbox2.width, -bbox2.height).result.x, - ); - // reset position by undo - I.pressKey(['Control', 'z']); - - I.say('Drag the region over the top border'); - AtImageView.drawThroughPoints([ - [transformerBboxCenter.x, transformerBboxCenter.y], - [transformerBboxCenter.x, -500], - ], 'steps', 20); - AtSidebar.seeSelectedRegion(); - // moving of the region should be constrained by borders - rectangleResult = await LabelStudio.serialize(); - Asserts.deepEqualWithTolerance( - rectangleResult[0].value.y * canvasSize.height / 100, - Shape.byBBox(transformerBbox.x + transformerBbox.width, transformerBbox.height, -bbox1.width, -bbox1.height).result.y, - ); - Asserts.deepEqualWithTolerance( - rectangleResult[1].value.y * canvasSize.height / 100, - Shape.byBBox(transformerBbox.x + bbox2.width, bbox2.height, -bbox2.width, -bbox2.height).result.y, - ); - // reset position by undo - I.pressKey(['Control', 'z']); - - I.say('Drag the region over the right border'); - AtImageView.drawThroughPoints([ - [transformerBboxCenter.x, transformerBboxCenter.y], - [canvasSize.width + 500, transformerBboxCenter.y], - ], 'steps', 20); - AtSidebar.seeSelectedRegion(); - // moving of the region should be constrained by borders - rectangleResult = await LabelStudio.serialize(); - - Asserts.deepEqualWithTolerance( - rectangleResult[0].value.x * canvasSize.width / 100, - Shape.byBBox(canvasSize.width, transformerBbox.y + transformerBbox.height, -bbox1.width, -bbox1.height).result.x, - ); - Asserts.deepEqualWithTolerance( - rectangleResult[1].value.x * canvasSize.width / 100, - Shape.byBBox(canvasSize.width - transformerBbox.width + bbox2.width, transformerBbox.y + bbox2.height, -bbox2.width, -bbox2.height).result.x, - ); - // reset position by undo - I.pressKey(['Control', 'z']); - - I.say('Drag the region over the bottom border'); - AtImageView.drawThroughPoints([ - [transformerBboxCenter.x, transformerBboxCenter.y], - [transformerBboxCenter.x, canvasSize.height + 500], - ], 'steps', 20); - AtSidebar.seeSelectedRegion(); - // moving of the region should be constrained by borders - rectangleResult = await LabelStudio.serialize(); - Asserts.deepEqualWithTolerance( - rectangleResult[0].value.y * canvasSize.height / 100, - Shape.byBBox(transformerBbox.x + transformerBbox.width, canvasSize.height, -bbox1.width, -bbox1.height).result.y, - ); - Asserts.deepEqualWithTolerance( - rectangleResult[1].value.y * canvasSize.height / 100, - Shape.byBBox(transformerBbox.x + bbox2.width, canvasSize.height - transformerBbox.height + bbox2.height, -bbox2.width, -bbox2.height).result.y, - ); - }); - -Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasRotator)) - .Scenario('Rotating the region near the border', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => { - const { shapeName } = current; - const Shape = shapes[shapeName]; - - I.amOnPage('/'); - LabelStudio.init(getParamsWithShape(shapeName, Shape.params)); - AtImageView.waitForImage(); - AtSidebar.seeRegions(0); - await AtImageView.lookForStage(); - const canvasSize = await AtImageView.getCanvasSize(); - - const bbox = { - x: canvasSize.width - Math.ceil(Math.sqrt(100 ** 2 + 100 ** 2)) / 2 - 50, - y: canvasSize.height - Math.ceil(Math.sqrt(100 ** 2 + 100 ** 2)) / 2 - 50, - width: 100, - height: 100, - }; - - const bboxCenter = { - x: bbox.x + bbox.width / 2, - y: bbox.y + bbox.height / 2, - }; - - // Draw the region - I.pressKey(Shape.hotKey); - drawShapeByBbox(Shape, bbox.x, bbox.y, bbox.width, bbox.height, AtImageView); - AtSidebar.seeRegions(1); - - // Select it - AtImageView.clickAt(bboxCenter.x, bboxCenter.y); - AtSidebar.seeSelectedRegion(); - - // The rotator anchor must be above top anchor by 50 pixels - const rotatorPosition = { - x: bboxCenter.x, - y: bbox.y - 50, - }; - - // Check 7 different rotations - const rotatorWayPoints = [[rotatorPosition.x, rotatorPosition.y]]; - const angle45 = Math.PI / 4; - - for (let i = 0; i < 8; i++) { - const angle = angle45 * i; - - rotatorWayPoints.push([bboxCenter.x + Math.sin(angle) * 100, bboxCenter.y - Math.cos(angle) * 100]); - rotatorWayPoints.push([bboxCenter.x + Math.sin(angle) * 1000, bboxCenter.y - Math.cos(angle) * 1000]); - - // Rotate clockwise by 45 * i degrees - AtImageView.drawThroughPoints(rotatorWayPoints, 'steps', 10); - AtSidebar.seeSelectedRegion(); - // Check that rotating was successful - const rectangleResult = await LabelStudio.serialize(); - - Asserts.deepEqualWithTolerance( - Math.round(rectangleResult[0].value.rotation), - 45 * i, - ); - - // undo rotation - I.pressKey(['Control', 'z']); - // clear unnecessary waypoints - rotatorWayPoints.pop(); - } - }); +const assert = require('assert'); +const Asserts = require('../utils/asserts'); +const Helpers = require('./helpers'); + +Feature('Image transformer'); + +const IMAGE = 'https://data.heartex.net/open-images/train_0/mini/0030019819f25b28.jpg'; + +const annotationEmpty = { + id: '1000', + result: [], +}; + +const getParamsWithShape = (shape, params = '') => ({ + config: ` + + + <${shape} ${params} name="tag" toName="img" /> + `, + data: { image: IMAGE }, + annotations: [annotationEmpty], +}); + +const getParamsWithLabels = (shape) => ({ + config: ` + + + <${shape}Labels name="tag" toName="img"> + `, + data: { image: IMAGE }, + annotations: [annotationEmpty], +}); + +const shapes = { + Rectangle: { + drawAction: 'drawByDrag', + hasTransformer: true, + hasRotator: true, + hasMoveToolTransformer: true, + hasMultiSelectionTransformer: true, + hasMultiSelectionRotator: true, + hotKey: 'r', + byBBox(x, y, width, height) { + return { + params: [x, y, width, height], + result: { width, height, rotation: 0, x, y }, + }; + }, + }, + Ellipse: { + drawAction: 'drawByDrag', + hasTransformer: true, + hasRotator: true, + hasMoveToolTransformer: true, + hasMultiSelectionTransformer: true, + hasMultiSelectionRotator: true, + hotKey: 'o', + byBBox(x, y, width, height) { + return { + params: [x + width / 2, y + height / 2, width / 2, height / 2], + result: { radiusX: width / 2, radiusY: height / 2, rotation: 0, x: x + width / 2, y: y + height / 2 }, + }; + }, + }, + Polygon: { + drawAction: 'drawByClickingPoints', + hasTransformer: false, + hasRotator: false, + hasMoveToolTransformer: true, + hasMultiSelectionTransformer: true, + hasMultiSelectionRotator: false, + hotKey: 'p', + byBBox(x, y, width, height) { + const points = []; + + points.push([x, y]); + points.push([x + width, y]); + points.push([x + width / 2, y + height / 2]); + points.push([x + width, y + height]); + points.push([x, y + height]); + return { + params: [[...points, points[0]]], + result: { + points, + }, + }; + }, + }, + KeyPoint: { + drawAction: 'clickAt', + hasTransformer: false, + hasRotator: false, + hasMoveToolTransformer: false, + hasMultiSelectionTransformer: true, + hasMultiSelectionRotator: false, + hotKey: 'k', + params: 'strokeWidth="2"', + byBBox(x, y, width, height) { + return { + params: [x + width / 2, y + height / 2], + result: { + x: x + width / 2, + y: y + height / 2, + width: 2, + }, + }; + }, + }, +}; + +function drawShapeByBbox(Shape, x, y, width, height, where) { + where[Shape.drawAction](...Shape.byBBox(x, y, width, height).params); +} + +const shapesTable = new DataTable(['shapeName']); + +for (const shapeName of Object.keys(shapes)) { + shapesTable.add([shapeName]); +} + +Data(shapesTable).Scenario('Check transformer existing for different shapes, their amount and modes.', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => { + const { shapeName } = current; + const Shape = shapes[shapeName]; + + I.amOnPage('/'); + const bbox1 = { + x: 100, + y: 100, + width: 200, + height: 200, + }; + const bbox2 = { + x: 400, + y: 100, + width: 200, + height: 200, + }; + const getCenter = bbox => [bbox.x + bbox.width / 2, bbox.y + bbox.height / 2]; + let isTransformerExist; + + LabelStudio.init(getParamsWithLabels(shapeName)); + AtImageView.waitForImage(); + AtSidebar.seeRegions(0); + await AtImageView.lookForStage(); + + // Draw two regions + I.pressKey('1'); + drawShapeByBbox(Shape, bbox1.x, bbox1.y, bbox1.width, bbox1.height, AtImageView); + AtSidebar.seeRegions(1); + I.pressKey('1'); + drawShapeByBbox(Shape, bbox2.x, bbox2.y, bbox2.width, bbox2.height, AtImageView); + AtSidebar.seeRegions(2); + + // Check that it wasn't a cause to show a transformer + isTransformerExist = await AtImageView.isTransformerExist(); + assert.strictEqual(isTransformerExist, false); + + // Select the first region + AtImageView.clickAt(...getCenter(bbox1)); + AtSidebar.seeSelectedRegion(); + + // Match if transformer exist with expectations in single selected mode + isTransformerExist = await AtImageView.isTransformerExist(); + assert.strictEqual(isTransformerExist, Shape.hasTransformer); + + // Match if rotator at transformer exist with expectations in single selected mode + isTransformerExist = await AtImageView.isRotaterExist(); + assert.strictEqual(isTransformerExist, Shape.hasRotator); + + // Switch to move tool + I.pressKey('v'); + + // Match if rotator at transformer exist with expectations in single selected mode with move tool chosen + isTransformerExist = await AtImageView.isTransformerExist(); + assert.strictEqual(isTransformerExist, Shape.hasMoveToolTransformer); + + // Deselect the previous selected region + I.pressKey(['u']); + + // Select 2 regions + AtImageView.drawThroughPoints([ + [bbox1.x - 5, bbox1.y - 5], + [bbox2.x + bbox2.width + 5, bbox2.y + bbox2.height + 5], + ], 'steps', 10); + + // Match if transformer exist with expectations in multiple selected mode + isTransformerExist = await AtImageView.isTransformerExist(); + assert.strictEqual(isTransformerExist, Shape.hasMultiSelectionTransformer); + + // Match if rotator exist with expectations in multiple selected mode + isTransformerExist = await AtImageView.isRotaterExist(); + assert.strictEqual(isTransformerExist, Shape.hasMultiSelectionRotator); +}); + +Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMoveToolTransformer)) + .Scenario('Resizing a single region', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => { + const { shapeName } = current; + const Shape = shapes[shapeName]; + + I.amOnPage('/'); + LabelStudio.init(getParamsWithShape(shapeName, Shape.params)); + AtImageView.waitForImage(); + AtSidebar.seeRegions(0); + await AtImageView.lookForStage(); + const canvasSize = await AtImageView.getCanvasSize(); + const convertToImageSize = Helpers.getSizeConvertor(canvasSize.width, canvasSize.height); + + // Draw a region in bbox {x1:50,y1:50,x2:150,y2:150} + I.pressKey(Shape.hotKey); + drawShapeByBbox(Shape, 50, 50, 100, 100, AtImageView); + AtSidebar.seeRegions(1); + + // Select the shape + AtImageView.clickAt(100, 100); + AtSidebar.seeSelectedRegion(); + + // Switch to move tool to force appearance of transformer + I.pressKey('v'); + const isTransformerExist = await AtImageView.isTransformerExist(); + + assert.strictEqual(isTransformerExist, true); + + + // Transform the shape + // Move the top anchor up for 50px (limited by image border) => {x1:50,y1:0,x2:150,y2:150} + AtImageView.drawByDrag(100, 50, 0, -100); + // Move the left anchor left for 50px (limited by image border) => {x1:0,y1:0,x2:150,y2:150} + AtImageView.drawByDrag(50, 75, -300, -100); + // Move the right anchor left for 50px => {x1:0,y1:0,x2:100,y2:150} + AtImageView.drawByDrag(150, 75, -50, 0); + // Move the bottom anchor down for 100px => {x1:0,y1:0,x2:100,y2:250} + AtImageView.drawByDrag(50, 150, 10, 100); + // Move the right-bottom anchor right for 200px and down for 50px => {x1:0,y1:0,x2:300,y2:300} + AtImageView.drawByDrag(100, 250, 200, 50); + // Check resulting sizes + const rectangleResult = await LabelStudio.serialize(); + const exceptedResult = Shape.byBBox(0, 0, 300, 300).result; + + Asserts.deepEqualWithTolerance(rectangleResult[0].value, convertToImageSize(exceptedResult)); + }); + +Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMoveToolTransformer)) + .Scenario('Resizing a single region with zoom', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => { + const { shapeName } = current; + const Shape = shapes[shapeName]; + + LabelStudio.setFeatureFlags({ + 'ff_front_dev_2394_zoomed_transforms_260522_short': true, + 'fflag_fix_front_dev_3377_image_regions_shift_on_resize_280922_short': true, + 'fflag_fix_front_dev_3793_relative_coords_short': true, + }); + + I.amOnPage('/'); + + LabelStudio.init(getParamsWithShape(shapeName, Shape.params)); + AtImageView.waitForImage(); + AtSidebar.seeRegions(0); + await AtImageView.lookForStage(); + const naturalSize = await AtImageView.getNaturalSize(); + const canvasSize = await AtImageView.getCanvasSize(); + // region sizes are relative (0 to 100) so we have to convert sizes we use for them... + // ...relatively to displayed image size, which is canvas size when we open the page + const convertToImageSize = Helpers.getSizeConvertor(canvasSize.width, canvasSize.height); + + // Draw a region in bbox {x1:50,y1:50,x2:150,y2:150} + I.pressKey(Shape.hotKey); + drawShapeByBbox(Shape, 50, 50, 300, 300, AtImageView); + AtSidebar.seeRegions(1); + + // Select the shape + AtImageView.clickAt(100, 100); + AtSidebar.seeSelectedRegion(); + + // Switch to move tool to force appearance of transformer + I.pressKey('v'); + const isTransformerExist = await AtImageView.isTransformerExist(); + + assert.strictEqual(isTransformerExist, true); + + // we display an image to fit to canvas size on page load, so initial zoom is not 1; + // to do an x3 zoom we have to calculate current zoom and multiply it by 3 + AtImageView.setZoom(3 * canvasSize.width / naturalSize.width, 0, 0); + + // Transform the shape + AtImageView.drawByDrag(150, 150, -150, -150); + + AtImageView.drawByDrag(0, 0, -300, -100); + + AtImageView.drawByDrag(0, 0, 150, 150); + + // Check resulting sizes + const rectangleResult = await LabelStudio.serialize(); + const exceptedResult = Shape.byBBox(50, 50, 300, 300).result; + + Asserts.deepEqualWithTolerance(rectangleResult[0].value, convertToImageSize(exceptedResult), 2); + }); + +Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionRotator)) + .Scenario('Simple rotating', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => { + const { shapeName } = current; + const Shape = shapes[shapeName]; + + I.amOnPage('/'); + LabelStudio.init(getParamsWithShape(shapeName, Shape.params)); + AtImageView.waitForImage(); + AtSidebar.seeRegions(0); + await AtImageView.lookForStage(); + const canvasSize = await AtImageView.getCanvasSize(); + + // Draw a region in bbox {x1:40%,y1:40%,x2:60%,y2:60%} + const rectangle = { + x: canvasSize.width * .4, + y: canvasSize.height * .4, + width: canvasSize.width * .2, + height: canvasSize.height * .2, + }; + const rectangleCenter = { + x: rectangle.x + rectangle.width / 2, + y: rectangle.y + rectangle.height / 2, + }; + + I.pressKey(Shape.hotKey); + drawShapeByBbox(Shape, rectangle.x, rectangle.y, rectangle.width, rectangle.height, AtImageView); + AtSidebar.seeRegions(1); + + + // Select the shape and check that transformer appears + AtImageView.clickAt(rectangleCenter.x, rectangleCenter.y); + AtSidebar.seeSelectedRegion(); + + // Switch to move tool to force appearance of transformer + I.pressKey('v'); + const isTransformerExist = await AtImageView.isTransformerExist(); + + assert.strictEqual(isTransformerExist, true); + + // The rotator anchor must be above top anchor by 50 pixels + const rotatorPosition = { + x: rectangleCenter.x, + y: rectangle.y - 50, + }; + + // Rotate for 45 degrees clockwise + AtImageView.drawThroughPoints( + [ + [rotatorPosition.x, rotatorPosition.y], + [rectangleCenter.x + 500, rectangleCenter.y - 500], + ], 'steps', 5); + + // Check resulting rotation + const rectangleResult = await LabelStudio.serialize(); + + Asserts.deepEqualWithTolerance(Math.round(rectangleResult[0].value.rotation), 45); + + }); + +Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionRotator)) + .Scenario('Rotating of unrotatable region', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => { + const { shapeName } = current; + const Shape = shapes[shapeName]; + + I.amOnPage('/'); + LabelStudio.init(getParamsWithShape(shapeName, Shape.params)); + AtImageView.waitForImage(); + AtSidebar.seeRegions(0); + await AtImageView.lookForStage(); + const canvasSize = await AtImageView.getCanvasSize(); + + // Draw a region which we cannot rotate 'cause of position near the image's border {x1:0,y1:20%,x2:20%,y2:50%} + const rectangle = { + x: 0, + y: canvasSize.height * .2, + width: canvasSize.width * .2, + height: canvasSize.height * .3, + }; + const rectangleCenter = { + x: rectangle.x + rectangle.width / 2, + y: rectangle.y + rectangle.height / 2, + }; + + I.pressKey(Shape.hotKey); + drawShapeByBbox(Shape, rectangle.x, rectangle.y, rectangle.width, rectangle.height, AtImageView); + AtSidebar.seeRegions(1); + + // Select the shape and check that transformer appears + AtImageView.clickAt(rectangleCenter.x, rectangleCenter.y); + AtSidebar.seeSelectedRegion(); + + // Switch to move tool to force appearance of transformer + I.pressKey('v'); + const isTransformerExist = await AtImageView.isTransformerExist(); + + assert.strictEqual(isTransformerExist, true); + + // The rotator anchor must be above top anchor by 50 pixels + const rotatorPosition = { + x: rectangleCenter.x, + y: rectangle.y - 50, + }; + + // Rotate for 45 degrees clockwise + AtImageView.drawByDrag(rotatorPosition.x, rotatorPosition.y, rectangleCenter.y - rotatorPosition.y + 100, -100); + + // Check the region hasn't been rotated + const rectangleResult = await LabelStudio.serialize(); + + Asserts.deepEqualWithTolerance(rectangleResult[0].value.rotation, 0); + }); + +Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionRotator)) + .Scenario('Broke the limits with rotation', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => { + const { shapeName } = current; + const Shape = shapes[shapeName]; + + I.amOnPage('/'); + LabelStudio.init(getParamsWithShape(shapeName, Shape.params)); + AtImageView.waitForImage(); + AtSidebar.seeRegions(0); + await AtImageView.lookForStage(); + const canvasSize = await AtImageView.getCanvasSize(); + + { + // Draw a region which have limitation at rotating by bbox {x1:5,y1:100,x2:305,y2:350} + const rectangle = { + x: 5, + y: 100, + width: 300, + height: 300, + }; + const rectangleCenter = { + x: rectangle.x + rectangle.width / 2, + y: rectangle.y + rectangle.height / 2, + }; + + I.pressKey(Shape.hotKey); + drawShapeByBbox(Shape, rectangle.x, rectangle.y, rectangle.width, rectangle.height, AtImageView); + AtSidebar.seeRegions(1); + + // Select the shape and check that transformer appears + AtImageView.clickAt(rectangleCenter.x, rectangleCenter.y); + AtSidebar.seeSelectedRegion(); + + // Switch to move tool to force appearance of transformer + I.pressKey('v'); + const isTransformerExist = await AtImageView.isTransformerExist(); + + assert.strictEqual(isTransformerExist, true); + + // The rotator anchor must be above top anchor by 50 pixels + const rotatorPosition = { + x: rectangleCenter.x, + y: rectangle.y - 50, + }; + + // Rotate for 45 degrees clockwise + AtImageView.drawThroughPoints([ + [rotatorPosition.x, rotatorPosition.y], + [rectangleCenter.x + 500, rectangleCenter.y - 500], + ], 'steps', 200); + + // Check that we cannot rotate it like this + let rectangleResult = await LabelStudio.serialize(); + + assert.notStrictEqual( + Math.round(rectangleResult[0].value.rotation), + 0, + 'Region must be rotated', + ); + assert.notStrictEqual( + Math.round(rectangleResult[0].value.rotation), + 45, + 'Angle must not be 45 degrees', + ); + + // Undo changes + I.pressKey(['CommandOrControl', 'z']); + + // Rotate for 90 degrees clockwise instead + AtImageView.drawThroughPoints([ + [rotatorPosition.x, rotatorPosition.y], + [rectangle.x + rectangle.width + 100, rectangleCenter.y], + [rectangle.x + rectangle.width + 200, rectangleCenter.y], + ], 'steps', 200); + + // Check the resulted rotation + rectangleResult = await LabelStudio.serialize(); + + Asserts.deepEqualWithTolerance(rectangleResult[0].value.rotation, 90, 'Angle must be 90 degrees'); + // remove region + I.pressKey('Backspace'); + } + + I.say('Check that it works same way with right border'); + + { + // Draw a region which have limitation at rotating by bbox {x1:100% - 305,y1:100,x2:100% - 5,y2:350} + const rectangle = { + x: canvasSize.width - 305, + y: 100, + width: 300, + height: 300, + }; + const rectangleCenter = { + x: rectangle.x + rectangle.width / 2, + y: rectangle.y + rectangle.height / 2, + }; + + I.pressKey(Shape.hotKey); + drawShapeByBbox(Shape, rectangle.x, rectangle.y, rectangle.width, rectangle.height, AtImageView); + AtSidebar.seeRegions(1); + + // Select the shape and check that transformer appears + AtImageView.clickAt(rectangleCenter.x, rectangleCenter.y); + AtSidebar.seeSelectedRegion(); + + // Switch to move tool to force appearance of transformer + I.pressKey('v'); + const isTransformerExist = await AtImageView.isTransformerExist(); + + assert.strictEqual(isTransformerExist, true); + + // The rotator anchor must be above top anchor by 50 pixels + const rotatorPosition = { + x: rectangleCenter.x, + y: rectangle.y - 50, + }; + + // Rotate for 45 degrees clockwise + AtImageView.drawThroughPoints([ + [rotatorPosition.x, rotatorPosition.y], + [rectangleCenter.x + 500, rectangleCenter.y - 500], + ], 'steps', 200); + + // Check the resulted rotation + let rectangleResult = await LabelStudio.serialize(); + + assert.notStrictEqual(Math.round(rectangleResult[0].value.rotation), 0); + assert.notStrictEqual(Math.round(rectangleResult[0].value.rotation), 45); + + // Undo changes + I.pressKey(['CommandOrControl', 'z']); + + // Rotate for 90 degrees clockwise instead + AtImageView.drawThroughPoints([ + [rotatorPosition.x, rotatorPosition.y], + [rectangle.x + rectangle.width + 100, rectangleCenter.y], + [rectangle.x + rectangle.width + 200, rectangleCenter.y], + ], 'steps', 200); + + // Check that we cannot rotate it like this + rectangleResult = await LabelStudio.serialize(); + + Asserts.deepEqualWithTolerance(rectangleResult[0].value.rotation, 90); + } + + }); + +Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionRotator)) + .Scenario('Check the initial rotation of transformer for the single region', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => { + const { shapeName } = current; + const Shape = shapes[shapeName]; + + I.amOnPage('/'); + LabelStudio.init(getParamsWithShape(shapeName, Shape.params)); + AtImageView.waitForImage(); + AtSidebar.seeRegions(0); + await AtImageView.lookForStage(); + + const bbox = { + x: 100, + y: 100, + width: 100, + height: 100, + }; + const bboxCenter = { + x: bbox.x + bbox.width / 2, + y: bbox.y + bbox.height / 2, + }; + + // Draw a region + I.pressKey(Shape.hotKey); + drawShapeByBbox(Shape, bbox.x, bbox.y, bbox.width, bbox.height, AtImageView); + AtSidebar.seeRegions(1); + + // Select it + AtImageView.clickAt(bboxCenter.x, bboxCenter.y); + AtSidebar.seeSelectedRegion(); + + // Switch to move tool to force appearance of transformer + I.pressKey('v'); + const isTransformerExist = await AtImageView.isTransformerExist(); + + assert.strictEqual(isTransformerExist, true); + + // The rotator anchor must be above top anchor by 50 pixels + let rotatorPosition = { + x: bboxCenter.x, + y: bbox.y - 50, + }; + + // Rotate for 90 degrees clockwise + AtImageView.drawThroughPoints([ + [rotatorPosition.x, rotatorPosition.y], + [bbox.x + bbox.width + 100, bboxCenter.y], + [bbox.x + bbox.width + 200, bboxCenter.y], + ], 'steps', 10); + + // Unselect current region + I.pressKey('u'); + AtSidebar.dontSeeSelectedRegion(); + + // Select it again + AtImageView.clickAt(bboxCenter.x, bboxCenter.y); + AtSidebar.seeSelectedRegion(); + + // The trick is that we turn it further, based on the assumption that transformer appears in rotated state on region selection + // So let's try to rotate it + // The rotator anchor must be to the right of the right anchor by 50 pixels + + rotatorPosition = { + x: bbox.x + bbox.width + 50, + y: bboxCenter.y, + }; + + // Rotate for 90 degrees clockwise once again + AtImageView.drawThroughPoints([ + [rotatorPosition.x, rotatorPosition.y], + [bboxCenter.x, bbox.y + bbox.height + 100], + [bboxCenter.x, bbox.y + bbox.height + 200], + ], 'steps', 10); + + // Check that region has been rotated for 180 degrees + const rectangleResult = await LabelStudio.serialize(); + + Asserts.deepEqualWithTolerance(rectangleResult[0].value.rotation, 180); + }); + +Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionRotator)) + .Scenario('Check the initial rotation of transformer for the couple of regions', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => { + const { shapeName } = current; + const Shape = shapes[shapeName]; + + I.amOnPage('/'); + LabelStudio.init(getParamsWithShape(shapeName, Shape.params)); + AtImageView.waitForImage(); + AtSidebar.seeRegions(0); + await AtImageView.lookForStage(); + + const bbox1 = { + x: 100, + y: 100, + width: 40, + height: 40, + }; + + const bbox2 = { + x: 160, + y: 160, + width: 40, + height: 40, + }; + + const transformerBbox = { + x: bbox1.x, + y: bbox1.y, + width: bbox2.x + bbox2.width - bbox1.x, + height: bbox2.y + bbox2.height - bbox1.y, + }; + const transformerBboxCenter = { + x: transformerBbox.x + transformerBbox.width / 2, + y: transformerBbox.y + transformerBbox.height / 2, + }; + + // Draw the first region + I.pressKey(Shape.hotKey); + drawShapeByBbox(Shape, bbox1.x, bbox1.y, bbox1.width, bbox1.height, AtImageView); + AtSidebar.seeRegions(1); + + // Draw the second region + I.pressKey(Shape.hotKey); + drawShapeByBbox(Shape, bbox2.x, bbox2.y, bbox2.width, bbox2.height, AtImageView); + AtSidebar.seeRegions(2); + + // Switch to move tool and select them + I.pressKey('v'); + AtImageView.drawThroughPoints([ + [transformerBbox.x - 20, transformerBbox.y - 20], + [transformerBbox.x + transformerBbox.width + 20, transformerBbox.y + transformerBbox.height + 20], + ]); + AtSidebar.seeSelectedRegion(); + + // The rotator anchor must be above top anchor by 50 pixels + const rotatorPosition = { + x: transformerBboxCenter.x, + y: transformerBbox.y - 50, + }; + + // Rotate for 180 degrees clockwise + AtImageView.drawThroughPoints([ + [rotatorPosition.x, rotatorPosition.y], + [transformerBboxCenter.x + 100, transformerBboxCenter.y + 100], + [transformerBboxCenter.x, transformerBboxCenter.y + 100], + [transformerBboxCenter.x, transformerBboxCenter.y + 200], + ], 'steps', 10); + + // Unselect current regions + I.pressKey('u'); + AtSidebar.dontSeeSelectedRegion(); + + // Select them again + AtImageView.drawThroughPoints([ + [transformerBbox.x - 20, transformerBbox.y - 20], + [transformerBbox.x + transformerBbox.width + 20, transformerBbox.y + transformerBbox.height + 20], + ]); + AtSidebar.seeSelectedRegion(); + + // So we have couple of rotated regions, let's check if rotates still appears above the top anchor + + // Rotate for 90 degrees clockwise + AtImageView.drawThroughPoints([ + [rotatorPosition.x, rotatorPosition.y], + [transformerBboxCenter.x + 100, transformerBboxCenter.y], + [transformerBboxCenter.x + 200, transformerBboxCenter.y], + ], 'steps', 10); + + // Check that region has been rotated for (180 + 90) degrees + const rectangleResult = await LabelStudio.serialize(); + + Asserts.deepEqualWithTolerance(rectangleResult[0].value.rotation, 180 + 90); + }); + +// KeyPoints are transformed unpredictable so for now just skip them +Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionTransformer && shapeName !== 'KeyPoint')) + .Scenario('Transforming of multiple regions', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => { + const { shapeName } = current; + const Shape = shapes[shapeName]; + + I.amOnPage('/'); + LabelStudio.init(getParamsWithShape(shapeName, Shape.params)); + AtImageView.waitForImage(); + AtSidebar.seeRegions(0); + await AtImageView.lookForStage(); + const canvasSize = await AtImageView.getCanvasSize(); + const convertToImageSize = Helpers.getSizeConvertor(canvasSize.width, canvasSize.height); + + const bbox1 = { + x: 100, + y: 100, + width: 50, + height: 50, + }; + + const bbox2 = { + x: 150, + y: 150, + width: 50, + height: 50, + }; + + const transformerBbox = { + x: bbox1.x, + y: bbox1.y, + width: bbox2.x + bbox2.width - bbox1.x, + height: bbox2.y + bbox2.height - bbox1.y, + }; + const transformerBboxCenter = { + get x() { + return transformerBbox.x + transformerBbox.width / 2; + }, + get y() { + return transformerBbox.y + transformerBbox.height / 2; + }, + }; + + // Draw the first region + I.pressKey(Shape.hotKey); + drawShapeByBbox(Shape, bbox1.x, bbox1.y, bbox1.width, bbox1.height, AtImageView); + AtSidebar.seeRegions(1); + + // Draw the second region + I.pressKey(Shape.hotKey); + I.pressKeyDown('Control'); + drawShapeByBbox(Shape, bbox2.x, bbox2.y, bbox2.width, bbox2.height, AtImageView); + I.pressKeyUp('Control'); + AtSidebar.seeRegions(2); + + // Switch to move tool and select them + I.pressKey('v'); + AtImageView.drawThroughPoints([ + [transformerBbox.x - 20, transformerBbox.y - 20], + [transformerBbox.x + transformerBbox.width + 20, transformerBbox.y + transformerBbox.height + 20], + ]); + AtSidebar.seeSelectedRegion(); + // Scale the shapes vertically + AtImageView.drawByDrag(transformerBboxCenter.x, transformerBbox.y + transformerBbox.height, 0, 50); + transformerBbox.height += 50; + AtSidebar.seeSelectedRegion(); + // Scale the shapes horizontally + AtImageView.drawByDrag(transformerBbox.x + transformerBbox.width, transformerBboxCenter.y, 50, 0); + transformerBbox.width += 50; + AtSidebar.seeSelectedRegion(); + // Scale the shapes in both directions + AtImageView.drawByDrag(transformerBbox.x + transformerBbox.width, transformerBbox.y + transformerBbox.height, 50, 50); + transformerBbox.height += 50; + transformerBbox.width += 50; + AtSidebar.seeSelectedRegion(); + + // Check resulting sizes + const rectangleResult = await LabelStudio.serialize(); + const exceptedResult1 = Shape.byBBox(bbox1.x, bbox1.y, bbox1.width + 50, bbox1.height + 50).result; + const exceptedResult2 = Shape.byBBox(bbox2.x + 50, bbox2.y + 50, bbox2.width + 50, bbox2.height + 50).result; + + Asserts.deepEqualWithTolerance(rectangleResult[0].value, convertToImageSize(exceptedResult1)); + Asserts.deepEqualWithTolerance(rectangleResult[1].value, convertToImageSize(exceptedResult2)); + }); + +Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionTransformer)) + .Scenario('Move regions by drag', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => { + const { shapeName } = current; + const Shape = shapes[shapeName]; + + I.amOnPage('/'); + LabelStudio.init(getParamsWithShape(shapeName, Shape.params)); + AtImageView.waitForImage(); + AtSidebar.seeRegions(0); + await AtImageView.lookForStage(); + const canvasSize = await AtImageView.getCanvasSize(); + const convertToImageSize = Helpers.getSizeConvertor(canvasSize.width, canvasSize.height); + + const bbox1 = { + x: 100, + y: 100, + width: 20, + height: 20, + }; + const bbox1Center = { + x: bbox1.x + bbox1.width / 2, + y: bbox1.y + bbox1.height / 2, + }; + + const bbox2 = { + x: 140, + y: 140, + width: 20, + height: 20, + }; + const bbox2Center = { + x: bbox2.x + bbox2.width / 2, + y: bbox2.y + bbox2.height / 2, + }; + + const transformerBbox = { + x: bbox1.x, + y: bbox1.y, + width: bbox2.x + bbox2.width - bbox1.x, + height: bbox2.y + bbox2.height - bbox1.y, + }; + const transformerBboxCenter = { + x: transformerBbox.x + transformerBbox.width / 2, + y: transformerBbox.y + transformerBbox.height / 2, + }; + + // Draw the first region + I.pressKey(Shape.hotKey); + drawShapeByBbox(Shape, bbox1.x, bbox1.y, bbox1.width, bbox1.height, AtImageView); + AtSidebar.seeRegions(1); + + // Draw the second region + I.pressKey(Shape.hotKey); + drawShapeByBbox(Shape, bbox2.x, bbox2.y, bbox2.width, bbox2.height, AtImageView); + AtSidebar.seeRegions(2); + + if (shapeName === 'KeyPoint') { + // Draw more points to get more space in transformer + I.pressKey(Shape.hotKey); + drawShapeByBbox(Shape, bbox1.x, bbox1.y, 0, 0, AtImageView); + AtSidebar.seeRegions(3); + + I.pressKey(Shape.hotKey); + drawShapeByBbox(Shape, bbox2.x + bbox2.width, bbox2.y + bbox2.height, 0, 0, AtImageView); + AtSidebar.seeRegions(4); + } + + // Switch to move tool and select them + I.pressKey('v'); + AtImageView.drawThroughPoints([ + [transformerBbox.x - 20, transformerBbox.y - 20], + [transformerBbox.x + transformerBbox.width + 20, transformerBbox.y + transformerBbox.height + 20], + ]); + AtSidebar.seeSelectedRegion(); + + const dragShapes = (startPoint, shift, rememberShift = true) => { + AtImageView.drawThroughPoints([ + [startPoint.x, startPoint.y], + [startPoint.x + shift.x, startPoint.y + shift.y], + [startPoint.x + shift.x, startPoint.y + shift.y], + ], 'steps', 10); + AtSidebar.seeSelectedRegion(); + + if (rememberShift) { + bbox1Center.x += shift.x; + bbox1Center.y += shift.y; + bbox2Center.x += shift.x; + bbox2Center.y += shift.y; + transformerBboxCenter.x += shift.x; + transformerBboxCenter.y += shift.y; + } + }; + + // Drag shapes by holding onto the first region + dragShapes(bbox1Center, { x: 100, y: 0 }); + // Drag shapes by holding onto the second region + dragShapes(bbox2Center, { x: 0, y: 100 }); + // Drag shapes by holding onto the transformer background + dragShapes(transformerBboxCenter, { x: 150, y: 150 }, false); + // Move back throught history to check that transformer's background moving with it + I.pressKey(['CommandOrControl', 'z']); + // Drag shapes by holding onto the transformer background again + dragShapes(transformerBboxCenter, { x: 100, y: 100 }, false); + + + // Check that dragging was successful + const rectangleResult = await LabelStudio.serialize(); + const exceptedResult1 = Shape.byBBox(bbox1.x + 200, bbox1.y + 200, bbox1.width, bbox1.height).result; + const exceptedResult2 = Shape.byBBox(bbox2.x + 200, bbox2.y + 200, bbox2.width, bbox2.height).result; + + Asserts.deepEqualWithTolerance(rectangleResult[0].value, convertToImageSize(exceptedResult1)); + Asserts.deepEqualWithTolerance(rectangleResult[1].value, convertToImageSize(exceptedResult2)); + }); + +Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionRotator)) + .Scenario('Limitation of dragging a single rotated region', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => { + const { shapeName } = current; + const Shape = shapes[shapeName]; + + I.amOnPage('/'); + LabelStudio.init(getParamsWithShape(shapeName, Shape.params)); + AtImageView.waitForImage(); + AtSidebar.seeRegions(0); + await AtImageView.lookForStage(); + const canvasSize = await AtImageView.getCanvasSize(); + + const bbox = { + x: canvasSize.width / 2 - 50, + y: canvasSize.height / 2 - 50, + width: 100, + height: 100, + }; + const bboxCenter = { + x: bbox.x + bbox.width / 2, + y: bbox.y + bbox.height / 2, + }; + + // Draw a region + I.pressKey(Shape.hotKey); + drawShapeByBbox(Shape, bbox.x, bbox.y, bbox.width, bbox.height, AtImageView); + AtSidebar.seeRegions(1); + + // Select it + AtImageView.clickAt(bboxCenter.x, bboxCenter.y); + AtSidebar.seeSelectedRegion(); + + // Switch to move tool to force appearance of transformer + I.pressKey('v'); + const isTransformerExist = await AtImageView.isTransformerExist(); + + assert.strictEqual(isTransformerExist, true); + + // The rotator anchor must be above top anchor by 50 pixels + const rotatorPosition = { + x: bboxCenter.x, + y: bbox.y - 50, + }; + + // Rotate for 180 degrees clockwise + AtImageView.drawThroughPoints([ + [rotatorPosition.x, rotatorPosition.y], + [bboxCenter.x + 100, bboxCenter.y], + [bboxCenter.x, bboxCenter.y + 100], + [bboxCenter.x, bboxCenter.y + 200], + ], 'steps', 10); + + // When we have the rotated region, we need to check its behavior when we drag it across the borders of the image + let rectangleResult; + + I.say('Drag the region over the left border'); + AtImageView.drawThroughPoints([ + [bboxCenter.x, bboxCenter.y], + [-500, bboxCenter.y], + ], 'steps', 20); + AtSidebar.seeSelectedRegion(); + // moving of the region should be constrained by borders + rectangleResult = await LabelStudio.serialize(); + Asserts.deepEqualWithTolerance( + rectangleResult[0].value.x * canvasSize.width / 100, + Shape.byBBox(bbox.width, bbox.y + bbox.height, -bbox.width, -bbox.height).result.x, + ); + // reset position by undo + I.pressKey(['CommandOrControl', 'z']); + + I.say('Drag the region over the top border'); + AtImageView.drawThroughPoints([ + [bboxCenter.x, bboxCenter.y], + [bboxCenter.x, -500], + ], 'steps', 20); + AtSidebar.seeSelectedRegion(); + // moving of the region should be constrained by borders + rectangleResult = await LabelStudio.serialize(); + Asserts.deepEqualWithTolerance( + rectangleResult[0].value.y * canvasSize.height / 100, + Shape.byBBox(bbox.x + bbox.width, bbox.height, -bbox.width, -bbox.height).result.y, + ); + // reset position by undo + I.pressKey(['CommandOrControl', 'z']); + + I.say('Drag the region over the right border'); + AtImageView.drawThroughPoints([ + [bboxCenter.x, bboxCenter.y], + [canvasSize.width + 500, bboxCenter.y], + ], 'steps', 20); + AtSidebar.seeSelectedRegion(); + // moving of the region should be constrained by borders + rectangleResult = await LabelStudio.serialize(); + + Asserts.deepEqualWithTolerance( + rectangleResult[0].value.x * canvasSize.width / 100, + Shape.byBBox(canvasSize.width, bbox.y + bbox.height, -bbox.width, -bbox.height).result.x, + ); + // reset position by undo + I.pressKey(['CommandOrControl', 'z']); + + I.say('Drag the region over the bottom border'); + AtImageView.drawThroughPoints([ + [bboxCenter.x, bboxCenter.y], + [bboxCenter.x, canvasSize.height + 500], + ], 'steps', 20); + AtSidebar.seeSelectedRegion(); + // moving of the region should be constrained by borders + rectangleResult = await LabelStudio.serialize(); + Asserts.deepEqualWithTolerance( + rectangleResult[0].value.y * canvasSize.height / 100, + Shape.byBBox(bbox.x + bbox.width, canvasSize.height, -bbox.width, -bbox.height).result.y, + ); + }); + +Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasMultiSelectionRotator)) + .Scenario('Limitation of dragging a couple of rotated regions', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => { + const { shapeName } = current; + const Shape = shapes[shapeName]; + + I.amOnPage('/'); + LabelStudio.init(getParamsWithShape(shapeName, Shape.params)); + AtImageView.waitForImage(); + AtSidebar.seeRegions(0); + await AtImageView.lookForStage(); + const canvasSize = await AtImageView.getCanvasSize(); + + const bbox1 = { + x: canvasSize.width / 2 - 50, + y: canvasSize.height / 2 - 50, + width: 50, + height: 50, + }; + + const bbox2 = { + x: canvasSize.width / 2, + y: canvasSize.height / 2, + width: 50, + height: 50, + }; + + const transformerBbox = { + x: bbox1.x, + y: bbox1.y, + width: bbox2.x + bbox2.width - bbox1.x, + height: bbox2.y + bbox2.height - bbox1.y, + }; + const transformerBboxCenter = { + get x() { + return transformerBbox.x + transformerBbox.width / 2; + }, + get y() { + return transformerBbox.y + transformerBbox.height / 2; + }, + }; + + // Draw the first region + I.pressKey(Shape.hotKey); + drawShapeByBbox(Shape, bbox1.x, bbox1.y, bbox1.width, bbox1.height, AtImageView); + AtSidebar.seeRegions(1); + + // Draw the second region + I.pressKey(Shape.hotKey); + I.pressKeyDown('Control'); + drawShapeByBbox(Shape, bbox2.x, bbox2.y, bbox2.width, bbox2.height, AtImageView); + I.pressKeyUp('Control'); + AtSidebar.seeRegions(2); + + // Select them by move tool + I.pressKey('v'); + AtImageView.drawThroughPoints( + [ + [transformerBbox.x - 50, transformerBbox.y - 50], + [transformerBbox.x + transformerBbox.width + 50, transformerBbox.y + transformerBbox.height + 50], + ], 'steps', 10, + ); + AtSidebar.seeSelectedRegion(); + + // The rotator anchor must be above top anchor by 50 pixels + const rotatorPosition = { + x: transformerBboxCenter.x, + y: transformerBbox.y - 50, + }; + + // Rotate for 180 degrees clockwise + AtImageView.drawThroughPoints([ + [rotatorPosition.x, rotatorPosition.y], + [transformerBboxCenter.x + 100, transformerBboxCenter.y], + [transformerBboxCenter.x, transformerBboxCenter.y + 100], + [transformerBboxCenter.x, transformerBboxCenter.y + 200], + ], 'steps', 10); + + // When we have the rotated region, we need to check its behavior when we drag it across the borders of the image + let rectangleResult; + + I.say('Drag the region over the left border'); + AtImageView.drawThroughPoints([ + [transformerBboxCenter.x, transformerBboxCenter.y], + [-500, transformerBboxCenter.y], + ], 'steps', 20); + AtSidebar.seeSelectedRegion(); + // moving of the region should be constrained by borders + rectangleResult = await LabelStudio.serialize(); + Asserts.deepEqualWithTolerance( + rectangleResult[0].value.x * canvasSize.width / 100, + Shape.byBBox(transformerBbox.width, transformerBbox.y + transformerBbox.height, -bbox1.width, -bbox1.height).result.x, + ); + Asserts.deepEqualWithTolerance( + rectangleResult[1].value.x * canvasSize.width / 100, + Shape.byBBox(bbox2.width, transformerBbox.y + bbox2.height, -bbox2.width, -bbox2.height).result.x, + ); + // reset position by undo + I.pressKey(['CommandOrControl', 'z']); + + I.say('Drag the region over the top border'); + AtImageView.drawThroughPoints([ + [transformerBboxCenter.x, transformerBboxCenter.y], + [transformerBboxCenter.x, -500], + ], 'steps', 20); + AtSidebar.seeSelectedRegion(); + // moving of the region should be constrained by borders + rectangleResult = await LabelStudio.serialize(); + Asserts.deepEqualWithTolerance( + rectangleResult[0].value.y * canvasSize.height / 100, + Shape.byBBox(transformerBbox.x + transformerBbox.width, transformerBbox.height, -bbox1.width, -bbox1.height).result.y, + ); + Asserts.deepEqualWithTolerance( + rectangleResult[1].value.y * canvasSize.height / 100, + Shape.byBBox(transformerBbox.x + bbox2.width, bbox2.height, -bbox2.width, -bbox2.height).result.y, + ); + // reset position by undo + I.pressKey(['CommandOrControl', 'z']); + + I.say('Drag the region over the right border'); + AtImageView.drawThroughPoints([ + [transformerBboxCenter.x, transformerBboxCenter.y], + [canvasSize.width + 500, transformerBboxCenter.y], + ], 'steps', 20); + AtSidebar.seeSelectedRegion(); + // moving of the region should be constrained by borders + rectangleResult = await LabelStudio.serialize(); + + Asserts.deepEqualWithTolerance( + rectangleResult[0].value.x * canvasSize.width / 100, + Shape.byBBox(canvasSize.width, transformerBbox.y + transformerBbox.height, -bbox1.width, -bbox1.height).result.x, + ); + Asserts.deepEqualWithTolerance( + rectangleResult[1].value.x * canvasSize.width / 100, + Shape.byBBox(canvasSize.width - transformerBbox.width + bbox2.width, transformerBbox.y + bbox2.height, -bbox2.width, -bbox2.height).result.x, + ); + // reset position by undo + I.pressKey(['CommandOrControl', 'z']); + + I.say('Drag the region over the bottom border'); + AtImageView.drawThroughPoints([ + [transformerBboxCenter.x, transformerBboxCenter.y], + [transformerBboxCenter.x, canvasSize.height + 500], + ], 'steps', 20); + AtSidebar.seeSelectedRegion(); + // moving of the region should be constrained by borders + rectangleResult = await LabelStudio.serialize(); + Asserts.deepEqualWithTolerance( + rectangleResult[0].value.y * canvasSize.height / 100, + Shape.byBBox(transformerBbox.x + transformerBbox.width, canvasSize.height, -bbox1.width, -bbox1.height).result.y, + ); + Asserts.deepEqualWithTolerance( + rectangleResult[1].value.y * canvasSize.height / 100, + Shape.byBBox(transformerBbox.x + bbox2.width, canvasSize.height - transformerBbox.height + bbox2.height, -bbox2.width, -bbox2.height).result.y, + ); + }); + +Data(shapesTable.filter(({ shapeName }) => shapes[shapeName].hasRotator)) + .Scenario('Rotating the region near the border', async ({ I, LabelStudio, AtImageView, AtSidebar, current }) => { + const { shapeName } = current; + const Shape = shapes[shapeName]; + + I.amOnPage('/'); + LabelStudio.init(getParamsWithShape(shapeName, Shape.params)); + AtImageView.waitForImage(); + AtSidebar.seeRegions(0); + await AtImageView.lookForStage(); + const canvasSize = await AtImageView.getCanvasSize(); + + const bbox = { + x: canvasSize.width - Math.ceil(Math.sqrt(100 ** 2 + 100 ** 2)) / 2 - 50, + y: canvasSize.height - Math.ceil(Math.sqrt(100 ** 2 + 100 ** 2)) / 2 - 50, + width: 100, + height: 100, + }; + + const bboxCenter = { + x: bbox.x + bbox.width / 2, + y: bbox.y + bbox.height / 2, + }; + + // Draw the region + I.pressKey(Shape.hotKey); + drawShapeByBbox(Shape, bbox.x, bbox.y, bbox.width, bbox.height, AtImageView); + AtSidebar.seeRegions(1); + + // Select it + AtImageView.clickAt(bboxCenter.x, bboxCenter.y); + AtSidebar.seeSelectedRegion(); + + // The rotator anchor must be above top anchor by 50 pixels + const rotatorPosition = { + x: bboxCenter.x, + y: bbox.y - 50, + }; + + // Check 7 different rotations + const rotatorWayPoints = [[rotatorPosition.x, rotatorPosition.y]]; + const angle45 = Math.PI / 4; + + for (let i = 0; i < 8; i++) { + const angle = angle45 * i; + + rotatorWayPoints.push([bboxCenter.x + Math.sin(angle) * 100, bboxCenter.y - Math.cos(angle) * 100]); + rotatorWayPoints.push([bboxCenter.x + Math.sin(angle) * 1000, bboxCenter.y - Math.cos(angle) * 1000]); + + // Rotate clockwise by 45 * i degrees + AtImageView.drawThroughPoints(rotatorWayPoints, 'steps', 10); + AtSidebar.seeSelectedRegion(); + // Check that rotating was successful + const rectangleResult = await LabelStudio.serialize(); + + Asserts.deepEqualWithTolerance( + Math.round(rectangleResult[0].value.rotation), + 45 * i, + ); + + // undo rotation + I.pressKey(['CommandOrControl', 'z']); + // clear unnecessary waypoints + rotatorWayPoints.pop(); + } + }); diff --git a/web/libs/editor/tests/e2e/tests/maxUsage.test.js b/web/libs/editor/tests/e2e/tests/maxUsage.test.js index 0299b7e344b5..fc2111838081 100644 --- a/web/libs/editor/tests/e2e/tests/maxUsage.test.js +++ b/web/libs/editor/tests/e2e/tests/maxUsage.test.js @@ -1,340 +1,340 @@ -Feature('Max usage'); - -const IMAGE = 'https://user.fm/files/v2-901310d5cb3fa90e0616ca10590bacb3/spacexmoon-800x501.jpg'; - -const createImageToolsConfig = ({ maxUsage }) => ` - - - - - - - - - -`; - -const createImageLabelsConfig = ({ maxUsage }) => ` - - - - - - - - - - - - - `; - -const shapes = { - Rectangle: { - drawAction: 'drawByDrag', - shortcut: 'r', - hotkey: '1', - byBBox(x, y, width, height) { - return { - params: [x, y, width, height], - }; - }, - }, - Ellipse: { - drawAction: 'drawByDrag', - shortcut: 'o', - hotkey: '2', - byBBox(x, y, width, height) { - return { - params: [x + width / 2, y + height / 2, width / 2, height / 2], - }; - }, - }, - Brush: { - drawAction: 'clickAt', - shortcut: 'b', - hotkey: '3', - byBBox(x, y, width, height) { - return { - params: [x + width / 2, y + height / 2], - }; - }, - }, - KeyPoint: { - drawAction: 'clickAt', - shortcut: 'k', - hotkey: '4', - byBBox(x, y, width, height) { - return { - params: [x + width / 2, y + height / 2], - }; - }, - }, - Polygon: { - drawAction: 'drawByClickingPoints', - shortcut: 'p', - hotkey: '5', - byBBox(x, y, width, height) { - const points = []; - - points.push([x, y]); - points.push([x + width, y]); - points.push([x + width / 2, y + height]); - return { - params: [[...points, points[0]]], - }; - }, - }, -}; - -function drawShapeByBbox(Shape, x, y, width, height, where) { - where[Shape.drawAction](...Shape.byBBox(x, y, width, height).params); -} - -const maxUsageImageToolsDataTable = new DataTable(['maxUsage', 'shapeName']); - -[1,3].forEach(maxUsage => { - Object.keys(shapes).forEach(shapeName => { - maxUsageImageToolsDataTable.add([maxUsage, shapeName]); - }); -}); - -const maxUsageDataTable = new DataTable(['maxUsage']); - -[1,3].forEach(maxUsage => { - maxUsageDataTable.add([maxUsage]); -}); - -Data(maxUsageImageToolsDataTable).Scenario('Max usages of separated labels in ImageView on region creating', async function({ I, LabelStudio, AtImageView, AtSidebar, current }) { - const { maxUsage, shapeName } = current; - const shape = shapes[shapeName]; - const annotations = []; - - for (let k = 0; k < maxUsage; k++) { - annotations.push({ - 'value': { - 'x': k, - 'y': 1, - 'width': 0.6666666666666666, - 'labels': [ - 'Label_1', - ], - }, - 'id': k, - 'from_name': 'Labels', - 'to_name': 'img', - 'type': 'labels', - }); - } - - I.amOnPage('/'); - LabelStudio.setFeatureFlags({ - fflag_fix_front_dev_3666_max_usages_on_region_creation_171122_short: true, - }); - LabelStudio.init({ - config: createImageToolsConfig({ maxUsage }), - data: { - image: IMAGE, - }, - annotations: [{ - id: 'test', - result: annotations, - }], - }); - await AtImageView.waitForImage(); - await AtImageView.lookForStage(); - AtSidebar.seeRegions(maxUsage); - - - I.pressKey('1'); - I.pressKey(shape.shortcut); - AtImageView.clickAt(50, 50); - - I.see(`You can't use Label_1 more than ${maxUsage} time(s)`); -}); - -Data(maxUsageImageToolsDataTable).Scenario('Max usages of labels in ImageView on region creating', async function({ I, LabelStudio, AtImageView, AtSidebar, current }) { - const { maxUsage, shapeName } = current; - const shape = shapes[shapeName]; - - I.amOnPage('/'); - LabelStudio.setFeatureFlags({ - fflag_fix_front_dev_3666_max_usages_on_region_creation_171122_short: true, - }); - LabelStudio.init({ - config: createImageLabelsConfig({ maxUsage }), - data: { - image: IMAGE, - }, - }); - - await AtImageView.waitForImage(); - await AtImageView.lookForStage(); - AtSidebar.seeRegions(0); - - for (let k = 0; k < maxUsage; k++) { - I.pressKey(shape.hotkey); - drawShapeByBbox(shape, 1 + 50 * k, 1, 30,30, AtImageView); - I.pressKey('u'); - } - - I.pressKey(shape.hotkey); - AtImageView.clickAt(50, 50); - - I.see(`You can't use ${shapeName}_1 more than ${maxUsage} time(s)`); -}); - -Data(maxUsageDataTable).Scenario('Max usages of labels in Audio on region creation', async function({ I, LabelStudio, AtSidebar, AtAudioView, current }) { - const { maxUsage } = current; - - LabelStudio.setFeatureFlags({ - fflag_fix_front_dev_3666_max_usages_on_region_creation_171122_short: true, - ff_front_dev_2715_audio_3_280722_short: true, - }); - I.amOnPage('/'); - LabelStudio.init({ - config: ` - - - - `, - data: { - audio: 'https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/audio/barradeen-emotional.mp3', - }, - }); - - await AtAudioView.waitForAudio(); - await AtAudioView.lookForStage(); - AtSidebar.seeRegions(0); - - for (let k = 0; k < maxUsage; k++) { - I.pressKey('1'); - AtAudioView.dragAudioElement(10 + 40 * k,30); - I.pressKey('u'); - } - I.pressKey('1'); - AtAudioView.dragAudioElement(10 + 40 * maxUsage,30); - - AtSidebar.seeRegions(maxUsage); - I.see(`You can't use Label_1 more than ${maxUsage} time(s)`); -}); - -Data(maxUsageDataTable).Scenario('Max usages of labels in RichText on region creation', async function({ I, LabelStudio, AtSidebar, AtRichText, current }) { - const { maxUsage } = current; - - I.amOnPage('/'); - LabelStudio.setFeatureFlags({ - fflag_fix_front_dev_3666_max_usages_on_region_creation_171122_short: true, - }); - LabelStudio.init({ - config: ` - - - - -`, - data: { - url: 'https://htx-pub.s3.amazonaws.com/example.txt', - }, - }); - - LabelStudio.waitForObjectsReady(); - AtSidebar.seeRegions(0); - - for (let k = 0; k < maxUsage; k++) { - I.pressKey('1'); - AtRichText.selectTextByGlobalOffset(1 + 5 * k, 5 * (k + 1)); - I.pressKey('u'); - } - I.pressKey('1'); - AtRichText.selectTextByGlobalOffset(1 + 5 * maxUsage, 5 * (maxUsage + 1)); - - I.see(`You can't use Label_1 more than ${maxUsage} time(s)`); -}); - -Data(maxUsageDataTable).Scenario('Max usages of labels in Paragraphs on region creation', async function({ I, LabelStudio, AtSidebar, AtParagraphs, current }) { - const { maxUsage } = current; - - I.amOnPage('/'); - LabelStudio.setFeatureFlags({ - fflag_fix_front_dev_3666_max_usages_on_region_creation_171122_short: true, - }); - LabelStudio.init({ - config: ` - - - - -`, - data: require('../examples/text-paragraphs').data, - }); - - LabelStudio.waitForObjectsReady(); - AtSidebar.seeRegions(0); - - for (let k = 0; k < maxUsage; k++) { - I.pressKey('1'); - AtParagraphs.selectTextByOffset(k + 1, 0, 3); - I.pressKey('u'); - } - I.pressKey('1'); - AtParagraphs.selectTextByOffset(maxUsage + 1, 0, 3); - - I.see(`You can't use Label_1 more than ${maxUsage} time(s)`); -}); - -Data(maxUsageDataTable).Scenario('Max usages of labels in Timeseries on region creation', async function({ I, LabelStudio, AtSidebar, AtTimeSeries, current }) { - const { maxUsage } = current; - - I.amOnPage('/'); - LabelStudio.setFeatureFlags({ - fflag_fix_front_dev_3666_max_usages_on_region_creation_171122_short: true, - }); - LabelStudio.init({ - config: ` - - - - - - - -`, - data: require('../examples/data/sample-sin.json'), - }); - - LabelStudio.waitForObjectsReady(); - AtSidebar.seeRegions(0); - await AtTimeSeries.lookForStage(); - - for (let k = 0; k < maxUsage; k++) { - I.pressKey('1'); - AtTimeSeries.drawByDrag(1 + k * 20, 10); - I.pressKey('u'); - } - I.pressKey('1'); - AtTimeSeries.drawByDrag(1 + maxUsage * 20, 10); - - I.see(`You can't use Label_1 more than ${maxUsage} time(s)`); -}); +Feature('Max usage'); + +const IMAGE = 'https://data.heartex.net/open-images/train_0/mini/0030019819f25b28.jpg'; + +const createImageToolsConfig = ({ maxUsage }) => ` + + + + + + + + + +`; + +const createImageLabelsConfig = ({ maxUsage }) => ` + + + + + + + + + + + + + `; + +const shapes = { + Rectangle: { + drawAction: 'drawByDrag', + shortcut: 'r', + hotkey: '1', + byBBox(x, y, width, height) { + return { + params: [x, y, width, height], + }; + }, + }, + Ellipse: { + drawAction: 'drawByDrag', + shortcut: 'o', + hotkey: '2', + byBBox(x, y, width, height) { + return { + params: [x + width / 2, y + height / 2, width / 2, height / 2], + }; + }, + }, + Brush: { + drawAction: 'clickAt', + shortcut: 'b', + hotkey: '3', + byBBox(x, y, width, height) { + return { + params: [x + width / 2, y + height / 2], + }; + }, + }, + KeyPoint: { + drawAction: 'clickAt', + shortcut: 'k', + hotkey: '4', + byBBox(x, y, width, height) { + return { + params: [x + width / 2, y + height / 2], + }; + }, + }, + Polygon: { + drawAction: 'drawByClickingPoints', + shortcut: 'p', + hotkey: '5', + byBBox(x, y, width, height) { + const points = []; + + points.push([x, y]); + points.push([x + width, y]); + points.push([x + width / 2, y + height]); + return { + params: [[...points, points[0]]], + }; + }, + }, +}; + +function drawShapeByBbox(Shape, x, y, width, height, where) { + where[Shape.drawAction](...Shape.byBBox(x, y, width, height).params); +} + +const maxUsageImageToolsDataTable = new DataTable(['maxUsage', 'shapeName']); + +[1,3].forEach(maxUsage => { + Object.keys(shapes).forEach(shapeName => { + maxUsageImageToolsDataTable.add([maxUsage, shapeName]); + }); +}); + +const maxUsageDataTable = new DataTable(['maxUsage']); + +[1,3].forEach(maxUsage => { + maxUsageDataTable.add([maxUsage]); +}); + +Data(maxUsageImageToolsDataTable).Scenario('Max usages of separated labels in ImageView on region creating', async function({ I, LabelStudio, AtImageView, AtSidebar, current }) { + const { maxUsage, shapeName } = current; + const shape = shapes[shapeName]; + const annotations = []; + + for (let k = 0; k < maxUsage; k++) { + annotations.push({ + 'value': { + 'x': k, + 'y': 1, + 'width': 0.6666666666666666, + 'labels': [ + 'Label_1', + ], + }, + 'id': k, + 'from_name': 'Labels', + 'to_name': 'img', + 'type': 'labels', + }); + } + + I.amOnPage('/'); + LabelStudio.setFeatureFlags({ + fflag_fix_front_dev_3666_max_usages_on_region_creation_171122_short: true, + }); + LabelStudio.init({ + config: createImageToolsConfig({ maxUsage }), + data: { + image: IMAGE, + }, + annotations: [{ + id: 'test', + result: annotations, + }], + }); + await AtImageView.waitForImage(); + await AtImageView.lookForStage(); + AtSidebar.seeRegions(maxUsage); + + + I.pressKey('1'); + I.pressKey(shape.shortcut); + AtImageView.clickAt(50, 50); + + I.see(`You can't use Label_1 more than ${maxUsage} time(s)`); +}); + +Data(maxUsageImageToolsDataTable).Scenario('Max usages of labels in ImageView on region creating', async function({ I, LabelStudio, AtImageView, AtSidebar, current }) { + const { maxUsage, shapeName } = current; + const shape = shapes[shapeName]; + + I.amOnPage('/'); + LabelStudio.setFeatureFlags({ + fflag_fix_front_dev_3666_max_usages_on_region_creation_171122_short: true, + }); + LabelStudio.init({ + config: createImageLabelsConfig({ maxUsage }), + data: { + image: IMAGE, + }, + }); + + await AtImageView.waitForImage(); + await AtImageView.lookForStage(); + AtSidebar.seeRegions(0); + + for (let k = 0; k < maxUsage; k++) { + I.pressKey(shape.hotkey); + drawShapeByBbox(shape, 1 + 50 * k, 1, 30,30, AtImageView); + I.pressKey('u'); + } + + I.pressKey(shape.hotkey); + AtImageView.clickAt(50, 50); + + I.see(`You can't use ${shapeName}_1 more than ${maxUsage} time(s)`); +}); + +Data(maxUsageDataTable).Scenario('Max usages of labels in Audio on region creation', async function({ I, LabelStudio, AtSidebar, AtAudioView, current }) { + const { maxUsage } = current; + + LabelStudio.setFeatureFlags({ + fflag_fix_front_dev_3666_max_usages_on_region_creation_171122_short: true, + ff_front_dev_2715_audio_3_280722_short: true, + }); + I.amOnPage('/'); + LabelStudio.init({ + config: ` + + + + `, + data: { + audio: 'https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/audio/barradeen-emotional.mp3', + }, + }); + + await AtAudioView.waitForAudio(); + await AtAudioView.lookForStage(); + AtSidebar.seeRegions(0); + + for (let k = 0; k < maxUsage; k++) { + I.pressKey('1'); + AtAudioView.dragAudioElement(10 + 40 * k,30); + I.pressKey('u'); + } + I.pressKey('1'); + AtAudioView.dragAudioElement(10 + 40 * maxUsage,30); + + AtSidebar.seeRegions(maxUsage); + I.see(`You can't use Label_1 more than ${maxUsage} time(s)`); +}); + +Data(maxUsageDataTable).Scenario('Max usages of labels in RichText on region creation', async function({ I, LabelStudio, AtSidebar, AtRichText, current }) { + const { maxUsage } = current; + + I.amOnPage('/'); + LabelStudio.setFeatureFlags({ + fflag_fix_front_dev_3666_max_usages_on_region_creation_171122_short: true, + }); + LabelStudio.init({ + config: ` + + + + +`, + data: { + url: 'https://htx-pub.s3.amazonaws.com/example.txt', + }, + }); + + LabelStudio.waitForObjectsReady(); + AtSidebar.seeRegions(0); + + for (let k = 0; k < maxUsage; k++) { + I.pressKey('1'); + AtRichText.selectTextByGlobalOffset(1 + 5 * k, 5 * (k + 1)); + I.pressKey('u'); + } + I.pressKey('1'); + AtRichText.selectTextByGlobalOffset(1 + 5 * maxUsage, 5 * (maxUsage + 1)); + + I.see(`You can't use Label_1 more than ${maxUsage} time(s)`); +}); + +Data(maxUsageDataTable).Scenario('Max usages of labels in Paragraphs on region creation', async function({ I, LabelStudio, AtSidebar, AtParagraphs, current }) { + const { maxUsage } = current; + + I.amOnPage('/'); + LabelStudio.setFeatureFlags({ + fflag_fix_front_dev_3666_max_usages_on_region_creation_171122_short: true, + }); + LabelStudio.init({ + config: ` + + + + +`, + data: require('../examples/text-paragraphs').data, + }); + + LabelStudio.waitForObjectsReady(); + AtSidebar.seeRegions(0); + + for (let k = 0; k < maxUsage; k++) { + I.pressKey('1'); + AtParagraphs.selectTextByOffset(k + 1, 0, 3); + I.pressKey('u'); + } + I.pressKey('1'); + AtParagraphs.selectTextByOffset(maxUsage + 1, 0, 3); + + I.see(`You can't use Label_1 more than ${maxUsage} time(s)`); +}); + +Data(maxUsageDataTable).Scenario('Max usages of labels in Timeseries on region creation', async function({ I, LabelStudio, AtSidebar, AtTimeSeries, current }) { + const { maxUsage } = current; + + I.amOnPage('/'); + LabelStudio.setFeatureFlags({ + fflag_fix_front_dev_3666_max_usages_on_region_creation_171122_short: true, + }); + LabelStudio.init({ + config: ` + + + + + + + +`, + data: require('../examples/data/sample-sin.json'), + }); + + LabelStudio.waitForObjectsReady(); + AtSidebar.seeRegions(0); + await AtTimeSeries.lookForStage(); + + for (let k = 0; k < maxUsage; k++) { + I.pressKey('1'); + AtTimeSeries.drawByDrag(1 + k * 20, 10); + I.pressKey('u'); + } + I.pressKey('1'); + AtTimeSeries.drawByDrag(1 + maxUsage * 20, 10); + + I.see(`You can't use Label_1 more than ${maxUsage} time(s)`); +}); diff --git a/web/libs/editor/tests/e2e/tests/ner-text.test.js b/web/libs/editor/tests/e2e/tests/ner-text.test.js index fca960f77bc8..2c95c7379481 100644 --- a/web/libs/editor/tests/e2e/tests/ner-text.test.js +++ b/web/libs/editor/tests/e2e/tests/ner-text.test.js @@ -259,3 +259,26 @@ Scenario('NER Text with SECURE MODE', async function({ I, LabelStudio }) { window.LS_SECURE_MODE = window.OLD_LS_SECURE_MODE; }); }); + +Scenario('NER Text regions in Outliner', async function({ I, LabelStudio }) { + const params = { + annotations: [{ id: 'TestCmpl', result: resultsFromUrl }], + config: configUrl, + data: { url }, + }; + + I.amOnPage('/'); + // enabling both flags for New UI, but will work with Otliner one only as well + LabelStudio.setFeatureFlags({ + ff_front_1170_outliner_030222_short: true, + fflag_feat_front_dev_3873_labeling_ui_improvements_short: true, + }); + LabelStudio.init(params); + + I.waitForElement('.lsf-richtext__line', 60); + + I.see('American political leader'); + + I.seeElement(locate('.lsf-outliner-item').withText(resultsFromUrl[0].value.text)); + I.seeElement(locate('.lsf-outliner-item').withText(resultsFromUrl[1].value.text)); +}); diff --git a/web/libs/editor/tests/e2e/tests/ocr.test.js b/web/libs/editor/tests/e2e/tests/ocr.test.js index 83869984b024..cb82ce333eb1 100644 --- a/web/libs/editor/tests/e2e/tests/ocr.test.js +++ b/web/libs/editor/tests/e2e/tests/ocr.test.js @@ -154,7 +154,7 @@ Scenario('Drawing multiple blank regions and then attaching labels', async ({ I, 'ff_front_1170_outliner_030222_short': true, }); I.amOnPage('/'); - LabelStudio.init({ config: createConfig( ), data }); + LabelStudio.init({ config: createConfig(), data }); AtImageView.waitForImage(); AtSettings.open(); AtSettings.setGeneralSettings({ @@ -192,12 +192,12 @@ Scenario('Drawing multiple blank regions and then attaching labels', async ({ I, } session('Deserialization', () => { I.amOnPage('/'); - LabelStudio.init({ config: createConfig( ), data, annotations: [{ id: 'test', result: results }] }); + LabelStudio.init({ config: createConfig(), data, annotations: [{ id: 'test', result: results }] }); AtImageView.waitForImage(); AtOutliner.seeRegions(regions.length); for (const [idx, region] of Object.entries(regions)) { if (region.text) { - I.seeInField(AtOutliner.locateRegionIndex((+idx)+1).find('.lsf-textarea-tag__input'), region.text); + I.seeInField(AtOutliner.locateRegionIndex((+idx) + 1).find('.lsf-textarea-tag__input'), region.text); } } }); diff --git a/web/libs/editor/tests/e2e/tests/paragraphs-filter.test.js b/web/libs/editor/tests/e2e/tests/paragraphs-filter.test.js index 9ebdbdbea2e5..7d3f58fc6a3e 100644 --- a/web/libs/editor/tests/e2e/tests/paragraphs-filter.test.js +++ b/web/libs/editor/tests/e2e/tests/paragraphs-filter.test.js @@ -1,600 +1,600 @@ -const assert = require('assert'); -const { omitBy } = require('./helpers'); - -Feature('Paragraphs filter'); - -const AUDIO = 'https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/audio/barradeen-emotional.mp3'; - -const ANNOTATIONS = [ - { - 'result': [ - { - 'id':'ryzr4QdL93', - 'from_name':'ner', - 'to_name':'text', - 'source':'$dialogue', - 'type':'paragraphlabels', - 'value':{ - 'start':'2', - 'end':'4', - 'startOffset':0, - 'endOffset':134, - 'paragraphlabels': ['Important Stuff'], - 'text': 'Uncomfortable silences. Why do we feel its necessary to yak about nonsense in order to be comfortable?I dont know. Thats a good question.Thats when you know you found somebody really special. When you can just shut the door closed a minute, and comfortably share silence.', - }, - }, - ], - }, -]; - -const DATA = { - audio: AUDIO, - dialogue: [ - { - start: 3.1, - end: 5.6, - author: 'Mia Wallace', - text: 'Dont you hate that?', - }, - { - start: 4.2, - duration: 3.1, - author: 'Vincent Vega:', - text: 'Hate what?', - }, - { - author: 'Mia Wallace:', - text: 'Uncomfortable silences. Why do we feel its necessary to yak about nonsense in order to be comfortable?', - }, - { - start: 90, - author: 'Vincent Vega:', - text: 'I dont know. Thats a good question.', - }, - { - author: 'Mia Wallace:', - text: - 'Thats when you know you found somebody really special. When you can just shut the door closed a minute, and comfortably share silence.', - }, - ], -}; - -const CONFIG = ` - - - - - - - -`; - -const FEATURE_FLAGS = { - ff_front_dev_2669_paragraph_author_filter_210622_short: true, - fflag_fix_front_dev_2918_labeling_filtered_paragraphs_250822_short: true, -}; - -Scenario('Create two results using excluding a phrase by the filter', async ({ I, LabelStudio, AtSidebar, AtParagraphs, AtLabels }) => { - const params = { - data: DATA, - config: CONFIG, - }; - - I.amOnPage('/'); - - LabelStudio.setFeatureFlags(FEATURE_FLAGS); - LabelStudio.init(params); - AtSidebar.seeRegions(0); - - I.say('Select 2 regions in the consecutive phrases of the one person'); - - AtLabels.clickLabel('Random talk'); - AtParagraphs.setSelection( - AtParagraphs.locateText('Hate what?'), - 5, - AtParagraphs.locateText('Hate what?'), - 10, - ); - - AtLabels.clickLabel('Random talk'); - AtParagraphs.setSelection( - AtParagraphs.locateText('I dont know. Thats a good question.'), - 0, - AtParagraphs.locateText('I dont know. Thats a good question.'), - 11, - ); - AtSidebar.seeRegions(2); - - I.say('Take a snapshot'); - const twoActionsResult = LabelStudio.serialize(); - - I.say('Reset to initial state'); - LabelStudio.init(params); - AtSidebar.seeRegions(0); - - I.say('Filter the phrases by that person.'); - AtParagraphs.clickFilter('Vincent Vega:'); - - I.say('Try to get the same result in one action'); - - AtLabels.clickLabel('Random talk'); - AtParagraphs.setSelection( - AtParagraphs.locateText('Hate what?'), - 5, - AtParagraphs.locateText('I dont know. Thats a good question.'), - 11, - ); - AtSidebar.seeRegions(2); - - I.say('Take a second snapshot'); - const oneActionResult = LabelStudio.serialize(); - - I.say('The results should be identical'); - - assert.deepStrictEqual(twoActionsResult, oneActionResult); - -}); - -Scenario('Check different cases ', async ({ I, LabelStudio, AtSidebar, AtParagraphs, AtLabels }) => { - const dialogue = [ - 1,// 1 - 3,// 2 - 1,// 3 - 2,// 4 - 3,// 5 - 1,// 6 - 2,// 7 - 1,// 8 - 3,// 9 - 1,// 10 - ].map((authorId, idx)=>({ - start: idx+1, - end: idx+2, - author: `Author ${authorId}`, - text: `Message ${idx+1}`, - })); - const params = { - config: CONFIG, - data: { - audio: AUDIO, - dialogue, - }, - }; - - I.amOnPage('/'); - - LabelStudio.setFeatureFlags(FEATURE_FLAGS); - LabelStudio.init(params); - AtSidebar.seeRegions(0); - - I.say('Hide Author 3'); - AtParagraphs.clickFilter('Author 1', 'Author 2'); - - I.say('Make regions by selecting everything'); - AtLabels.clickLabel('Random talk'); - AtParagraphs.setSelection( - AtParagraphs.locateText('Message 1'), - 0, - AtParagraphs.locateText('Message 10'), - 10, - ); - - I.say('There should be 4 new regions'); - AtSidebar.seeRegions(4); - { - const result = await LabelStudio.serialize(); - - assert.strictEqual(result.length, 4); - - assert.deepStrictEqual(omitBy(result[0].value, (v, key)=> key === 'paragraphlabels'), { - 'start': '0', - 'end': '0', - 'startOffset': 0, - 'endOffset': 9, - 'text': 'Message 1', - }); - - assert.deepStrictEqual(omitBy(result[1].value, (v, key)=> key === 'paragraphlabels'), { - 'start': '2', - 'end': '3', - 'startOffset': 0, - 'endOffset': 9, - 'text': 'Message 3\n\nMessage 4', - }); - - assert.deepStrictEqual(omitBy(result[2].value, (v, key)=> key === 'paragraphlabels'), { - 'start': '5', - 'end': '7', - 'startOffset': 0, - 'endOffset': 9, - 'text': 'Message 6\n\nMessage 7\n\nMessage 8', - }); - - assert.deepStrictEqual(omitBy(result[3].value, (v, key)=> key === 'paragraphlabels'), { - 'start': '9', - 'end': '9', - 'startOffset': 0, - 'endOffset': 10, - 'text': 'Message 10', - }); - } - - I.say('Test the overlaps of regions #1'); - AtLabels.clickLabel('Important Stuff'); - AtParagraphs.setSelection( - AtParagraphs.locateText('Message 3'), - 4, - AtParagraphs.locateText('Message 8'), - 4, - ); - AtSidebar.seeRegions(6); - - { - const result = await LabelStudio.serialize(); - - assert.deepStrictEqual(omitBy(result[4].value, (v, key)=> key === 'paragraphlabels'), { - 'start': '2', - 'end': '3', - 'startOffset': 4, - 'endOffset': 9, - 'text': 'age 3\n\nMessage 4', - }); - - assert.deepStrictEqual(omitBy(result[5].value, (v, key)=> key === 'paragraphlabels'), { - 'start': '5', - 'end': '7', - 'startOffset': 0, - 'endOffset': 4, - 'text': 'Message 6\n\nMessage 7\n\nMess', - }); - } - - I.say('Test the overlaps of regions #2'); - AtParagraphs.clickFilter('Author 2', 'Author 3'); - AtLabels.clickLabel('Important Stuff'); - AtParagraphs.setSelection( - AtParagraphs.locateText('age 3'), - 4, - AtParagraphs.locateText('age 8'), - 3, - ); - AtSidebar.seeRegions(9); - - { - const result = await LabelStudio.serialize(); - - assert.deepStrictEqual(omitBy(result[6].value, (v, key)=> key === 'paragraphlabels'), { - 'start': '2', - 'end': '2', - 'startOffset': 8, - 'endOffset': 9, - 'text': '3', - }); - - assert.deepStrictEqual(omitBy(result[7].value, (v, key)=> key === 'paragraphlabels'), { - 'start': '4', - 'end': '5', - 'startOffset': 0, - 'endOffset': 9, - 'text': 'Message 5\n\nMessage 6', - }); - - assert.deepStrictEqual(omitBy(result[8].value, (v, key)=> key === 'paragraphlabels'), { - 'start': '7', - 'end': '7', - 'startOffset': 0, - 'endOffset': 7, - 'text': 'Message', - }); - } -}); - -Scenario( - 'Check start and end indices do not leak to other lines', - async ({ I, LabelStudio, AtSidebar, AtParagraphs, AtLabels }) => { - const dialogue = [ - 1, // 1 - 3, // 2 - 1, // 3 - 2, // 4 - 3, // 5 - 1, // 6 - 2, // 7 - 1, // 8 - 3, // 9 - 1, // 10 - 3, // 11 - 2, // 12 - 3, // 13 - 2, // 14 - ].map((authorId, idx) => ({ - start: idx + 1, - end: idx + 2, - author: `Author ${authorId}`, - text: `Message ${idx + 1}`, - })); - const params = { - config: CONFIG, - data: { - audio: AUDIO, - dialogue, - }, - }; - - LabelStudio.setFeatureFlags(FEATURE_FLAGS); - I.amOnPage('/'); - - LabelStudio.init(params); - AtSidebar.seeRegions(0); - - I.say( - 'Test selection from the end of one turn to end of the one below correctly creates a single region with proper start,startOffset,end,endOffset', - ); - AtLabels.clickLabel('Random talk'); - AtParagraphs.setSelection( - AtParagraphs.locateText('Message 8'), - 9, - AtParagraphs.locateText('Message 9'), - 9, - ); - AtSidebar.seeRegions(1); - - { - const result = await LabelStudio.serialize(); - - assert.deepStrictEqual( - omitBy(result[0].value, (v, key) => key === 'paragraphlabels'), - { - start: '8', - end: '8', - startOffset: 0, - endOffset: 9, - text: 'Message 9', - }, - ); - } - - I.say( - 'Test selection from the end of one turn to the very start of another below correctly creates a single region with proper start,startOffset,end,endOffset', - ); - AtLabels.clickLabel('Random talk'); - AtParagraphs.setSelection( - AtParagraphs.locateText('Message 8'), - 9, - AtParagraphs.locateText('Message 10'), - 0, - ); - AtSidebar.seeRegions(2); - - { - const result = await LabelStudio.serialize(); - - assert.deepStrictEqual( - omitBy(result[1].value, (v, key) => key === 'paragraphlabels'), - { - start: '8', - end: '8', - startOffset: 0, - endOffset: 9, - text: 'Message 9', - }, - ); - } - - I.say( - 'Test selection from the end of one turn to end of ones below across collapsed text correctly creates regions with proper start,startOffset,end,endOffset', - ); - AtParagraphs.clickFilter('Author 2', 'Author 3'); - AtLabels.clickLabel('Important Stuff'); - AtParagraphs.setSelection( - AtParagraphs.locateText('Message 2'), - 9, - AtParagraphs.locateText('Message 8'), - 9, - ); - AtSidebar.seeRegions(4); - - { - const result = await LabelStudio.serialize(); - - assert.deepStrictEqual( - omitBy(result[2].value, (v, key) => key === 'paragraphlabels'), - { - start: '3', - end: '4', - startOffset: 0, - endOffset: 9, - text: 'Message 4\n\nMessage 5', - }, - ); - assert.deepStrictEqual( - omitBy(result[3].value, (v, key) => key === 'paragraphlabels'), - { - start: '6', - end: '6', - startOffset: 0, - endOffset: 9, - text: 'Message 7', - }, - ); - } - - I.say( - 'Test selection from the end of one turn to very start of ones below across collapsed text correctly creates creates regions with proper start,startOffset,end,endOffset', - ); - AtLabels.clickLabel('Other'); - AtParagraphs.setSelection( - AtParagraphs.locateText('Message 2'), - 9, - AtParagraphs.locateText('Message 8'), - 0, - ); - AtSidebar.seeRegions(6); - - { - const result = await LabelStudio.serialize(); - - assert.deepStrictEqual( - omitBy(result[4].value, (v, key) => key === 'paragraphlabels'), - { - start: '3', - end: '4', - startOffset: 0, - endOffset: 9, - text: 'Message 4\n\nMessage 5', - }, - ); - assert.deepStrictEqual( - omitBy(result[5].value, (v, key) => key === 'paragraphlabels'), - { - start: '6', - end: '6', - startOffset: 0, - endOffset: 9, - text: 'Message 7', - }, - ); - } - - I.say( - 'Test selection from the end of Message 11 to the start of Message 14 to get region over Message 12 and Message 13', - ); - AtLabels.clickLabel('Random talk'); - AtParagraphs.setSelection( - AtParagraphs.locateText('Message 11'), - 10, - AtParagraphs.locateText('Message 14'), - 0, - ); - AtSidebar.seeRegions(7); - - { - const result = await LabelStudio.serialize(); - - assert.deepStrictEqual( - omitBy(result[6].value, (v, key) => key === 'paragraphlabels'), - { - start: '11', - end: '12', - startOffset: 0, - endOffset: 10, - text: 'Message 12\n\nMessage 13', - }, - ); - } - }, -); - -Scenario('Selecting the end character on a paragraph phrase to the very start of other phrases includes all selected phrases', async ({ I, LabelStudio, AtSidebar, AtParagraphs, AtLabels }) => { - const params = { - data: DATA, - config: CONFIG, - }; - - I.amOnPage('/'); - - LabelStudio.setFeatureFlags(FEATURE_FLAGS); - LabelStudio.init(params); - AtSidebar.seeRegions(0); - - I.say('Select 2 regions in the consecutive phrases'); - - AtLabels.clickLabel('Random talk'); - AtParagraphs.setSelection( - AtParagraphs.locateText('Dont you hate that?'), - 18, - AtParagraphs.locateText('Uncomfortable silences. Why do we feel its necessary to yak about nonsense in order to be comfortable?'), - 0, - ); - - AtSidebar.seeRegions(1); - - const result = await LabelStudio.serialize(); - - assert.deepStrictEqual( - omitBy(result[0].value, (v, key) => key === 'paragraphlabels'), - { - start: '0', - end: '1', - startOffset: 18, - endOffset: 10, - text: '?\n\nHate what?', - }, - ); -}); - -Scenario('Selecting the end character on a paragraph phrase to the very start of other phrases includes all selected phrases except the very last one', async ({ I, LabelStudio, AtSidebar, AtParagraphs, AtLabels }) => { - const params = { - data: { - ...DATA, - dialogue: DATA.dialogue.map(d => [d, { ...d, text: `${d.text}2` }]).flat(), - }, - config: CONFIG, - }; - - I.amOnPage('/'); - - LabelStudio.setFeatureFlags(FEATURE_FLAGS); - LabelStudio.init(params); - AtSidebar.seeRegions(0); - - - I.say('Select 2 regions in the consecutive phrases of the one person'); - AtParagraphs.clickFilter('Vincent Vega'); - AtLabels.clickLabel('Random talk'); - AtParagraphs.setSelection( - AtParagraphs.locateText('Hate what?2'), - 10, - AtParagraphs.locateText('I dont know. Thats a good question.2'), - 0, - ); - - AtSidebar.seeRegions(2); - - const result = await LabelStudio.serialize(); - - assert.deepStrictEqual( - omitBy(result[0].value, (v, key) => key === 'paragraphlabels'), - { - start: '3', - end: '3', - startOffset: 10, - endOffset: 11, - text: '2', - }, - ); - assert.deepStrictEqual( - omitBy(result[1].value, (v, key) => key === 'paragraphlabels'), - { - start: '6', - end: '6', - startOffset: 0, - endOffset: 35, - text: 'I dont know. Thats a good question.', - }, - ); -}); - -Scenario('Initializing a paragraph region range should not include author names in text', async ({ I, LabelStudio, AtSidebar }) => { - const params = { - data: DATA, - annotations: ANNOTATIONS, - config: CONFIG, - }; - - I.amOnPage('/'); - LabelStudio.setFeatureFlags(FEATURE_FLAGS); - - const [{ result : [region] }] = ANNOTATIONS; - const { paragraphlabels: _paragraphlabels, ...value } = region.value; - - LabelStudio.init(params); - AtSidebar.seeRegions(1); - - const result = await LabelStudio.serialize(); - - assert.deepStrictEqual( - omitBy(result[0].value, (v, key) => key === 'paragraphlabels'), - value, - ); -}); +const assert = require('assert'); +const { omitBy } = require('./helpers'); + +Feature('Paragraphs filter'); + +const AUDIO = 'https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/audio/barradeen-emotional.mp3'; + +const ANNOTATIONS = [ + { + 'result': [ + { + 'id': 'ryzr4QdL93', + 'from_name': 'ner', + 'to_name': 'text', + 'source': '$dialogue', + 'type': 'paragraphlabels', + 'value': { + 'start': '2', + 'end': '4', + 'startOffset': 0, + 'endOffset': 134, + 'paragraphlabels': ['Important Stuff'], + 'text': 'Uncomfortable silences. Why do we feel its necessary to yak about nonsense in order to be comfortable?I dont know. Thats a good question.Thats when you know you found somebody really special. When you can just shut the door closed a minute, and comfortably share silence.', + }, + }, + ], + }, +]; + +const DATA = { + audio: AUDIO, + dialogue: [ + { + start: 3.1, + end: 5.6, + author: 'Mia Wallace', + text: 'Dont you hate that?', + }, + { + start: 4.2, + duration: 3.1, + author: 'Vincent Vega:', + text: 'Hate what?', + }, + { + author: 'Mia Wallace:', + text: 'Uncomfortable silences. Why do we feel its necessary to yak about nonsense in order to be comfortable?', + }, + { + start: 90, + author: 'Vincent Vega:', + text: 'I dont know. Thats a good question.', + }, + { + author: 'Mia Wallace:', + text: + 'Thats when you know you found somebody really special. When you can just shut the door closed a minute, and comfortably share silence.', + }, + ], +}; + +const CONFIG = ` + + + + + + + +`; + +const FEATURE_FLAGS = { + ff_front_dev_2669_paragraph_author_filter_210622_short: true, + fflag_fix_front_dev_2918_labeling_filtered_paragraphs_250822_short: true, +}; + +Scenario('Create two results using excluding a phrase by the filter', async ({ I, LabelStudio, AtSidebar, AtParagraphs, AtLabels }) => { + const params = { + data: DATA, + config: CONFIG, + }; + + I.amOnPage('/'); + + LabelStudio.setFeatureFlags(FEATURE_FLAGS); + LabelStudio.init(params); + AtSidebar.seeRegions(0); + + I.say('Select 2 regions in the consecutive phrases of the one person'); + + AtLabels.clickLabel('Random talk'); + AtParagraphs.setSelection( + AtParagraphs.locateText('Hate what?'), + 5, + AtParagraphs.locateText('Hate what?'), + 10, + ); + + AtLabels.clickLabel('Random talk'); + AtParagraphs.setSelection( + AtParagraphs.locateText('I dont know. Thats a good question.'), + 0, + AtParagraphs.locateText('I dont know. Thats a good question.'), + 11, + ); + AtSidebar.seeRegions(2); + + I.say('Take a snapshot'); + const twoActionsResult = LabelStudio.serialize(); + + I.say('Reset to initial state'); + LabelStudio.init(params); + AtSidebar.seeRegions(0); + + I.say('Filter the phrases by that person.'); + AtParagraphs.clickFilter('Vincent Vega:'); + + I.say('Try to get the same result in one action'); + + AtLabels.clickLabel('Random talk'); + AtParagraphs.setSelection( + AtParagraphs.locateText('Hate what?'), + 5, + AtParagraphs.locateText('I dont know. Thats a good question.'), + 11, + ); + AtSidebar.seeRegions(2); + + I.say('Take a second snapshot'); + const oneActionResult = LabelStudio.serialize(); + + I.say('The results should be identical'); + + assert.deepStrictEqual(twoActionsResult, oneActionResult); + +}); + +Scenario('Check different cases ', async ({ I, LabelStudio, AtSidebar, AtParagraphs, AtLabels }) => { + const dialogue = [ + 1,// 1 + 3,// 2 + 1,// 3 + 2,// 4 + 3,// 5 + 1,// 6 + 2,// 7 + 1,// 8 + 3,// 9 + 1,// 10 + ].map((authorId, idx) => ({ + start: idx + 1, + end: idx + 2, + author: `Author ${authorId}`, + text: `Message ${idx + 1}`, + })); + const params = { + config: CONFIG, + data: { + audio: AUDIO, + dialogue, + }, + }; + + I.amOnPage('/'); + + LabelStudio.setFeatureFlags(FEATURE_FLAGS); + LabelStudio.init(params); + AtSidebar.seeRegions(0); + + I.say('Hide Author 3'); + AtParagraphs.clickFilter('Author 1', 'Author 2'); + + I.say('Make regions by selecting everything'); + AtLabels.clickLabel('Random talk'); + AtParagraphs.setSelection( + AtParagraphs.locateText('Message 1'), + 0, + AtParagraphs.locateText('Message 10'), + 10, + ); + + I.say('There should be 4 new regions'); + AtSidebar.seeRegions(4); + { + const result = await LabelStudio.serialize(); + + assert.strictEqual(result.length, 4); + + assert.deepStrictEqual(omitBy(result[0].value, (v, key) => key === 'paragraphlabels'), { + 'start': '0', + 'end': '0', + 'startOffset': 0, + 'endOffset': 9, + 'text': 'Message 1', + }); + + assert.deepStrictEqual(omitBy(result[1].value, (v, key) => key === 'paragraphlabels'), { + 'start': '2', + 'end': '3', + 'startOffset': 0, + 'endOffset': 9, + 'text': 'Message 3\n\nMessage 4', + }); + + assert.deepStrictEqual(omitBy(result[2].value, (v, key) => key === 'paragraphlabels'), { + 'start': '5', + 'end': '7', + 'startOffset': 0, + 'endOffset': 9, + 'text': 'Message 6\n\nMessage 7\n\nMessage 8', + }); + + assert.deepStrictEqual(omitBy(result[3].value, (v, key) => key === 'paragraphlabels'), { + 'start': '9', + 'end': '9', + 'startOffset': 0, + 'endOffset': 10, + 'text': 'Message 10', + }); + } + + I.say('Test the overlaps of regions #1'); + AtLabels.clickLabel('Important Stuff'); + AtParagraphs.setSelection( + AtParagraphs.locateText('Message 3'), + 4, + AtParagraphs.locateText('Message 8'), + 4, + ); + AtSidebar.seeRegions(6); + + { + const result = await LabelStudio.serialize(); + + assert.deepStrictEqual(omitBy(result[4].value, (v, key) => key === 'paragraphlabels'), { + 'start': '2', + 'end': '3', + 'startOffset': 4, + 'endOffset': 9, + 'text': 'age 3\n\nMessage 4', + }); + + assert.deepStrictEqual(omitBy(result[5].value, (v, key) => key === 'paragraphlabels'), { + 'start': '5', + 'end': '7', + 'startOffset': 0, + 'endOffset': 4, + 'text': 'Message 6\n\nMessage 7\n\nMess', + }); + } + + I.say('Test the overlaps of regions #2'); + AtParagraphs.clickFilter('Author 2', 'Author 3'); + AtLabels.clickLabel('Important Stuff'); + AtParagraphs.setSelection( + AtParagraphs.locateText('age 3'), + 4, + AtParagraphs.locateText('age 8'), + 3, + ); + AtSidebar.seeRegions(9); + + { + const result = await LabelStudio.serialize(); + + assert.deepStrictEqual(omitBy(result[6].value, (v, key) => key === 'paragraphlabels'), { + 'start': '2', + 'end': '2', + 'startOffset': 8, + 'endOffset': 9, + 'text': '3', + }); + + assert.deepStrictEqual(omitBy(result[7].value, (v, key) => key === 'paragraphlabels'), { + 'start': '4', + 'end': '5', + 'startOffset': 0, + 'endOffset': 9, + 'text': 'Message 5\n\nMessage 6', + }); + + assert.deepStrictEqual(omitBy(result[8].value, (v, key) => key === 'paragraphlabels'), { + 'start': '7', + 'end': '7', + 'startOffset': 0, + 'endOffset': 7, + 'text': 'Message', + }); + } +}); + +Scenario( + 'Check start and end indices do not leak to other lines', + async ({ I, LabelStudio, AtSidebar, AtParagraphs, AtLabels }) => { + const dialogue = [ + 1, // 1 + 3, // 2 + 1, // 3 + 2, // 4 + 3, // 5 + 1, // 6 + 2, // 7 + 1, // 8 + 3, // 9 + 1, // 10 + 3, // 11 + 2, // 12 + 3, // 13 + 2, // 14 + ].map((authorId, idx) => ({ + start: idx + 1, + end: idx + 2, + author: `Author ${authorId}`, + text: `Message ${idx + 1}`, + })); + const params = { + config: CONFIG, + data: { + audio: AUDIO, + dialogue, + }, + }; + + LabelStudio.setFeatureFlags(FEATURE_FLAGS); + I.amOnPage('/'); + + LabelStudio.init(params); + AtSidebar.seeRegions(0); + + I.say( + 'Test selection from the end of one turn to end of the one below correctly creates a single region with proper start,startOffset,end,endOffset', + ); + AtLabels.clickLabel('Random talk'); + AtParagraphs.setSelection( + AtParagraphs.locateText('Message 8'), + 9, + AtParagraphs.locateText('Message 9'), + 9, + ); + AtSidebar.seeRegions(1); + + { + const result = await LabelStudio.serialize(); + + assert.deepStrictEqual( + omitBy(result[0].value, (v, key) => key === 'paragraphlabels'), + { + start: '8', + end: '8', + startOffset: 0, + endOffset: 9, + text: 'Message 9', + }, + ); + } + + I.say( + 'Test selection from the end of one turn to the very start of another below correctly creates a single region with proper start,startOffset,end,endOffset', + ); + AtLabels.clickLabel('Random talk'); + AtParagraphs.setSelection( + AtParagraphs.locateText('Message 8'), + 9, + AtParagraphs.locateText('Message 10'), + 0, + ); + AtSidebar.seeRegions(2); + + { + const result = await LabelStudio.serialize(); + + assert.deepStrictEqual( + omitBy(result[1].value, (v, key) => key === 'paragraphlabels'), + { + start: '8', + end: '8', + startOffset: 0, + endOffset: 9, + text: 'Message 9', + }, + ); + } + + I.say( + 'Test selection from the end of one turn to end of ones below across collapsed text correctly creates regions with proper start,startOffset,end,endOffset', + ); + AtParagraphs.clickFilter('Author 2', 'Author 3'); + AtLabels.clickLabel('Important Stuff'); + AtParagraphs.setSelection( + AtParagraphs.locateText('Message 2'), + 9, + AtParagraphs.locateText('Message 8'), + 9, + ); + AtSidebar.seeRegions(4); + + { + const result = await LabelStudio.serialize(); + + assert.deepStrictEqual( + omitBy(result[2].value, (v, key) => key === 'paragraphlabels'), + { + start: '3', + end: '4', + startOffset: 0, + endOffset: 9, + text: 'Message 4\n\nMessage 5', + }, + ); + assert.deepStrictEqual( + omitBy(result[3].value, (v, key) => key === 'paragraphlabels'), + { + start: '6', + end: '6', + startOffset: 0, + endOffset: 9, + text: 'Message 7', + }, + ); + } + + I.say( + 'Test selection from the end of one turn to very start of ones below across collapsed text correctly creates creates regions with proper start,startOffset,end,endOffset', + ); + AtLabels.clickLabel('Other'); + AtParagraphs.setSelection( + AtParagraphs.locateText('Message 2'), + 9, + AtParagraphs.locateText('Message 8'), + 0, + ); + AtSidebar.seeRegions(6); + + { + const result = await LabelStudio.serialize(); + + assert.deepStrictEqual( + omitBy(result[4].value, (v, key) => key === 'paragraphlabels'), + { + start: '3', + end: '4', + startOffset: 0, + endOffset: 9, + text: 'Message 4\n\nMessage 5', + }, + ); + assert.deepStrictEqual( + omitBy(result[5].value, (v, key) => key === 'paragraphlabels'), + { + start: '6', + end: '6', + startOffset: 0, + endOffset: 9, + text: 'Message 7', + }, + ); + } + + I.say( + 'Test selection from the end of Message 11 to the start of Message 14 to get region over Message 12 and Message 13', + ); + AtLabels.clickLabel('Random talk'); + AtParagraphs.setSelection( + AtParagraphs.locateText('Message 11'), + 10, + AtParagraphs.locateText('Message 14'), + 0, + ); + AtSidebar.seeRegions(7); + + { + const result = await LabelStudio.serialize(); + + assert.deepStrictEqual( + omitBy(result[6].value, (v, key) => key === 'paragraphlabels'), + { + start: '11', + end: '12', + startOffset: 0, + endOffset: 10, + text: 'Message 12\n\nMessage 13', + }, + ); + } + }, +); + +Scenario('Selecting the end character on a paragraph phrase to the very start of other phrases includes all selected phrases', async ({ I, LabelStudio, AtSidebar, AtParagraphs, AtLabels }) => { + const params = { + data: DATA, + config: CONFIG, + }; + + I.amOnPage('/'); + + LabelStudio.setFeatureFlags(FEATURE_FLAGS); + LabelStudio.init(params); + AtSidebar.seeRegions(0); + + I.say('Select 2 regions in the consecutive phrases'); + + AtLabels.clickLabel('Random talk'); + AtParagraphs.setSelection( + AtParagraphs.locateText('Dont you hate that?'), + 18, + AtParagraphs.locateText('Uncomfortable silences. Why do we feel its necessary to yak about nonsense in order to be comfortable?'), + 0, + ); + + AtSidebar.seeRegions(1); + + const result = await LabelStudio.serialize(); + + assert.deepStrictEqual( + omitBy(result[0].value, (v, key) => key === 'paragraphlabels'), + { + start: '0', + end: '1', + startOffset: 18, + endOffset: 10, + text: '?\n\nHate what?', + }, + ); +}); + +Scenario('Selecting the end character on a paragraph phrase to the very start of other phrases includes all selected phrases except the very last one', async ({ I, LabelStudio, AtSidebar, AtParagraphs, AtLabels }) => { + const params = { + data: { + ...DATA, + dialogue: DATA.dialogue.map(d => [d, { ...d, text: `${d.text}2` }]).flat(), + }, + config: CONFIG, + }; + + I.amOnPage('/'); + + LabelStudio.setFeatureFlags(FEATURE_FLAGS); + LabelStudio.init(params); + AtSidebar.seeRegions(0); + + + I.say('Select 2 regions in the consecutive phrases of the one person'); + AtParagraphs.clickFilter('Vincent Vega'); + AtLabels.clickLabel('Random talk'); + AtParagraphs.setSelection( + AtParagraphs.locateText('Hate what?2'), + 10, + AtParagraphs.locateText('I dont know. Thats a good question.2'), + 0, + ); + + AtSidebar.seeRegions(2); + + const result = await LabelStudio.serialize(); + + assert.deepStrictEqual( + omitBy(result[0].value, (v, key) => key === 'paragraphlabels'), + { + start: '3', + end: '3', + startOffset: 10, + endOffset: 11, + text: '2', + }, + ); + assert.deepStrictEqual( + omitBy(result[1].value, (v, key) => key === 'paragraphlabels'), + { + start: '6', + end: '6', + startOffset: 0, + endOffset: 35, + text: 'I dont know. Thats a good question.', + }, + ); +}); + +Scenario('Initializing a paragraph region range should not include author names in text', async ({ I, LabelStudio, AtSidebar }) => { + const params = { + data: DATA, + annotations: ANNOTATIONS, + config: CONFIG, + }; + + I.amOnPage('/'); + LabelStudio.setFeatureFlags(FEATURE_FLAGS); + + const [{ result: [region] }] = ANNOTATIONS; + const { paragraphlabels: _paragraphlabels, ...value } = region.value; + + LabelStudio.init(params); + AtSidebar.seeRegions(1); + + const result = await LabelStudio.serialize(); + + assert.deepStrictEqual( + omitBy(result[0].value, (v, key) => key === 'paragraphlabels'), + value, + ); +}); diff --git a/web/libs/editor/tests/e2e/tests/regression-tests/brush-relations.test.js b/web/libs/editor/tests/e2e/tests/regression-tests/brush-relations.test.js index 451e4534b48d..4b748d9a8172 100644 --- a/web/libs/editor/tests/e2e/tests/regression-tests/brush-relations.test.js +++ b/web/libs/editor/tests/e2e/tests/regression-tests/brush-relations.test.js @@ -1,103 +1,103 @@ -Feature('Brush relations').tag('@regress'); - -const IMAGE = 'https://user.fm/files/v2-901310d5cb3fa90e0616ca10590bacb3/spacexmoon-800x501.jpg'; - -const config = ` - - - - - `; - -function getPointAtSpiral(t, v , w) { - return { x: v * t * Math.cos(w * t), y: v * t * Math.sin(w * t) }; -} - -function generateSpiralPoints(x0, y0, R, v , w) { - let t = 1, x, y; - const points = []; - - do { - ({ x, y } = getPointAtSpiral(t++, v, w)); - points.push([x + x0, y + y0]); - } while (x ** 2 + y ** 2 < R ** 2); - return points; -} - -Scenario('Brush relations shouldn\'t crash everything', async ({ I, LabelStudio, AtImageView, AtSidebar }) => { - const params = { - config, - data: { image: IMAGE }, - }; - - I.amOnPage('/'); - LabelStudio.init(params); - AtImageView.waitForImage(); - AtSidebar.seeRegions(0); - await AtImageView.lookForStage(); - const canvasSize = await AtImageView.getCanvasSize(); - const regionsCentralPoints = []; - - // create 4 brush regions - for (let i = 0; i < 4; i++) { - // find start position - const x = canvasSize.width / 4 * ((i % 2) * 2 + 1); - const y = canvasSize.height / 4 * ((Math.floor(i / 2) % 2) * 2 + 1); - // generate points in a spiral - const points = generateSpiralPoints(x, y, Math.min(canvasSize.width / 6, canvasSize.height / 6), .4, Math.PI / 18); - - // select the brush label - I.pressKey('1'); - // draw a brush region - AtImageView.drawThroughPoints(points); - AtSidebar.seeRegions(i+1); - // unselect the region - I.pressKey('u'); - // save the central point - regionsCentralPoints.push({ x, y }); - } - - // Check that we can create a relation between the brush regions - { - // switch to the move tool for easy region selecting - I.pressKey('v'); - // select the first region - AtImageView.clickAt(regionsCentralPoints[0].x, regionsCentralPoints[0].y); - // create relation to the second region - I.pressKey(['alt', 'r']); - AtImageView.clickAt(regionsCentralPoints[1].x, regionsCentralPoints[1].y); - // check that the relation has been created - AtSidebar.seeRelations(1); - } - - // Check that relations work fine on a brush restoration (from rle) - { - // export annotation - const annotation = await LabelStudio.serialize(); - - // reload LS with that datalabel studio logo - LabelStudio.init({ - ...params, - annotations: [{ id: 'imported', result: annotation }], - }); - - AtImageView.waitForImage(); - // Check that relation still exist - AtSidebar.seeRelations(1); - - // Try to create new relation with restored regions - { - // switch to the move tool for easy region selecting - I.pressKey('v'); - // select the third region - AtImageView.clickAt(regionsCentralPoints[2].x, regionsCentralPoints[2].y); - // create relation to the fourth region - I.pressKey(['alt', 'r']); - AtImageView.clickAt(regionsCentralPoints[3].x, regionsCentralPoints[3].y); - // check that the relation has been created - AtSidebar.seeRelations(2); - } - - /// The potential errors should be caught by `errorsCollector` plugin - } -}); +Feature('Brush relations').tag('@regress'); + +const IMAGE = 'https://data.heartex.net/open-images/train_0/mini/0030019819f25b28.jpg'; + +const config = ` + + + + + `; + +function getPointAtSpiral(t, v , w) { + return { x: v * t * Math.cos(w * t), y: v * t * Math.sin(w * t) }; +} + +function generateSpiralPoints(x0, y0, R, v , w) { + let t = 1, x, y; + const points = []; + + do { + ({ x, y } = getPointAtSpiral(t++, v, w)); + points.push([x + x0, y + y0]); + } while (x ** 2 + y ** 2 < R ** 2); + return points; +} + +Scenario('Brush relations shouldn\'t crash everything', async ({ I, LabelStudio, AtImageView, AtSidebar }) => { + const params = { + config, + data: { image: IMAGE }, + }; + + I.amOnPage('/'); + LabelStudio.init(params); + AtImageView.waitForImage(); + AtSidebar.seeRegions(0); + await AtImageView.lookForStage(); + const canvasSize = await AtImageView.getCanvasSize(); + const regionsCentralPoints = []; + + // create 4 brush regions + for (let i = 0; i < 4; i++) { + // find start position + const x = canvasSize.width / 4 * ((i % 2) * 2 + 1); + const y = canvasSize.height / 4 * ((Math.floor(i / 2) % 2) * 2 + 1); + // generate points in a spiral + const points = generateSpiralPoints(x, y, Math.min(canvasSize.width / 6, canvasSize.height / 6), .4, Math.PI / 18); + + // select the brush label + I.pressKey('1'); + // draw a brush region + AtImageView.drawThroughPoints(points); + AtSidebar.seeRegions(i + 1); + // unselect the region + I.pressKey('u'); + // save the central point + regionsCentralPoints.push({ x, y }); + } + + // Check that we can create a relation between the brush regions + { + // switch to the move tool for easy region selecting + I.pressKey('v'); + // select the first region + AtImageView.clickAt(regionsCentralPoints[0].x, regionsCentralPoints[0].y); + // create relation to the second region + I.pressKey(['alt', 'r']); + AtImageView.clickAt(regionsCentralPoints[1].x, regionsCentralPoints[1].y); + // check that the relation has been created + AtSidebar.seeRelations(1); + } + + // Check that relations work fine on a brush restoration (from rle) + { + // export annotation + const annotation = await LabelStudio.serialize(); + + // reload LS with that datalabel studio logo + LabelStudio.init({ + ...params, + annotations: [{ id: 'imported', result: annotation }], + }); + + AtImageView.waitForImage(); + // Check that relation still exist + AtSidebar.seeRelations(1); + + // Try to create new relation with restored regions + { + // switch to the move tool for easy region selecting + I.pressKey('v'); + // select the third region + AtImageView.clickAt(regionsCentralPoints[2].x, regionsCentralPoints[2].y); + // create relation to the fourth region + I.pressKey(['alt', 'r']); + AtImageView.clickAt(regionsCentralPoints[3].x, regionsCentralPoints[3].y); + // check that the relation has been created + AtSidebar.seeRelations(2); + } + + /// The potential errors should be caught by `errorsCollector` plugin + } +}); diff --git a/web/libs/editor/tests/e2e/tests/regression-tests/dynamic-choices.test.js b/web/libs/editor/tests/e2e/tests/regression-tests/dynamic-choices.test.js index 53e3e9ad88ae..28020b51cecc 100644 --- a/web/libs/editor/tests/e2e/tests/regression-tests/dynamic-choices.test.js +++ b/web/libs/editor/tests/e2e/tests/regression-tests/dynamic-choices.test.js @@ -1,80 +1,80 @@ -const assert = require('assert'); - -Feature('Dynamic choices').tag('@regress'); - -Scenario('Hotkeys for dynamic choices', async ({ I, LabelStudio })=>{ - const params = { - config: ` - - - - - -`, - data: { - text: 'Some text', - choices: [ - { - value: 'Header 1', - children: [ - { - value: 'Option 1.1', - }, - { - value: 'Option 1.2', - }, - ], - }, - { - value: 'Header 2', - children: [ - { - value: 'Option 2.1', - }, - { - value: 'Option 2.2', - }, - { - value: 'Option 2.3', - hotkey: 'q', - }, - ], - }, - ], - }, - annotations: [{ - id: 'test', - result: [], - }], - }; - - I.amOnPage('/'); - - LabelStudio.init(params); - - I.see('Header 1'); - I.see('Option 1.1'); - I.see('Header 2'); - I.see('Option 2.2'); - - I.say('Select some choices by pressing hotkeys'); - - I.pressKey('1'); - I.pressKey('q'); - I.pressKey('s'); - - I.say('Check the result'); - - I.seeElement('.ant-checkbox-checked [name=\'Header 1\']'); - I.seeElement('.ant-checkbox-indeterminate [name=\'Header 2\']'); - I.seeElement('.ant-checkbox-checked [name=\'Option 1.1\']'); - I.seeElement('.ant-checkbox-checked [name=\'Option 1.2\']'); - I.seeElement('.ant-checkbox-checked [name=\'Option 2.3\']'); - I.seeElement('.ant-checkbox-checked [name=\'Static option\']'); - - const result = await LabelStudio.serialize(); - - assert.deepStrictEqual(result.length, 1); - assert.deepStrictEqual(result[0].value.choices, [['Static option'], ['Header 1'], ['Header 1', 'Option 1.1'], ['Header 1', 'Option 1.2'], ['Header 2', 'Option 2.3']]); - -}); +const assert = require('assert'); + +Feature('Dynamic choices').tag('@regress'); + +Scenario('Hotkeys for dynamic choices', async ({ I, LabelStudio }) => { + const params = { + config: ` + + + + + +`, + data: { + text: 'Some text', + choices: [ + { + value: 'Header 1', + children: [ + { + value: 'Option 1.1', + }, + { + value: 'Option 1.2', + }, + ], + }, + { + value: 'Header 2', + children: [ + { + value: 'Option 2.1', + }, + { + value: 'Option 2.2', + }, + { + value: 'Option 2.3', + hotkey: 'q', + }, + ], + }, + ], + }, + annotations: [{ + id: 'test', + result: [], + }], + }; + + I.amOnPage('/'); + + LabelStudio.init(params); + + I.see('Header 1'); + I.see('Option 1.1'); + I.see('Header 2'); + I.see('Option 2.2'); + + I.say('Select some choices by pressing hotkeys'); + + I.pressKey('1'); + I.pressKey('q'); + I.pressKey('s'); + + I.say('Check the result'); + + I.seeElement('.ant-checkbox-checked [name=\'Header 1\']'); + I.seeElement('.ant-checkbox-indeterminate [name=\'Header 2\']'); + I.seeElement('.ant-checkbox-checked [name=\'Option 1.1\']'); + I.seeElement('.ant-checkbox-checked [name=\'Option 1.2\']'); + I.seeElement('.ant-checkbox-checked [name=\'Option 2.3\']'); + I.seeElement('.ant-checkbox-checked [name=\'Static option\']'); + + const result = await LabelStudio.serialize(); + + assert.deepStrictEqual(result.length, 1); + assert.deepStrictEqual(result[0].value.choices, [['Static option'], ['Header 1'], ['Header 1', 'Option 1.1'], ['Header 1', 'Option 1.2'], ['Header 2', 'Option 2.3']]); + +}); diff --git a/web/libs/editor/tests/e2e/tests/regression-tests/image-ctrl-drawing.test.js b/web/libs/editor/tests/e2e/tests/regression-tests/image-ctrl-drawing.test.js index d67580b6e624..01e5fc3f3531 100644 --- a/web/libs/editor/tests/e2e/tests/regression-tests/image-ctrl-drawing.test.js +++ b/web/libs/editor/tests/e2e/tests/regression-tests/image-ctrl-drawing.test.js @@ -206,6 +206,9 @@ Scenario('How it works without ctrl', async function({ I, LabelStudio, AtSidebar for (const regionPair of regionPairs) { const [outerRegion, innerRegion] = regionPair; + // Brush is not relevant in this case anymore (it will not interact with other regions) + if (innerRegion.shape === 'Brush') continue; + LabelStudio.init(params); AtImageView.waitForImage(); AtSidebar.seeRegions(0); diff --git a/web/libs/editor/tests/e2e/tests/regression-tests/image-width.test.js b/web/libs/editor/tests/e2e/tests/regression-tests/image-width.test.js index c8889c01d393..c19eca425887 100644 --- a/web/libs/editor/tests/e2e/tests/regression-tests/image-width.test.js +++ b/web/libs/editor/tests/e2e/tests/regression-tests/image-width.test.js @@ -1,44 +1,44 @@ -const assert = require('assert'); - -Feature('Image width parameter').tag('@regress'); - -const IMAGE = 'https://user.fm/files/v2-901310d5cb3fa90e0616ca10590bacb3/spacexmoon-800x501.jpg'; - -const config = ` - - - - `; - -Scenario('Setting width 50% shouldn\'t break canvas size on resize of working area', async ({ I, LabelStudio, AtImageView, AtSidebar }) => { - const params = { - config, - data: { image: IMAGE }, - }; - - I.amOnPage('/'); - LabelStudio.init(params); - AtImageView.waitForImage(); - AtSidebar.seeRegions(0); - await AtImageView.lookForStage(); - let canvasSize = await AtImageView.getCanvasSize(); - let imageSize = await AtImageView.getImageSize(); - - // The sizes of the canvas and image element should be equal (or almost equal) - assert.strictEqual(Math.ceil(imageSize.width), Math.ceil(canvasSize.width)); - assert.strictEqual(Math.ceil(imageSize.height), Math.ceil(canvasSize.height)); - - // Create full-size region - // This step is just for visual identification of the bug - AtImageView.drawByDrag(5, 5, canvasSize.width - 10, canvasSize.height - 10); - - I.resizeWindow(800, 900); - I.resizeWindow(1200, 900); - - canvasSize = await AtImageView.getCanvasSize(); - imageSize = await AtImageView.getImageSize(); - - // The sizes should still be equal (or almost equal) - assert.strictEqual(Math.ceil(imageSize.width), Math.ceil(canvasSize.width)); - assert.strictEqual(Math.ceil(imageSize.height), Math.ceil(canvasSize.height)); -}); +const assert = require('assert'); + +Feature('Image width parameter').tag('@regress'); + +const IMAGE = 'https://data.heartex.net/open-images/train_0/mini/0030019819f25b28.jpg'; + +const config = ` + + + + `; + +Scenario('Setting width 50% shouldn\'t break canvas size on resize of working area', async ({ I, LabelStudio, AtImageView, AtSidebar }) => { + const params = { + config, + data: { image: IMAGE }, + }; + + I.amOnPage('/'); + LabelStudio.init(params); + AtImageView.waitForImage(); + AtSidebar.seeRegions(0); + await AtImageView.lookForStage(); + let canvasSize = await AtImageView.getCanvasSize(); + let imageSize = await AtImageView.getImageSize(); + + // The sizes of the canvas and image element should be equal (or almost equal) + assert.strictEqual(Math.ceil(imageSize.width), Math.ceil(canvasSize.width)); + assert.strictEqual(Math.ceil(imageSize.height), Math.ceil(canvasSize.height)); + + // Create full-size region + // This step is just for visual identification of the bug + AtImageView.drawByDrag(5, 5, canvasSize.width - 10, canvasSize.height - 10); + + I.resizeWindow(800, 900); + I.resizeWindow(1200, 900); + + canvasSize = await AtImageView.getCanvasSize(); + imageSize = await AtImageView.getImageSize(); + + // The sizes should still be equal (or almost equal) + assert.strictEqual(Math.ceil(imageSize.width), Math.ceil(canvasSize.width)); + assert.strictEqual(Math.ceil(imageSize.height), Math.ceil(canvasSize.height)); +}); diff --git a/web/libs/editor/tests/e2e/tests/regression-tests/preselected-choices.test.js b/web/libs/editor/tests/e2e/tests/regression-tests/preselected-choices.test.js index 7a2cfd198726..226f91fdfefc 100644 --- a/web/libs/editor/tests/e2e/tests/regression-tests/preselected-choices.test.js +++ b/web/libs/editor/tests/e2e/tests/regression-tests/preselected-choices.test.js @@ -1,91 +1,91 @@ -const assert = require('assert'); - -Feature('Preselected choices'); - -Scenario('Make a duplicate of annotation with preselected choices', async ({ I, LabelStudio, AtTopbar })=>{ - const params = { - config: ` - - - - - - -`, - data: { - text: 'Some text', - }, - annotations: [{ - id: 'test', - result: [ - { - from_name: 'choices', - id: 'mDp7Hpbw6_', - origin: 'manual', - to_name: 'text', - type: 'choices', - value: { - choices: ['Option 2'], - }, - }, - ], - }], - }; - - I.amOnPage('/'); - LabelStudio.init(params); - // Try to create copy of current annotation - AtTopbar.click('[aria-label="Copy Annotation"]'); - const duplicateResult = await LabelStudio.serialize(); - - // Make sure there are no results other than the copied ones - assert.deepStrictEqual(duplicateResult.length, 1); - assert.deepStrictEqual(duplicateResult[0].value.choices, ['Option 2']); - - // Create new annotation - I.click('[aria-label="Annotations List Toggle"]'); - I.click('[aria-label="Create Annotation"]'); - const annotationWithPresetValues = await LabelStudio.serialize(); - - // Check that there is only the result come from selecting by default - assert.deepStrictEqual(annotationWithPresetValues.length, 1); - assert.deepStrictEqual(annotationWithPresetValues[0].value.choices, ['Option 1']); -}); - -Scenario('Make a duplicate of empty annotation with preselected choices', async ({ I, LabelStudio, AtTopbar })=>{ - const params = { - config: ` - - - - - - -`, - data: { - text: 'Some text', - }, - annotations: [{ - id: 'test', - result: [], - }], - }; - - I.amOnPage('/'); - LabelStudio.init(params); - // Try to create copy of current annotation - AtTopbar.click('[aria-label="Copy Annotation"]'); - const duplicateResult = await LabelStudio.serialize(); - - // Make sure there are no preselected results - assert.deepStrictEqual(duplicateResult.length, 0); - - // Create new annotation - I.click('[aria-label="Annotations List Toggle"]'); - I.click('[aria-label="Create Annotation"]'); - const annotationWithPresetValues = await LabelStudio.serialize(); - - // Check that there is only the result come from selecting by default - assert.deepStrictEqual(annotationWithPresetValues.length, 1); - assert.deepStrictEqual(annotationWithPresetValues[0].value.choices, ['Option 1']); -}); +const assert = require('assert'); + +Feature('Preselected choices'); + +Scenario('Make a duplicate of annotation with preselected choices', async ({ I, LabelStudio, AtTopbar }) => { + const params = { + config: ` + + + + + + +`, + data: { + text: 'Some text', + }, + annotations: [{ + id: 'test', + result: [ + { + from_name: 'choices', + id: 'mDp7Hpbw6_', + origin: 'manual', + to_name: 'text', + type: 'choices', + value: { + choices: ['Option 2'], + }, + }, + ], + }], + }; + + I.amOnPage('/'); + LabelStudio.init(params); + // Try to create copy of current annotation + AtTopbar.click('[aria-label="Copy Annotation"]'); + const duplicateResult = await LabelStudio.serialize(); + + // Make sure there are no results other than the copied ones + assert.deepStrictEqual(duplicateResult.length, 1); + assert.deepStrictEqual(duplicateResult[0].value.choices, ['Option 2']); + + // Create new annotation + I.click('[aria-label="Annotations List Toggle"]'); + I.click('[aria-label="Create Annotation"]'); + const annotationWithPresetValues = await LabelStudio.serialize(); + + // Check that there is only the result come from selecting by default + assert.deepStrictEqual(annotationWithPresetValues.length, 1); + assert.deepStrictEqual(annotationWithPresetValues[0].value.choices, ['Option 1']); +}); + +Scenario('Make a duplicate of empty annotation with preselected choices', async ({ I, LabelStudio, AtTopbar }) => { + const params = { + config: ` + + + + + + +`, + data: { + text: 'Some text', + }, + annotations: [{ + id: 'test', + result: [], + }], + }; + + I.amOnPage('/'); + LabelStudio.init(params); + // Try to create copy of current annotation + AtTopbar.click('[aria-label="Copy Annotation"]'); + const duplicateResult = await LabelStudio.serialize(); + + // Make sure there are no preselected results + assert.deepStrictEqual(duplicateResult.length, 0); + + // Create new annotation + I.click('[aria-label="Annotations List Toggle"]'); + I.click('[aria-label="Create Annotation"]'); + const annotationWithPresetValues = await LabelStudio.serialize(); + + // Check that there is only the result come from selecting by default + assert.deepStrictEqual(annotationWithPresetValues.length, 1); + assert.deepStrictEqual(annotationWithPresetValues[0].value.choices, ['Option 1']); +}); diff --git a/web/libs/editor/tests/e2e/tests/regression-tests/richtext.test.js b/web/libs/editor/tests/e2e/tests/regression-tests/richtext.test.js index 542c8977130a..4b3d71734bc6 100644 --- a/web/libs/editor/tests/e2e/tests/regression-tests/richtext.test.js +++ b/web/libs/editor/tests/e2e/tests/regression-tests/richtext.test.js @@ -293,7 +293,7 @@ Scenario('Neighboring nested regions misplacement', async ({ I, LabelStudio, AtS }, ]); - Data(startBeforeEndParams).Scenario('Start before end problem', async ({ I, LabelStudio, AtSidebar, AtRichText, current }) => { + Data(startBeforeEndParams).Scenario('Start before end problem', async ({ I, LabelStudio, AtSidebar, current }) => { const { tag, content, range } = current; LabelStudio.setFeatureFlags({ diff --git a/web/libs/editor/tests/e2e/tests/taxonomy.test.js b/web/libs/editor/tests/e2e/tests/taxonomy.test.js index 2fd8b96297fa..840bba6f9373 100644 --- a/web/libs/editor/tests/e2e/tests/taxonomy.test.js +++ b/web/libs/editor/tests/e2e/tests/taxonomy.test.js @@ -1,4 +1,3 @@ -/* global Before */ const assert = require('assert'); Feature('Taxonomy'); diff --git a/web/libs/editor/tests/e2e/tests/text-area.test.js b/web/libs/editor/tests/e2e/tests/text-area.test.js index 6187dec5631f..6a2dfdea6fb8 100644 --- a/web/libs/editor/tests/e2e/tests/text-area.test.js +++ b/web/libs/editor/tests/e2e/tests/text-area.test.js @@ -1,6 +1,3 @@ - -/* global Feature, Scenario */ - const assert = require('assert'); const { serialize } = require('./helpers'); diff --git a/web/libs/editor/tests/e2e/tests/textarea.skip-duplicates.test.js b/web/libs/editor/tests/e2e/tests/textarea.skip-duplicates.test.js index 60069fe9688e..2114eb6d7703 100644 --- a/web/libs/editor/tests/e2e/tests/textarea.skip-duplicates.test.js +++ b/web/libs/editor/tests/e2e/tests/textarea.skip-duplicates.test.js @@ -1,528 +1,534 @@ -const assert = require('assert'); - -Feature('Skip duplicates (textarea)'); - -const SKIP_DUPLICATES_ERROR = 'There is already an entry with that text. Please enter unique text.'; - -const scenarioDataTable = new DataTable(['scenarioKey']); - -const SK_SIMPLE = 'Simple textarea'; -const SK_PER_REGION = 'Per region'; -const SK_OCR = 'Ocr'; - -scenarioDataTable.add([SK_SIMPLE]); -scenarioDataTable.add([SK_PER_REGION]); -scenarioDataTable.add([SK_OCR]); - -const SCENARIO_PARAMS = { - [SK_SIMPLE]: { - data: { question: 'Is it a question?' }, - config: ` - -