From 2ae9f6099f34806a88f6ef6a1f2022d3063c2a21 Mon Sep 17 00:00:00 2001 From: Voiko Volodymyr Date: Wed, 17 Dec 2025 12:26:44 +0200 Subject: [PATCH 01/11] Fix data snapshots --- .../Resources/expected.snapptheming.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/SnappThemingDesignTokensSupportTests/Resources/expected.snapptheming.json b/Tests/SnappThemingDesignTokensSupportTests/Resources/expected.snapptheming.json index 0767b0b..bf911b6 100644 --- a/Tests/SnappThemingDesignTokensSupportTests/Resources/expected.snapptheming.json +++ b/Tests/SnappThemingDesignTokensSupportTests/Resources/expected.snapptheming.json @@ -39,7 +39,7 @@ } }, "images" : { - "filesImagesAlien" : "image\/svg+xml:base64:PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IS0tIFVwbG9hZGVkIHRvOiBTVkcgUmVwbywgd3d3LnN2Z3JlcG8uY29tLCBHZW5lcmF0b3I6IFNWRyBSZXBvIE1peGVyIFRvb2xzIC0tPg0KPHN2ZyB3aWR0aD0iODAwcHgiIGhlaWdodD0iODAwcHgiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4NCjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNOCAxNkwzLjU0MjIzIDEyLjMzODNDMS45MzI3OCAxMS4wMTYyIDEgOS4wNDI4NyAxIDYuOTYwMDVDMSAzLjExNjEyIDQuMTU2MDcgMCA4IDBDMTEuODQzOSAwIDE1IDMuMTE2MTIgMTUgNi45NjAwNUMxNSA5LjA0Mjg3IDE0LjA2NzIgMTEuMDE2MiAxMi40NTc4IDEyLjMzODNMOCAxNlpNMyA2SDVDNi4xMDQ1NyA2IDcgNi44OTU0MyA3IDhWOUwzIDcuNVY2Wk0xMSA2QzkuODk1NDMgNiA5IDYuODk1NDMgOSA4VjlMMTMgNy41VjZIMTFaIiBmaWxsPSIjMDAwMDAwIi8+DQo8L3N2Zz4=", + "filesImagesAlien" : "image\/svg+xml:base64:PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IS0tIFVwbG9hZGVkIHRvOiBTVkcgUmVwbywgd3d3LnN2Z3JlcG8uY29tLCBHZW5lcmF0b3I6IFNWRyBSZXBvIE1peGVyIFRvb2xzIC0tPgo8c3ZnIHdpZHRoPSI4MDBweCIgaGVpZ2h0PSI4MDBweCIgdmlld0JveD0iMCAwIDE2IDE2IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTggMTZMMy41NDIyMyAxMi4zMzgzQzEuOTMyNzggMTEuMDE2MiAxIDkuMDQyODcgMSA2Ljk2MDA1QzEgMy4xMTYxMiA0LjE1NjA3IDAgOCAwQzExLjg0MzkgMCAxNSAzLjExNjEyIDE1IDYuOTYwMDVDMTUgOS4wNDI4NyAxNC4wNjcyIDExLjAxNjIgMTIuNDU3OCAxMi4zMzgzTDggMTZaTTMgNkg1QzYuMTA0NTcgNiA3IDYuODk1NDMgNyA4VjlMMyA3LjVWNlpNMTEgNkM5Ljg5NTQzIDYgOSA2Ljg5NTQzIDkgOFY5TDEzIDcuNVY2SDExWiIgZmlsbD0iIzAwMDAwMCIvPgo8L3N2Zz4=", "filesImagesAlien2" : "image\/png:base64:iVBORw0KGgoAAAANSUhEUgAAAyAAAAMgCAQAAABxec7jAAAAAmJLR0QA\/4ePzL8AAC08SURBVHja7d1roJ1leebxa+dADgRCDFIORU5RpEI5iQKijKAUUHDQoqUIItrUqbaxqE1HYZqq0wZPGBwYo0A146BGiwyUcSgiWBAUQakIiikIyBkSAiHnZGc+gBji3jt7r70O7\/u8v\/\/\/m9\/c9\/1cF3nWet+VAM1gTF6Q3XNgjsxb8q58IB\/LvMzPxVmYq3JNbs7PclfuzZLnXJsNz7pmo\/\/13tyVn+XmXJOrsjAXZ34+m4\/lA3lX3pIjc2B2zwsyxh8bAOpaFTvm4Byfd+fMnJuv5Xu5I4ufq4Nu+HjuyLX5WublzLw7x+WV2VGtAED12Dr75\/j8Zc7O\/851uSdruloWw3VN7sl1+d85O+\/L8dkvWxkcAHSfaTkwJ2Z25ueq3JX+ShbG5l2Sm7MwczMzr8vuhgoAnSuNwzIz83JVHqxpYWy+ThZkdo7L7ukzbgAYHVvmkPxFvpDrs6TI0hjMxbku8\/NfcnAmWwIAGD5Tc1hmZUFuz7pG1cbAPpjLMyfHZTuLAQADMzGH5v35an6lNAbx7nw1788hmWBZACBJXpy353O5qaLfnaqiq\/PDnJuTs4flAdBMds\/MLMi9CmEUPpzLMzsHesYEQBMYmwPz17k0j4v\/NvpYLsn7c0DGWjAApf6LY2GXnwhvmstyVWbnDywbgDKYktdlXu4W7130rszPiXmB5QNQT\/pyQM7KD30Vt2euyw9yZva3igDqw6S8LvNynwiviI9kQU707i0A1WbHvCdXZIXQrqAr8i95T3awpACqxosyK1dt9BsarKbrc3Pm5MUWFkAV2C2zcn1t34jbVG\/PnOxleQH0ihk5Kz8TxjX2p\/mIF8kD6C7TM9O\/Ogr618hsn40A6DxTc2ou91lHgZ+NXJ9Z2daCA+gEY3NMFmaVsC3YlflajvZCFADt5CWZk3sEbEN8IPOyj6UHMFq2zqm5yqcdDfTmzPTwIYBWeUUuzHJR2mCfzgV5uYMAYCRMzKm5RYDyuX+LTHEoAGyel2aul65zE5\/M\/OzrcAAYjHF5W64XlhzU6\/LWjHNQADyfqZnl52U5DB\/MHM+LAPgNL8k8H5ZzBK7Kguzt4ADNpi9v9CVdtmR\/rswx6XOIgCYyPqd6FSJH6W05NeMdJqBJTPGJB9vmPZmdqQ4V0ARemDl5XOyxrS7NPG\/0Bcrm93OuH5xlh1yez2ZHhwwokZ0zLyvFHDvq6szP7ztsQEm8SHmwiyWyIHs4dEAJ7JJ5fsWDXXZNFmSGwwfU+zOPL\/j1QPasRP6nz0SAejI9c31gzgp8JrKdwwjUiSmZnaXii5VwWeZ6TgSoB1tkZh4WW6yUj2d2JjmcQJXpy4m5W1yxkv46MzPGIQWqySG5QUyx0t6cwx1UoGrsmYXiibXwKq+CB6rD9MzNasHEGn3Bd35+z8EFes34fCBPiiTWzifyfj+PC\/SSI\/yiB2vsnTnaIQZ6wc5ZIIJYey\/Pbg4z0E0mZY6XI7IQV2RupjjUQHc4wa8JsjB\/leMdbKDT7JRviBsWepm1iwMOdIpxmZWnBA2LdXnmZLyDDrSfA\/MjEcPivTUHO+xAO9kmn8964cJGuD7neXsv0C6Oza\/FChvlgznBwQdGy7TMFydspAuzrQAAWufEPCpI2FgfyalCAGiF7fNNEUJf7s1OwgAYGadmifAgsyGL83aBAAyXF+afxQa5kd\/0eQgwHI7OAwKD\/J3PQ94kHICh2Np3rshBXZCthAQwMIdmkZAgh\/BX+U+CAtiULfIJz5qTw3hO\/R+8LwvYmF1zg2ggh+lNmSE0gGc4MU8IBXIEPuWrvUCylR+mJVtyYbYRIGgyL\/exOTmKj9QPESJoJn05I2uEADkKV2eWKEHz2DoLHX+yDV7qKgvNYn9XV2Tb\/GX2FSpoCqdmuUNPttGVrrLQBCblAsed7IALsqWAQcm8OD9z0MkO+dPsIWRQKsf4jQ+yoz6Z4wUNyqMvs73riuy4\/ZmbPoGDkpji52nJrvl\/srXQQSnMyG0ONdlFf56XCh6UwNFelUh23SV5vfBB3ZmZtQ4z2QPX5a8EEOrLuJzrGJM9dL4fn0I9mZbvOMBkj\/1Xb8pC\/dgjdzi8ZAX8ZfYUSKgTR2Sxg0tWxMdzuFBCXTg1qx1askKuzsmCCXVgVvodWLJi9meOcEK1GZvzHVWyol6QcUIKVWXLXO6QkhX2\/2SyoEIVmZ7vO6Bkxb0p2wkrVI0ZucvhJGvgouwusFAlXpYHHEyyJj7kN9RRHV6Zxx1KskY+kVcJLlSBN2S5A0nWzOU5Rnih1\/xp1jiMZA1dm9MEGHrJe\/1MLVlb+\/PXQgy94kxHkKy5fyvI0AvmOHxkAc4VZug2H3fwyEL8hEBD9+jLOQ4dWZDnp0+woTv18TkHjizML2SMcEOnGZt\/ctjIAr0oYwUcOsmYfNlBIwv1S\/4Vgk5eXn3eISOL\/leICkGH6sOPRZGle6EKQSfq4zyHi2yAX\/SNLPjmFcnWnKdC0E7mOVRkg\/yM0EO7+EcHimyYHxV8aAfvd5jIBvoh4YfR8s70O0pkA+3PTAGI0fCnfu+DbKzr8ydCEK1ynF8bJBvtmrxBEKIVXpuVDhDZcFfkcGGIkbJPnnB4SObJ7CcQMRJ2y0MODslsyIY8kF2FIobLtvmFQ0PyORflhYIRw2FSvu\/AkHyeP8yWwhGbY2y+5bCQ\/B0vzzgBiaHoywUOCskB\/YKIxFDMcUhIDupHhCQG461eW0JyCPtzsqDEQLwqqxwQkkO6MocIS2zKbnnE4SC5WR\/LDIGJjdk6tzkYJIflHdlGaOI3jM9VDgXJYXtNthCceAZf3SU5MucLTiTJLIeB5Ih9r\/DEkVnrKJAcsWvzWgHabHbNYw4CyZZ8PLsL0eYyJT91CEi27K1esdhU+vJ1B4DkqPzn9AnTJuK9VyRHr\/djNZDjvPeKZBtcn2MEarPYJY9bfJJtcUl2E6rNYWJusfQk2+ZPMkmwNoULLTzJturJ9IbwZ5adZNt9p3Atn32zwqqTbLsrc4CALZtpuduik+yIi7zmvWwuseQkO+Y3hGy5vM+Ck+yofy5oy2Qfn36Q7PgnIfsK2\/LYMj+33CQ77u2ZLHBL48sWm2RXvEDglsXbLDXJrnmy0C2HF2eZlSbZNZ\/MHoK3DMblRgtNsqvekLHCtwT87gfJ7num8K0\/B2aNVSbZddfmlQK43kzOnRaZZE9clClCuM583hKT7JnnCeH68kd+tpZkD+3PGwRxPdk2D1lgkj31wUwXxnXkq5aXZM\/9ijCuH8dbXJKV8M0CuV5Mz8PWlmQlfCgvEMp14mJLS7IyLhDKrq9IsjVPEMx1ub7y7SuSVfs2lmusWvC\/LCvJyvlPwrn6HGVRSVbS1wnoajMp\/2FNSVbSX2aikK4ycy0pycr6MSFdXfbx6naSFXZt9hXU1WRMbrCgJCvtDzJGWFeRv7ScJCvve4R19dgpT1pNkpV3aXYQ2FXDu3dJ1kMvNqkYr\/bTUSRrYn8OF9rVYVz+3VKSrI23ZZzgrgpnWEiStfJ9grsa\/F6WWkeStfJJH6VXAy9PJFk\/LxTePj4nydY+Sj9YgPf66fMfWUSStfSH6RPiveQ0S0iytv6pEO8dk3KfFSRZW3+dyYK8V8yxgCRr7ZmCvDfslKetH8lau8zXeXvDly0fydp7gTDvPvtnvdUjWXvX50CB3m2+a\/FIFuFVAr27HG3pSBbj64R69+jLLVaOZDH+yCOF3eMkC0eyKP9YsHeH8Vlk3UgW5Z1+IaQ7vNeykSzOPxPunWfLPGTVSBbnA15r0nk+bNFIFumHBHxn2TqLrRnJIn08Wwn5TnKWJSNZrLOFvH9\/kKR\/g1SM\/2bBSBbtfxX0\/v1Bkv4N4t8fJNlFPyzs28\/ULLFaJBvwb5CtBX67+YjFItkI\/1bgt5fJedRakWyEj2SS0G8nf2WpSDbG\/yL028f43GOlSDbGu72bt32cZqFINsqTBX976MvPrBPJRnl7xgj\/dvBmy0Sycb5R+LeDH1olko3zeuE\/el5jkUg20oMVwGj5ljUi2Ui\/rgBGx25ZZ41INtJ12U0JjIZ5lohkY\/2UEmidrfKkFSLZWJ\/0YsXWOcMCkWy0sxRBa4zNXdaHZKP9VcYqAw8QkmQr\/mdl0ArftTokG+9VymDkvDT9Vodk4+3PngrBF3hJshU\/oxBGxmS\/gE6S2ZANeSKTlcJImGlpSPJZT1cKI+EWK0OSz\/pjpTB8DrUwJLmRBymG4fIl60KSG3mBYhgeW2e5dSHJjXw6WymH4fDnloUkfZDeCn7CliQ31U\/cDoOXWRSSHMC9FMTm+Iw1IckBPFtBDM0WedSakOQAPpzxSmIo3mJJSHIQ36QkhuJyK0KSg3ipkhicF2S1FSHJQVyd6YpiMP7CgpDkEM5UFIPxfetBkkP4PUUxMLv4DUKSHNL+7KosBuJMy0GSm3G2shiIn1kNktyMtyuL3+UAi0GSw3AfhbEpn7QWJDkM5yqMTbnLWpDkMLxbYTyfgywFSQ7T\/ZXGxsy1EiQ5TD+uNDZmkZUgyWF6p9L4LftbCJL0TaxW+Lh1IMkROEdx\/IafWweSHIE\/UxzP4FfQSXKkvlR5JMmHrQJJjtC\/UR5JcqNVIMkRep3ySLbLeqtAkiN0XbZVIKdbBJJswbcrkG9ZA5Jswa83vT4m5ClrQJIt+GS2aHaBHGsJSLJFj2x2gZxnBUiyRc9pdoH4FRCSbNU7mlwfMywASY7CFzW3QP7C+ElyFL7LV3hJkq3Y2K\/yjstS4yfJUbgkY5tZIIcZPkmO0lc0s0D+3uhJcpSe6S28JMlW\/F4T62OrrDV6khyla7Kll5iQJFvxdc0rkLONnSTb4MeaVyA\/MHaSbIP\/1rT6mJI1xk6SbXB1JjerQP7I0EmyTb62WQXyD0ZOkm1yTrMK5PtGTpJt8pom1cekrDZykmyTK5v047avNnCSbKOvbE6BfMi4SbKNzmpOgXzTuEmyjX61OQVyv3GTZBu9pyn1sbNhk2Sb3bEZBXKiUZNkmz2hGQXyaaMmyTZ7djMK5HqjJsk224hXKo7NcqMmyTb7dMaUXyAvM2iS7IAvLb9ATjFmkuyAf1p+gXzGmEmyA36y\/AK51phJsgN+p\/T66MsTxkySHXBJ+soukBmGTJIdcldPoZMkW7Hwp9H\/uxGTZIf8aNkFcqkRk2SH\/GbZBbLIiEmyQ\/685PqYmHVGTJIdcm0mlFsg+xswSXbQfcotkLcbL0l20D8pt0D+0XhJsoN+rNwCucx4SbKDXlJugdxlvCTZQe8stT4m+A4WSXbUddmizALZy3BJssO+uMwCOc5oSbLDHlNmgZxhtCTZYf+yzAI5z2hJssPOK7NArqz9YMphy0zLtOyaPXNojs8788F8Ot\/MLVns+HFIH8\/N+WY+lQ\/mtByXQ7Nnds20TMuWBZ2Ous\/o\/\/oSrwLpFVvnwJyWT+eqPCIumQ3ZkIdzVT6V03JAtm7EGaj7vH5Z4lC2KOBLvE1jhxyXubk+q4VoI78Oenvm59Ts1ri9r\/vk1mRceUN5SQFHqqlsmWNybn4pVBvinZmXo4u6lGpWgWzI7uUN5fUKpPbskfflmqwXscX+m+O7eW+J4dO4AjmivKG8W4EUwvScmqvUSFGuz\/WZlR0sdyEF8o7yhjJHgRTFLvn7\/Fr0FuB9mZMXWeiiCuSs8oZyoQIpjrF5Qy71hrMaX1l9K2\/IWItcXIF8obyhfEeBFMqumZsnxHHNXJb5eYnlLbRA\/l95Q\/mFAimYafnbPCiWa+IDmZ1tLG3BBXJ7eUNZrkAKZ4vMVCKV97HMziTLWniBLCttJC8s4vBhc2yZWXlYTFfUxzOnIc+SN71ANuQFZY1kPwXSGKbmk55dr5yrcrbyaFCB\/GFZIzlGgTSKGVkotCvk5dnDUjaqQF5f1kjeoUAax+u9\/KQS\/iJHWsbGFcjbyxrJbAXSQCZmjsusnro28xr8TqsmF8gHyhrJZxRIQ\/nD\/FCQ98if5EAL2NAC+URZI\/mKAmksY3NW1orzrv\/b40zPmDe4QL5c1ki+o0AazSt8HtJVf5VXWbpGF0hhz6L\/VIE0nK3yJcHeJS\/MFAvX8AL5SVkjeUyBIKdkhXjvsCvzLoumQPJgSQMZU8ivR2C0vDz3CvmOXl0dYMkUSDZkTUkD2aaQ44nRs20hn4dV0X\/NdAumQJ61oGvM3RQInmNszhf2HfCCjLdcCuQ5dy5nIPsrEDyPWX4St632Z46lUiDPc59yBnKEAsEmvMUH6m1zVU6yUApkE19TzkDerEDwOxyep4R\/G3w6r7NMCuR3PL6cgZyuQDAAB2WxAhilT+RQi6RABvAd5QzkAwoEg3w69qgSGIWL8wpLpEAGdFY5A\/moAsEg7F3IQ6a98LHsbYEUyCDOKWcg5ygQDMqBWaoMWrq82t\/yKJBB\/XQ5A5mvQDAEB2eZQhihy\/Nqi6NAhvD8cgbyvxQIhuTIrFIKI3BlXmtpFMiQFvRC939WINgMb02\/Yhj2Y4MnWxgFshkXljOQbysQbJaPqIZh+jeWRYFs1n8pZyDfUyAYBt6RNRy\/aFEUyDD8bjkDuUmBYBiMz9UKYjP+a8ZZFAUyDH9QzkBuVyAYFi\/I3UpiCO\/NtpZEgQzLn5YzkLsVCIbJ\/l6yOMR3r15uQRTIMF1UzkAeUCAYNqeqikE83XIokGF7XzkDeViBYAQsUBYDeKHFUCAjsKBfRX9cgWAETMkvFcYm\/ke2shgKZAQ+Us5AnlAgGBEHZY3S2Mi1OdhSKJAR+Xg5A3lKgWCEzFEbG3mmhVAgI3RpOQN5WoFghIzLLYrjWW\/KWAuhQEbosnIGskqBYMTs6xrr2esrr21XICN3RTkDWatA0AKfUB\/ZkI9bBAXSgmvKGUi\/AkELTMqixtfHnZloERRIC64vZyDrFQha4ujGF8iRlkCBNL1AXGGhVf6l0fVxiQVQIC26upyBrFQgaJEZDf6twtV5sQVQIC26vJyB+BovWufTjS2QuYavQFr2qXIG4kl0tM42WdzI+ngsWxu+AmnZJeUMZLECwSj4cCML5EMGr0BG4aPlDOQRBYJRsGUxGzSS4z\/F4BXIKHyonIE8qEAwKj7YuAKZZegKZFT+upyB3KdAMComF\/ObMsPzfo8PKpBRek85A\/GTthgtH2lUgcw2cAUySv+jnIHcoUAwSqZlWWPq46lsY+AKZJTeVs5AfqRAMGo+15gCOcewFcio\/UE5A7lWgWDU7JZ1jaiPddndsBXIqL26nIFcoUDQBi5rRIF8y6AVSBu8vJyBfEOBoA0c14gCOdagFUgb\/Fo5A\/mSAkEbGJf7i6+PX\/v5WgXSFi8sZyDnKRC0hY8XXyB\/Z8gKpC2eW85APqFA0BZ2K+bXLQd2fV5kyAqkLRb0Luc5CgRt4oaiC+R7BqxA2uRZ5QzkgwoEbWJW0QXyPgNWIG3yjHIG8i4FgjaxQ9YXfIG1owErkDZ5WjkDOUGBoG38W7EFcrXhKpC2eXw5AzlcgcAl1mZ9r+EqkLb56nIG8ocKBG1jz2ILZA\/DVSBt8w\/KGcjvKxC0kf8osj7uNFgF0ka3L2cgkxUI2sh5RRbIZw1WgbTRCSWNZKUCQdt4Y5EFcrTBKpC2uayskTxQxFBuz+z8nvPVc6ZkbXH1sTZTDLbnTMvM3FzEPt1b1mBuK+agr8tVOTFbOGs95cfFFchNhtpTxuZ1WZjVxezTT8oazzWFHfdH89ns59T1jPJ+ndAnIL1jv3w2jxa2T98pa0QXF3lr7UqrV\/xJcbv0x4bqyqqNLihrUJ8u9rv7a3Jp3pTxzmJX2bm4PdrJULvK+Lwpl2ZNsbn0ibLG9cFiB\/WMSzI\/hzmVXeSRovbnIQPtIi\/L3DxceCK9v6yRnVz4uFxpdZuri9qcKw3UlVVbfVtZgzuiEUPzLa3ucU5RW\/MpA+0wpX3LanO+pqzx7dWYwbnS6g6nF7Uv7zBQV1Zt9cVljXCbho3vN9\/FnpUXOr8d4aCiNuUAA+0IL8ys\/KSR2bN1aaNc3sgxutLq3G224w5XVgP7dHkDvauho\/TgYad4opjteNww20qJDwaOzEXlDfWaRg\/Ut7TaTzlXEzcbZtv+XdqUb1k16jn0JPknY3Wl1VYuKWYnvmGYrqza6gXlDfjvjNWVVlv5jC\/xwpXVgJ5Z3pDfYayutNrK7GL24IOG6cqqrZ5c3qgPN1ZXWm3lXcXswGmG6cqqrb6qvIHvYqwePGwr\/7mY6R9nmCOiiQ8GjswdS\/wvhjUG60qrjRxWzNwPMUxXVm10ZcaUOPy7jHYYej38cCnn9Th7GuZmKf316+30zjJX4LtG60qrjbyomGn7LRBXVu30\/5W5Bl802hHqXVpD8XvFzHk7wxyE5r7LajSeX+YyfMhofUurrffhpcx4qmEO8Jmpb1m16qwyV+J4o3Wl1UYmFzPdSYbpyqqNHl3mWrzEaH1Lq63\/jVrKXMca5nP\/qvQtq9G7a5nLMc4\/SF1ptZVSZgpXVu38Em+x\/0Fyh\/G60lIgCsSVVQf993IX5VvG60pLgSgQV1YddGG56\/KPxutKS4EoEFdWHfSj5S7NacbrSkuBNL5AXFl10pPLXZyDjdeDhwqkwQXiwcDO+\/Jy12er9BuwKy0F0sACcWXVHddny5LXyAsVXWkpkKYViCur7vmLslfpEiP2LS0F0pgC8S2rbvv1sgtkjhG70lIgDSgQV1a98SNlF8gJRuxKS4EUXiCurHrnG8sukD2M2JWWAim2QFxZ9doXlV0gfXnSkF1pKZDiCsSVVRV8In2lfyPj+8bsSkuBFFUgrqyq4rXFf6Mv5xuzKy0FUkiBuLKqlvPKL5CZxlwp1+TSvCnjFYgCGRHj86ZcmjVOUKU8vfwC2c+YXWkpkFoXiCurqrp3+QUyNk8btCstBVLLAnFlVWWXNeO3La8zat\/SUiA1KxDfsqq+16QRfMqoXWkpkBoViCurenh2MwrkbUbtSkuB1KJAXFnVybc0o0B2NWpXWgqk4gXiyqp+7pyG4J\/DrrQUSHULxJVVHX0ojeFy43alpUAqWCCurOrrpc0pkLOMu7ZW6cFDBdI+PBhYdz\/SnAJ5rXG70lIglSkQV1YleHhzCmSSD+dcaSmQChSIK6tSXJ3JaRA3GLlvaSmQHhaIb1mV5XVpFGcbuSstBdKjAnFlVZ7\/0KwCeaORu9JSIF0vEFdWpXpMswpkWtYbuistBdK1AnFlVfapm5qG8e\/G7kpLgXSlQFxZle7NaRyfM3ZXWgqkwwXiyqoZntO8AnmrsTfCTj54qEA8GMgN2ZA3N69Atku\/wbvSUiAdKBBXVs1yfbZNA7nV6F1pKZC2Fogrqyb6ozSSTxq9b2kpkDYViG9ZNdd\/aGaBHGX0rrQUSBsKxJVVs31tMwtkYlYYvistBeLKiqNweSakoVxl\/K60RnGl1eQCcWXFZ\/x2GsvfGD9HcaXV1AJxZcXfekZzC2R\/4+corrSaVyCurLip+zS3QPryiAVgy1daTSoQV1YcyIfTlwbzFSvAAXw0n81+CuRZ9stn86it4AB+OY3mT6wAW77SKr9AXFlxaP+42QUy1dt62PKVVskF4sqKm3dN817jvinftQZs8VtapRaIb1lxeP5rGs8Z1oAtXmmVVyCurDgS\/0qB7GEN2OKVVkkF4sqKI3eGAkl+YRHY0re0Svl\/41tWbO1f5EjyKatAkiP0H5VHkvwnq0CSI\/RVyiNJxuVxy0CSI7rGHas8nuFC60CSI\/DziuM3HGMdSHIEvl5x\/IbxWWwhSHKYPp5xiuO3fMlKkOQw\/aLS2Jg3WgmSHKZHK42N2SJLLAVJDsMnWvwR6IJZYC1Ichj+k8LYlOOtBUkOwzcojE2ZmKUWgyQ3e4E1QWF4nJAkR+4XlMVAvNZqkORmfI2yGIgxuc9ykOQQ3pMxymJg5loPkhzCjyuKwfgD60GSQ\/gyRTE4t1oQkhzEm5XEUHzQipDkIL5fSQzFjllnSUhyANdmeyUxNFdaE5IcwCsUxOZ4mzUhyQF8s4LY\/Ht5H7UoJLmJD2e8gtg8n7YqJLmJZyuH4fDS9FsWknyeeymH4fF9y0KSG\/k9xTBc3mldSHIjT1EMw2VSnrAwJPmsSzNZMQyfz1sZknzW\/6EURsL+VoYkn3VfpTAyrrc0JOkD9FY4ydqQZDbkrQphpIzP\/RaHZON9wBPorTDH6pBsvGcpg1bYIastD8lGu9or3Fvlq9aHZKP9iiJolVdZH5KN9mBF0Do3WyCSjfXHSmA0nGqFSDbWtyuB0X2Z915LRLKR\/toXeEfLh6wRyUZ6hgIYLVtnqUUi2TifzFQFMHo+aZVINk4\/YdsWdvJAIcmGuSY7C\/\/28GXrRLJRfknwt4t90m+hSDZIvwDSRr5toUg2xm8L\/XZyqJUi2RhfLfTby7WWimQj\/K7AbzdHWiuSjfC1Ar\/9XGexSBbvDcK+ExxjtUgW71HCvjPcZLlIFu0PBH2nON56kSzaNwj6TtHnB6ZIFuyP0yfoO8cJVoxksR4v5DvLDywZySK9yb8\/Os1R1oxkkR4h4DvP1RaNZHFeJdy7wSu8m5dkcR4s3LvDZZaNZFF+S7B3i32y3sKRLMb1fv+jm1xs5UgW4wKh3k1mZI2lI1mEq7OHUO8u51o7kkV4jkDvNtOy2OKRrL1LMl2gd58PWj2StfevhXkv2CKLLB\/JWntXJgjz3vBW60ey1r5ZkPeO6y0gydp6g9cn9pJXeq0JyZran0OFeG\/5mjUkWUu\/IsB7zc552iKSrJ3Ls4sA7z1nWUWStfO\/Cu8qMCF3WkaStXKRr+9WhT+yjiRr5bGCuzpcbiFJ1ka\/\/lEpds9KS0myFq7IbkK7WnzUWpKshX8nsKvGpNxtMUlW3nszWWBXj+OtJsnK+0ZhXU2+aTlJVtqvC+qqsn2WWFCSlXVpdhTU1eU9VpRkZX23kK4yY3KdJSVZSb\/n5e1VZ8+ssqgkK+eq7CWgq8\/fW1WSlfMs4VwHJuR2y0qyUv7cyxPrwmFZb2FJVsZ1OUQw14fPWFmSlfFsoewaiyRbub6aKJTrxcFZZ3FJ9ty1eYVArh9nW12SPffjwrie11i3WV6SPfV211d1Zf+sscAke3h99XJBXF8+boVJ9sw5QrjObJEfW2KSPfHmjBfC9WavLLfIJLvu03mpAK4\/XvFOsvt6dXshXGqZSXbVSwRvKWybBy00ya55f6YL3nJ4ffotNcmuuD5HCt2y8HpFkt3RqxOLY0J+YrFJdtxbsoXALY8X50nLTbKjLvPl3VJ5m\/Um2VFPFrTl8nkLTrJj\/g8hW\/YnIbdYcpId8dZMErJls0eWWnSSbfeJ7C5gy+d4z4SQbLP9ebNwbQbnWneSbfUcwdoUtsgPLTzJtnmjZz+axA55wNKTbIsP5\/eFarM4NKstPslRuyavEajN431Wn+SofY8wbSYXWH6So\/LLgrSpTPRhOslRfXg+QZD6MJ0kR+pD2UmI+jDdQSA58g\/PXy1A8eeOAskR+y7hiSQ5x2EgOSI\/ITjxDGNyqQNBcthekbGCE79hSm51KEgOyx9nitDExuyY+x0Mkpv1wewsMLEpB+Zph4PkkK7IK4QlBuItWe+AkBzU\/rxNUGIw\/tYRITmoHxKSGIp5DgnJAT1fQGJzX+n9hoNC8ne81Fd3sXkm5t8cFpLP89pMFI4YDlPz7w4Myee8LdMEI4bLTrnXoSGZDdmQX3vyAyPjZVni4JDM0uwjEDFSDs9Kh4dsuMvzKmGIVjgqqxwgssGuyRsEIVrlzVnrEJENdZ3nzjE63pF+B4lsoP15twDEaPlLR4lsoGcIP7SD\/+YwkQ3zw4IP7eJsB4pskOcIPbSPvvxPh4psiOelT+ihvRVynoNFNsAvZozAgwohOVK\/oD6gQkiqD6gQkuoDKoSk+oAKIVlUffjmFbpUIb7US5bk59QHulkhn3DoyEKcK9LQbWY7eKT6AFrjL7Le8SNrbH\/eL8jQK97u90LI2roupwsx9JI3+dVCspauzokCDL3mmKxwGMmauSpvEl6oAq\/OEgeSrJFLcpjgQlX4g9zrUJI18YH8odBCldgxP3EwyRr4s+wssFA1tsm1DidZca\/JNsIKVWRCvuaAkhX2kkwSVKgqffmkQ0pW1HO9bxdVZ3b6HVWyYq7P3wgn1IG3ZLkDS1bIlTlJMKEu7Jf7HFqyIj6Yg4QS6sSOudnBJSvgT\/MigYS6sWUudXjJHvvtbC2MUEf6MscBJnvovIwVRKgvf5Y1jjHZA9dkpgBC3TksjzjMZJddnCOFD0pg59ziQJNd9BfZU\/CgFKbkEoea7JJXZKrQQVkfqHtCney8\/ZnrhSUokRM9oU521JU5RdCgVPbNPQ452SEf8MQ5ymZbvxpCdsQbs4OAQelMyEUOO9lmL\/ZLH2gKM7PWkSfb5LrMFipoEkdliYNPtsGncpxAQdOYkTscfnKULspewgRNZKtcJgDIUXhlpgkSNJWxmSsEyBadn3FCBM3mpKwQBeQIXZXThQeQHJKHBAI5Ah\/L4YIDeIadcpNQIIfprdlFaAC\/ZWIWCAZyGC7MZIEBPJ++zM568UAOoXftAoNybJYKCXIQl+UEIQEMzt65W1CQA3hfDhAQwNBMz9XCgtzE67KdcAA2zziPF5KbPDK4hWAAhsvMrBEbZDZkrXftAiPlsDwiPNh4F+dIYQCMnJ1ziwBho\/1F9hQEQGtMySVChI31ikwVAkDr9GV2+kUJPTIIoBVOzHKBwka5Mqc4+EB72Df3CBU2xgdykEMPtI9tc61gYSO8MTs48EB7mZCLhAuL9+JMctiBTjAza0UMi3WdRwaBTnJUlggaFulTOc4BBzrLjNwhbFici7KXww10nq1ymcBhUV6ZaQ420B3Gel8vC3J+xjnUQDc5KStED2vvqpzuMAPd55A8JIBYax\/L4Q4y0Bt2yk1CiLX11uziEAO9Y2IWCCLW0oWZ7AADvaUvs7NeHLFW9mdu+hxeoAocm6VCibVxWU5waIHqsHfuFkyshfflAAcWqBbTc7VwYuW9Lts5rED1GOfxQlb+kcEtHFSgqszMGjHFSrrWu3aBqnNYHhFWrJyLc4TDCVSfnXOLwGKlvC27O5hAPZiSS4QWK+MVmepQAvWhL7PTL7pYiUcGxziQQN04McsFGHvqypziIAL1ZN\/cI8TYMx\/IQQ4hUF+2zbWCjD3xxuzgAAL1ZkIuEmbsuhdnksMHlMDMrBVp7JrrPDIIlMRRWSLY2BWfynEOHFAWM3KHcGPHXZS9HDagPLbKZQKOHfXKTHPQgDIZ63297KDzM84hA0rmpKwQdWy7q3K6wwWUzyF5SOCxrT6Wwx0soBnslJuEHtvmrdnFoQKaw8QsEHxsiwsz2YECmkVfZme9+OOo7M\/c9DlMQBM5NkuFIFt2WU5wiIDmsnfuFoRsyftygAMENJvpuVoYcsRel+0cHgDjPF7IET8yuIWDA+AZZmaNWOSwXOtduwCez2F5RDhysy7OEQ4LgE3ZObcISA7pbdndQQEwEFNyiZDkoF6RqQ4JgMHoy+z0i0oO+MjgGAcEwNCcmOUCk89zZU5xMAAMh\/1yj9Dkc96fgxwKAMNl21wrOJkN2ZAbs70DAWAkTMhFwpO5OJMcBgAjZ2bWitAGu84jgwBa56gsEaQN9akc5wAAGA0zcocwbaCLspflBzBatsplArVhXplpFh9AOxjrfb0Ne9fuOEsPoH2clBWitQGuyumWHUC7OSQPCdjCfSyHW3QAnWCn3CRkC\/bW7GLJAXSKiVkgaAt1YSZbcACdpC+zs17cFviu3T7LDaDzHJulQrcgl+UESw2gW+yduwVvId6XAyw0gG4yPVcL3wK8LttZZgDdZpzHCwt4ZHALiwygN8zMGjFcU9d61y6A3nJYHhHGNXRxjrC8AHrNzrlFINfM27K7xQVQBabkEqFcI6\/IVEsLoCr0ZXb6RXNNHhkcY2EBVIsTs1xAV9yVOcWiAqgi++UeIV1h789BlhRAVdk21wrqinpjtregAKrMhFwkrCvoxZlkOQFUn5lZK7Ir5DqPDAKoD0dlieCuiE\/lOAsJoE7MyB3CuwIuyl6WEUDd2CqXCfAee2WmWUQAdWSs9\/X2+F274ywhgPpyUlaI8h64KqdbPgB155A8JNC77GM53OIBKIGdcpNQ76K3ZhdLB6AUJmaBYO+SCzPZwgEoib7Mznrx3oV37fZZNgDlcWyWCvkOuiwnWDIApbJ37hb0HfK+HGDBAJTM9Fwt7DvgddnOcgEonXEeL+zAI4PjLRaAZjAza8R+m1zrXbsAmsVheUT4t8HFOcIyAWgaO+cWBTBKb8vuFglAE5mSS5TAKLwiUy0RgKbSl9npVwUtPjI4xgIBaDYnZrlCGKErc4rFAYBkv9yjFEbg\/TnI0gDAM2ybaxXDML0x21sYAPgtE3KRchiGF2eSZQGATZmZtSpiCNd5ZBAABuOoLFEUg\/hUjrMgADA4M3KHshjARdnLcgDA0GyVyxTGJl6ZaRYDADbPWO\/r3eRdu+MsBQAMl5OyQnVkQ1bldMsAACPjkDzU+Pp4LIdbBAAYOTvlpkbXx63ZxRIAQGtMzILG1sfCTLYAANA6fZmd9Y18126f4QPAaDk2SxtVH8tygqEDQHvYO3c3pj7uywEGDgDtY3qubkR9XJftDBsA2su4BjxeOD\/jDRoAOsHMrCm2PNZ61y4AdJLD8kiR9fF4jjBcAOgsO+eW4urjtuxmsADQeabkkqLq44pMNVQA6A59mZ3+Yh4ZHGOgANBNTszy2tfHypxikADQffbLPbWuj\/tzkCECQG\/YNtfWtj5uyPYGCAC9Y0IuqmV9XJxJhgcAvWZm1taqPNZ5ZBAAqsJRWVKb+ngqxxkYAFSHGbmjFvWxKHsZFgBUi61yWeXr48pMMygAqB5jK\/6+3vkZZ0gAUFVOyopKlseqnG44AFBtDslDlauPx3K4wQBA9dkpN1WqPm7NLoYCAPVgYhZUpj4WZrKBAEB96MvsrK\/Eu3b7DAMA6saxWdrT+liWEwwBAOrJ3rm7Z\/VxXw4wAACoL9NzdU\/q47ps548PAPVmXA8eL5yf8f7wAFACM7Oma+Wx1rt2AaAkDssjXamPx3OEPzYAlMXOuaXj9XFbdvOHBoDymJJLOlofV2SqPzIAlElfZqe\/Y48MjvEHBoCSOTHL214fK3OKPywAlM9+uaet9XF\/DvJHBYBmsG2ubVt93JDt\/UEBoDlMyEVtqY+LM8kfEwCaxsysHVV5rPPIIAA0laOypOX6eCrH+QMCQHOZkTtaqo9F2csfDwCazVa5bMT1cWWm+cMBAMaO8H298zPOHw0A8AwnZcWwymNV3umPBQDYmEPy0Gbr47Ec7g8FANiUnXLTkPXxk+zijwQAGIiJWTBofSzMZH8gAMBg9GV21g\/4rt0+fxwAwNAcm6XPq49lOcEfBQAwHPbO3c\/Vx305wB8EADBcpufqbMiGXJft\/DEAACNhfM7P+RnvDwEMzP8HKEoO9N9smf4AAAAASUVORK5CYII=", "filesImagesAlien3" : "image\/jpeg:base64:\/9j\/4AAQSkZJRgABAQAAAQABAAD\/\/gAfQ29tcHJlc3NlZCBieSBqcGVnLXJlY29tcHJlc3P\/2wCEAAMDAwMDAwQEBAQFBQUFBQcHBgYHBwsICQgJCAsRCwwLCwwLEQ8SDw4PEg8bFRMTFRsfGhkaHyYiIiYwLTA+PlQBAwMDAwMDBAQEBAUFBQUFBwcGBgcHCwgJCAkICxELDAsLDAsRDxIPDg8SDxsVExMVGx8aGRofJiIiJjAtMD4+VP\/CABEIAyADIAMBIgACEQEDEQH\/xAAeAAEAAgICAwEAAAAAAAAAAAAACAkHCgQGAQIDBf\/aAAgBAQAAAAC1MAAAAAAeMMR9wlh7F\/QOofgfn+ef+\/2\/v+T8t5zkDmj2AAAAAAAAAAAAHjBsPYlRk64AADscmZaTEzn5AAAAAAAAAAAGGYBQWxGAAAAyzOif2aQAAAAAAAAAA+cG63IxAAAAAJN2Szi+wAAAAAAAAAOLXpV7i4AAAAAGULQrEPsAAAAAAAAA8QYqVxSAAAAAAMp20zr8gAAAAAAABhOmKMAAAAAAACT1zmbgAAAAAAAHrVjVtwAAAAAAABzrS7TfYAAAAAAAMW0exrAAAAAAAASTvHycAAAAAAARBpC6kAAAAAAAAO2Xey\/AAAAAAB4rQqN+IAAAAAAAAPtbpZcAAAAAAPWoOtUAAAAAAAAAWVW+ewAAAAAHrS1X8AAAAAAAAACwO6L3AAAAAD1per6AAAAAAAAAAWDXQ+QAAAABUTWaAAAAAAAAAALMbdwAAAACt2nkAAAAAAAAAAFw9kIAAAAEUKD+MAAAAAAAAAADk34SvAAAADH2ub0oAAAAAAAAAAB3XY07+AAAAeuv\/FkAAAAAAAAAAASov+9gAAAFZ1RAAAAAAAAAAAALeLMAAAAMTa5H44AAAAAAAAAAAP2NjzKgAAAKG4dgAAAAAAAAAAAJiXyAAABEKhQAAAAAAAAAAAAX1y9AAAPGuhgcAAAAAAAAAAAAZ52LvIAACD1HAAAAAAAAAAAAALyZvgAAeuuxgAAAAAAAAAAAAAEgdiX2AABFagAAAAAAAAAAAAABsAynAABRvB8AAAAAAAAAAAABOG8cAAOp6yPBAAAAAAAAAAAAAc\/Zs7aAAK6qbAAAAAAAAAAAAABcpYoAAKBYogAAAAAAAAAAAACV1\/QAB1vWF4oAAAAAAAAAAAAA5ez72AABCmi0AAAAAAAAAAAAAF6M1wAFPVbgAAAAAAAAAAAAALJbgwAGu9HgAAAAAAAAAAAAAEiNiAADj6tvBAAAAAAAAAAAAABz9pP7AAwfrjgAAAAAAAAAAAAANjrNwAITUYABlewUAHw6th6NPSgAAAAd0ktmLtP3ABX1igAL0ZrgArOqIACV1\/QAB6xkrhgz8AAAAfaclj8nfYAAoFiiAFvFmAAKhK0gAldf0AADBFLEdQAABIm6jO4AAKBYogBZfbyACkyBQASuv6AAA9KZK9QAAFhFz3sAABQLFEAJ73YgAolhiAErr+gAAHrTlXMAACxm4z3AAAUCxRACZ97IAKDokABK6\/oAAA9KC4nAACWF+3sAAAUCxRACXF+AAKB4ngBK6\/oAAAY51ufwwAH7myNkYAAAUCxRACWF\/AAKDokABK6\/oAAAKnKugAFo9sIAAAUCxRACXF+AAKJoYABK6\/oAAAOh6zfHABydmTvYAAAUCxRACaF7AAKTYEgBJ\/YI8gAABQDFUAEq7\/QAAA8a\/MXgAnxdgACoatAAGa7A5+99AAAKraqQAWr2ogAAHRIA1\/YRABZhbwACs6ogAByJc2CTa5IAAIN0dgAvGnEAADjQmr7iLxwAFvFmAAITUYAAB2qd1gcjPIABFSgIAF\/crQADxHivmCXUgAAvRmuADCGuMAAAzVYJP3voAEYdfUAGwVJ4ADo0AK\/cIgAANjfOIAPlq1cEAAA+8uLBZs8sARh19QAbBUngBw4VV+RD44AABztpX6gAa7seQAAAdqnZYJI3yBGHX1ABsFSeA8R2r7gl1IAAAEh9iEABTzW6AAAAZrsCn930Iw6+oANgqTwdEgJX3hEAAAAsiuGAAQjozAAAAD7y6sDm3yUYdfUAGwVJ5xoT1+RD44AAAAXmTcAAdW1ieMAAAAB2qd1gX5uvuADYI\/Wr7gl1IAAAADk7PPZgADX9iyAAAAAO8dHAB3rooAAAAAlPsAgACuCnYAAAAAAAAAAAAAXFWPAADpmspxQAAAAAAAAAAAAHK2bO5AABRTC0AAAAAAAAAAAABNK9YAAERqEAAAAAAAAAAAAAF+EuAAAeuuPhEAAAAAAAAAAAAGbtjj2AAAgRScAAAAAAAAAAAAC6+fIAAD11u8OgAAAAAAAAAAADMWyD9QAACEFGwAAAAAAAAAAAAvKm8AAAPTX5jAAAAAAAAAAAAAk\/sDe4AAAYK12OGAAAAAAAAAAADmbEudwAAAKn6twAAAAAAAAAAAWkWwAAAAPhruYBAAAAAAAAAAAGf9iHkAAAAGG9dn8EAAAAAAAAAAB+9sRZoAAAACDFH\/gAAAAAAAAAAHm7ydYAAAAFTFXwAAAAAAAAAALP7aQAAAAHpSLBQAAAAAAAAAATqu98gAAAAHyoeh6AAAAAAAAAAmBe\/wAkAAAAAOHRDEEAAAAAAAAAEvL4OYAAAAABxaPoRAAAAAAAAACbl432AAAAAAPFQNawAAAAAAAACye4DyAAAAAACvGnLhAAAAAAAADm3D2KAAAAAAAEX6LekgAAAAAAAO6XnylAAAAAAADGdDmBgAAAAAAAZ3vdyoAAAAAAAB+XSHCcAAAAAAATUu6\/aAAAAAAAAPWqirL1AAAAAAB7WlWt+QAAAAAAAAQgpQ\/FAAAAAAH7N1c5AAAAAAAAADAlEGMQAAAAAGS74M+gAAAAAAAAB0qiiMYAAAAAJNXqd6AAAAAAAAAA+NMVfQAAAAAn\/c\/ygAAAAAAAAADxWzUNxwAAAAci3SyvyAAAAAAAAAACJdG3VAAAAB2q8WXIAAAAAAAAAABiShzCoAAADM98WXwAAAAAAAAAAA\/Do2h4AAACYF4vYwAAAAAAAAAAAPWo+skAAAWZ25+wAAAAAAAAAAAAgTTB+YAAD9K5ufYAAAAAAAAAAAAEcqJcfAADIF6skwAAAAAAAAAAAADoFEEdQAEh74MggAAAAAAAAAAAABw6UYIgAnXdb+gAAAAAAAAAAAAAB61g1OfIB9LXbRPcAAAAAAAAAAAAAAQ1pD66DsN3czAAAAAAAAAAAAAAAMKUPYjGWb4M3gAAAAAAAAAAAAAAHWKLYoJWXpdqAAAAAAAAAAAAAAAD507+txX0AAAAAAAB\/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP\/aAAgBAhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP\/aAAgBAxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP\/8QANBAAAAYCAAMGBQQCAgMAAAAAAQMEBQYHAggJMEAAERITUGAUFRk4ZxYXIUEQICMzJCUx\/9oACAEBAAEMAPSFStKhIzPUnlEEyXZugYgGYOU\/ZBzf+IlRjSOeDYhkTxm+8TRdkOWDFXhJYPXERvlyyzBEmjjZg67m7Kuwj5k6UEYuOwl6ugj8TY8r7K7FsFwERVyx+Udj3t5Vf97isN7ZGm5h3ZZ5ZBiabgHdjnliBD28pf8AocVhXZJYtgt4gKSWPyfs3bCXq1CHw1jyrs1bnbKtIh5c6UH4s\/ERvtuEPjE8bcwYOJi5kiGD5XqU4I1xFKPdPBg6oJCzZxfaCgJgOGDZP2UM0ipKtIwUJjyjyvZgiGICIiABOdn6IroDMHeaNxymbcSxiI8ZMMhatWMu3m2IlQGFkPyZiTyGYS2XKBUP765ux3RR6YS2JKPiGB+dGk6G7ybEREMCzX9O+EQjiWMx4FkzSFqkuUC2hoawhKKaJm3lKwEMgAQEBD2NZeydMVQBpUhlCUV1jcSZ5VAalgMVKRYT6+bfs4cwlEvc1pHVQC9LcrDPD9LS1zQkV1xJnxH5SWexQhaXWuy1LWt5REflCTFd7BcXJuZUJ69xWJ0SS1d\/6mhYHIokSdLHK0dvbwtQDkyt+FnbRHv68BEB7w7Vft3eNW+QnSP+bu21RxAqpmgkIZaQdE3Bscmx3QEL29YnWJPXHZ4aI82nubuvSoEVw8Q+Hx0D2yuG75+tsq6bNtxd8VLpCrX4eiVrc9mVGvBXEpCrQY05xEIlIBTtljN3yFY0PrNIWtO6M7glcEXrBhhZJeZhmeOGF3b615APiWmF4Fyl5tG7LLuNy+Mlr6erw9IrC6bKp5z+NiT6oRY0fvrX09FM0TQsqLPBZhZxeBheeOeHqt17IVlRiERf1\/xDreO2NoXYaeiPVizR\/wBMo3bC0KQOJRplQvDBSeyNZ3mgAWJf8M7epOLi3tCBQvcFRCRJsJv6IZKo9VHZ0dXN7cFLi5rFC1Z6c2OjkyuCZwbVZ6NZrzv+ICljtsdm9zbndAmcG9WQrSen27dcApGOi7yhcGGd+7QWDfK8SVxvyxg9SoPaCwqGX4lIDRc2CnrtgF3R0HaLrgyz9N2X3AitJkKGJk8h5ls3nUtseRKpBJ3VQ5OPqkJnMrrqRJJBGHQ9tcdZ9wYvdhJDC+AQzS30vabd0tgFbC6yWYGuKlSpWqDlKk4w4\/1ZMpUI1BShOcYSdqtvAS+iihdnLcCV\/pBxxKYkw44zAorbXdI+Witg9crcyWb1rUvdI+JCig9jLczmUg8lSSWeQZgaV6KpUpkSY5SpOLJI232+VWYcrhcJUmERf1zUbcFXWJ6SFzZSYoiyVSmWpiVKY4s8j0M00ogrM03PEsvcHbY+yFiqDwpZkXF\/XtPNuD60VpYPNFeRkXKNKPKwNKzxML9C3V2zGTHra3g67\/1PsDSvbMYyeirecLh+U+g7ubThFkiytIWt7nb2FpDtP+qE6StZks73br9uNk01GREG5oNLMlaxYrcVihYrPMUKPYSNYrbladYjPMTqdR9kkt5xH5c7mlFyvrbftSOUvAXOVvWffhYk\/kloTF0lMhUieu9iV3P5JV8wa5TH1IkLqftWOXNA22Vsmfdh1alUmRJjlKk4skja7YBVeU+zBAdnjGvY2p+waqi5\/h8ecZlGUypMtTEqUxxZxHVb+bB\/J279qo8r7lnsjQHYT5sg\/auQq+9X1N6220UjW7pKlwYGKZA\/u8pfHF7d1Wapf7Ijz+7xV9bntoVZpV9D260XbW7XKUIYFKeo3PvYbgswxua1PjjvsrS+9hqCzSm10U+CO9Pu1d41PVpjQ2KfA\/8AszSa7xtirimlzUjm\/wDSqFBCROaoUGYFE7I3Apuu13iQhnn8t9ma23AopS12eQ5Z5\/LE6ghUnKUEGYmldJvtcIwCrMIo3H+B19naEXEM+q0Yo4n+N16PLLHDEcshAA2ctnO47hfX0k4c2z2drFbOdOXCxPpxw4NmOWOeIZYiAh0W6Vp\/thSLoUkO8Dr7Q0stXKzqSaylh\/mOnRb72eM5ucxgSneNu9oaD2eMHufCPqjvA39DZk1RVrXsklarwjg6Oa56c1rkuOyPV+0GpzXMrmickJ2RCutJwisWvI3LEgYgX0HEfsQWiBx6EJje472lw4bEF3gUhhCk3vO6DdOfjPdgZH5RvjR+0tK5+MD2AjgGm+BHz7AlSSBwWRyVR4RLXLVTmuUrVRuRqj2kgXK2tclXJDcilFfytLOoPHJMm7gK53EEmoxqiRZyTO4\/2pw\/5sMkofBoNz7z+dxJpgLjYkUixefeX7U4bMwFvsKWRYzPuL5218rGY7CzxcGfjL9qanysYdsLA1w5+ArmvTmmZGZwdFP8JnRxUvDmtcVOXiP9qNTkpZnRE4pcvCe0O6V6Z290TD3k8zaaRfpjXqw1wZ+Afa2rUh\/U+vderxz8Y8ziGPgNdAAhDL+eXR9YfvLaDFCPmvyrt9MT8p9vpiflPt9MT8p9vpiflPt9MT8p9vpiflPt9MT8p9vpiflPt9MT8p9vpiflPt9MT8p9vpiflPsbwxjw\/wCm0MM+znw0Z+ViPyybsSkZHoXsYwhnkmZm55wl1bz+BHeVJ4w7tA9dE65n87M8EZjDw8do1oZsW\/4YZqWZuZsGzhoz83EPmc3YkwlcMY\/\/AOnWgXh2+mJ+U+30xPyn2+mJ+U+30xPyn2+mJ+U+30xPyn2+mJ+U+30xPyn2+mJ+U+30xPyn2+mJ+U+30xPyn2vCsP2atB9hHzX5rzOHo+fNdf8A4Icv55nEzd\/KjdetPM0g+6GCdAoTJ1ZBhCgos4qydMaGsbA00GDFgX25oNa8DwPXxfPCWtilKpRKDUykkwg\/qE6dQrPKTpyjDjqi0ItefYJ3GTZlxNrrnTCha5wJPFgB\/Xp0ydIQWQnKwJK5+7\/3QzvmcMx2A6NWC0CPN4lziBk6gzb\/AFy9IPuhgnR3XrPVt2ozM3tuBG8XvrLYtDrxydSPmDJ01FazWLfC\/wAbUnBvZKW1nq2jUZZjO3Ateei3f+6Gd8zhouPgnM5be\/m8RdaKq+UBP9cvSD7oYJ0jy0tL61qmx1RkLUW1ely6tcFkxgZJ62NdJqppevssEcxnZJyKMtDQ1MDYlbGpEnRIej3f+6Gd8zhzrvhb4cSf65m\/KgTtj3nDmaQfdDBOlzwwMwywzxDLHc3UoICcqn8IRj8g6LTLUoJ+cmn83Rj+nsMMC8McMMQxx6Td\/wC6Gd8zQZQJOx7Phzd5jBz2cmIczSD7oYJ0y1GjcEahGsIKUJttddz6KnXmthRmcX6DUvXc+9Zz5rmUbhF0aJI3I06NGQWnTdLu\/wDdDO+ZowYOGzcPDm7w\/dBOeZpB90ME6e6KoZLmrt2iTmGOHaTRx4iEhc2B4TZJnDnRmNvEwkLYwM6bJS4UzVbHS1dtMTbAxzHpt3\/uhnfM0e+5+D83efAcNm5iPM0g+6GCdRxGKewQubRZrYR3Yc7hz09gucneznMjvw6fd\/7oZ3zNGCxz2bh483flP5Ox7xnzNIPuhgnUW\/AElq1pJoieGHZYkUoFZ6RUVkUfzEaNS4LE6RKVkafUUASVVWUaiSYMBHp93\/uhnfM0FT+dsc0Z83iLoPhL3bjv65emTo2M2ysHWOS1MiTAIZAAgICHT7owXGC7CSbAkoC0vM0tguM62EjOJxQGJenEQABER7g3Ocm532WnKtvWJ1ifl8OZCKq93I\/+uZxL2zyp1BnLm1tsTcdTiWXGZSsKR1rxJGo\/yEdgxbNJlX9yVfaScDYlKG5zz6XiYxfEFEBk5eH88zhmxfETp9JzMP56Wf29WVWpROlknbmsbK4kLOj81JXsYMWmWTsZctr5HYSOVLc0XM4aDZ5s5nTlzeJm0CdG69d\/65yRWrQKSlKQ80g+tN4L2r4Ckyx1LkyCtOIJTsv8pNJyVkTXMUhYJQ3FOTG6InNF0XEQZsV9CJFn98zh3tGDbQalaIfz0Ty+sUYbTXF6c0bajsjf+nIZ56WNlrJYusneG9J8ByZC6FxluVq1a9SapVnmnn87hmNAkxqwXfm8QtkF1oAFoY\/z0MSnMygTkDjGH1waFVa8RexWESUs3Z0ciS1tt9RNneUnRyPBocAEMgAQEBDn70kAdrLLc\/75mjBWCfWSI5\/3zxEAAREe4LL25oqsPOIXSQp0cLL4jFhv3mpIOzJI6nl89mk\/cRcJQ\/uLwp6Hh6sgtWvwLBw5u08fGS682EiDDxj0lbbD3FVGRWEalS0pHWnEkbFIkJLDjGaTOv7lq+0k4GxKUNznnzd3\/tenfN0g+16Cc6f3BWVWpRPlsnbmsbI4kTOkA5JX0XNWmWTsbctriaXI5UszRdJq4xfpnXqvUHg8GXMeGlK9tDg1qQ7yXNvUtLksb1OPhP6VKqVIVBSlKeYQfWu797V8JKdW7YSVBWnEEp6WgQkkxCyKLWOQMEkbCnJjc0Tkj5W7\/wBr075ukH2vQTlvb8wxZtNcXpzRNqOyd\/6bhvnJY0Wsla6y9471n\/nJkLmVGECtYrcFRqpWoNUH9K1tyl3c0Temx8R7S2JmNnb20gO4jm7XRX9H7CTxABfgL6iJTmZQJyBxjD64NCqteIvY7CJKWbtCORpq129oizRJJSSPBpcAEMgAQEBD\/fd\/7Xp3zdIPtegnIEQABER7gsrbiiax85OukZbovsniMWE9gcjgzKjjyaXz2aT9xFwlD+4vCnqNUIr+sNhYGhyw8ZfO4k0Q+XWLFZQWX3F9XW+xNx1TkVhG5UuKR1rxJGxQJKSwovmkzr+5KvtJOBsSlDc55\/67v\/a9O+bpB9r0E\/2n9vVlVqUTpZJ25rGyuJI0JBOSV7GDFudlbF3JbAmlySUrM0XV8NmH\/MbElcoMw7y+dxBYT+oqLB5JLET+tSqlSFQUpSnmEH1tvDe9f+SnWOxclQVpxBKeloEJJMQsii1ikLBKG4pyY3RE5ov8bv8A2vTvm6Qfa9BP8vL6xRhtNcXpzRtqOyd\/6bhvnJY0Wsla6y9471sDzkyF0LjCBWsVuCo1UrUGqD+t4fkNCM0V85NL7j+dYUPTz2ByaMHeHwLEalvWKEikvIo\/r4lOZlAnIHGMPrg0Kq04jFisAFJJuzo5GnrXb2iLNEklJI8Glw3dEMtXZ0ICAhzNIRANXoL2srbiiax85OukZbovsriM2E+eckg7KjjqaXz2aT9xFwlD+4vCnr0SNS4rE6NKVkafXkTTwCBRqMkiA4c\/cyAjAtgZPgWV4EnoRFlz9ND18PwkTiMf5h1m2CdD0MPGSOWEf9C0ygIz7YGM4GFeNJ0HEirwXOGxmcJie832lw3a8FshklnCknuN6C14Gksyt5NE1AYgC5ErbVqlErKyJUe0ECFW5rkyFGTkcpqiCJKzriMxNN4RDod7ayCBXesdkpPgbvaGiNY\/r27kjuqJ8bd0W8VWfuNSa5xSEDm6e0NHar\/bmk0DirJ8Dn0RxJSgowk7DEwvYmqzqctyQRkMMgQ+ztdqrOuO3I\/GRwyFCSSUnKLJKwxLL6PiC09+sK6SzluI8Tl7O4flQfo2ulU5cSPC5dI4NqJ3b1TeuIwUJL6qhdTFovkVPDPJN7MoWqF1z2ixxUgM8UzcgRNDekb0JBZCXpd8KRGxa4CXNSbxvXszQ6khrutxlzqn8D102eGBuGWGeIZY7ZUcZSVoqkyIgcWD2VqbRxl22glTrSRFgwwwKwxwwxDHDp9lKRR3jWK9jAC8Hdwb1zSvVIFycxMq9kN7etdl6VAhTmKFWtlKI6MrFCx5Bhm7dTv\/AK9ihWfutHko+R7I0A19+MVfuvIU3\/B1T2yNUgZ1zS6pi1SHY+jXWh7EVMpgGnNPsbW+jHW+LESspfmEtDS0tUcZ0LO1JS0iLq9gqQY71r1VH1fgIcJRGHyGSFyYHxGYjcfYkXjL5M5C2sDGjMWOOv1KslEV8ljyIcD3Drd1dYwtNhzmsXSd8pEBxEQEBAfYQAOQgAAIjpZrL+1TDhNJOk7pT1+8OrAtJy60Iai\/8P2Fo\/q381ORWjMkXej9APIIVEGEHlYGlbh6qn1C7GS2LJszIl7A081VPt92LlspTZlxEkghMQWQQXgUV6C9NTU+tSxqc0ZCxFtbq260Y\/C7M5ZyuIevaq6uut5PoOzxgckiLO1NbC1I2pqRkokHob\/H2OTsa5le0RC5v2k1XfaKec3VqwPXxD1zVzVh9vV4xdnQDkERYGBjiLGhZWRCQhQeivLGzP7OsanZEQuQ7U6hvNNLFMljJZ6+H+tar6hvVzLE8kkxZ7fD2VkaI20I2lpRkIW\/0dWkSrkpyVUQWen2n0jWxQV0yrVIcrZ\/WNVdJFkvFDM7ISmpWVIlSN6UhIlIKTp\/StotIG+bfGy+uU5CF9cmxxZnBS3uKQ9Is9UbW1weF6ZA3pT1avV7SJBCRQy+x05K189N2R1Sh17t2bgQBTRKbBruYVdJlUdlLYagX+pQCvJfZ8mSx2LNhq9frjqnD6FbsHJT5TvK\/T7ipOC3fGsmWTI+\/O9teZ3Qr\/8ACPRPxTX6hRWvc6vqQfBMhHwzZT1JwWkY3iyxlH3ZeoyuIRqcR9YwSFtTuLbs5p5JKYOUSGOge7xH07WXT6S3QcnkEg89oiMTiEZgceRMMfbSG5s9TOJJUEmEnF4GF7RaMZpAWzGrEY5k54Zl55YZ4jjl6VhhmZnjhhiOWWrmjBq74KYWmiEtOQQQlILIJLwKK9W2f0xYraBVJ4fgnaZVIo4\/RF7Wsr63qG9x9Ij0dfZY9I2Zjb1Dg4av6ZMNUYJJTMcE7rKvWdgNbIPfjJ4HDAED7a9QzmmZOawylAJBno1VVJN7kk5TDFkAnm6\/a1QegmXuQYAvffW7LqyFW1FlEelLdgsS7E6vzSg3YTjQzc436JrvrBNb8dgNJDNsjlZ1bC6ii5EfizdgjS+uvDEzyFpVtLuhTrkO0OlDvW3xksgZShzjXoWsGljxZQo5VOilDZGWRkZ4w0pGtpQp0CH18QAQ7h7bRaNJZD8ZL6wSFJnJYjVt6s9IrINTqOuSJFS9UQlSEGqFGr+jiRgBHMLQSFqHAAAAAAD2HstqNFLxSHPDT5DPLZtB5VXUjWR6TNh7c49ZC4TKbDkaOPRptOcXHWnUeK0clJenfyXiW+xbtoeB3rHBbJEm8pXdtDTuiZGLXIU3mJOqpSiJ3esjBrjqXwJaToaCUPHPlrAm81Z7Hm0HitjRtZHpM2EuLdsvqNKqOVnPLV5zxEuo1p1Ild5LCXhz85oiUKhEUrmNJI7F2whubvZKxGkcEpyRWQUoT7R6Nq4+K2YVgkMUtogICICHcPSAAiPcHbVzRtXJPg5hZyQ1K2I0aRuSEJEZBSZN7M2h0oZ7JBZLIESnbZK9MjvHHVW1O6FQhX9EzMztIXVI1NKI9cu1f0oaa4BHLJ8UQ4yP2fsTq\/Cb6aRPODBrkdmVdNajk6iPSptzRq+grWsJpbcnTx6KtuaxXrvrDCqDagPKDB0kvtG16gg9zRc1glCADytgNbJxQb34HDAV7HzqA1unN+PfltpYoWSqKggtLRcpii6ACMPacijjFLmVYzPrencG7aDTF9qXNXJ4fgodYpzNYNMn63M0sml2KlpikfjrDD2NGxsLenbm\/wBq54YGYZYZ4hljtHowWrBbMKsRhgceQclPMIPLzKN5BJJyg4skkvMwzV\/RvBJ8FMLTRBmbhhgXhjhhiGOPtjZzTyOXOSokMdAholssiUkgz+tYZE2qG5y\/2isTkc4f0TDHm1Q4uWsenccpglPI5KCd3lvtu+Ne4LfMeFI9E\/Culx0pOqQk2bLJkfdh\/pT9LTq7ZNgyxlF4saJ16glBsPwjMSCt29uz+u4haMZVR6UthS9BshqlMKHcDF5Hmu8V\/wAa4aqTG+XHBecBrTFq\/ryHVXGEsdizYUhQ+33FqbnlApQOSQhWj2j0hXwj42XVynOXMWrukLlOBRy6xU56BhbmxtZG5K3NyQhIj9Q\/\/8QAVRAAAgIAAgQIBwsFDQgDAQAAAQIDBAAFESExQRIwQFBRYGFxBhMUIjJCtSUzUnaBgqOywsPkFWKRlKEgI0NFU1RjcnOSk6SxECQ0RHSi0tOEtMHE\/9oACAEBAA0\/AOaEGl5JHCIo6SThPSiqT+XSDsKVRIcD0WiqRwRH5ZnRsbpbuZF\/o441xuENJ5T9NI+Pg1qlSD6kQON6x5pYhX9EbKMf0uYzv9Zsfnzu3+px0Ek46ASMfmTuv+hx\/RZjOn1WxuWTNJ5l\/RIzY+DZqVJ\/rxE43+PovGfoJExvkpZi0P8A2SRvje0lZLEQ+WFy2H9GO3KaDnsC2hFhxpSSJw6MOwjqaBpJOI9tOgxvT8LoKwcLgfOxutZpOtdf8GHh4f8AgcrqJF9LL4yTGknxl23LYbSe2QnkenT4ylblrNp74yMJsizWss30sfAkxse1lU4nX\/Bn4GH2VLxNGbhfBUThA5\/qYI0g9R0\/i6mfKrfcY4\/Q+eRjdfzV\/HS\/JBEQow+2osniKv8AgQhE5Wh0imZPH1flgl4UeN97Kn8RL3mCUlXOJNmXWz5Ja0ncscvp\/MJ6hV0LzWJ5FiijUb2dyABhNIDwkwUUPbM40v8AMGJNuXZVpqxkdDuCZH7i3MMWgfk7NdNqIL0I5IkT5rYfV42U+PoueyZQCnzxiwgeGeCRZY5FOwq6Egjn2sheazZlWGKNelncgDA0r+UrQeGih6UTVJLgOWiqafFVYf6kKaEHMpcNLU4XjKs39pA+lDg6F\/KdUPNSc9LpreLFlOFBZrTLNFIOlXQkHnlFLMzHQABtJOE0oZkcihA\/bKNcvcmEctBSQ+KqQf2cK6h37eaXcGemx8ZVsf2sLeae\/bh9CCd305fO3ZKdcPc+HUMrKdIYHWCCOdpI+HWyeroe1L0EjZGn5z4JITJqchCOv9PJtmPNoPn5NccmNBv8nfbCcJFwrOTWiEtRdJUbJE\/OXnOtG0k9iaQRxxouss7NoAAxrjm8IZY\/\/qRP9d8WZDJPZnkaWWVztZ3Ykk831ZBJBZgkaKWJ12MjqQQcao4PCKJP\/txJ9dMWYhJBYgkEkcqNrDIy6QQecJAwp5fFoe1ccbok6OljqGIpOFVyau58X2PO22WTnOWTTayWw58V2vA22KTEQUXKEuhLNR23Sp\/ow1Hm5k1VQ2mCiTse0Rv6I8WT580p1Ku5EUakQblHOtU+ZNEdo3o6nU6Hepwqa6vC0QXtG16xO\/pj5sGmK\/n0ZDpW6Y6vTJ0yYmkaSWWRi7u7HSzMx1kk7TzvDIskUsbFHR1OkMpGsEHYcHgw0M+kIVLG4R2+iTok5piQu7uQqqqjSSSdgGPPhzLOIjoe9uMUB3QdLbX571Q5dnEpLPR6Ipzvg6G2piVFeORGDKysNIII2g8zQxtJLLIwRERBpZmY6gANpxG5S5cXSj5oV\/0r8+yuEp3HJd8rJ\/1r4njWSKWNg6OjjSrKw1EEaweZI1LO7EKqqNZJJ2AYrvwLt2MkHNHT7gc\/2JODSuSEk5XI\/wD\/ADnEihkdSCrKdYII2g8xxOYs6zKF\/wDjHG2tEf5EeufX6gzOIslzKZ\/+Dc7K0p\/kT6h9TmKzFwM7vxNrqQuP+HjO6Vx6fUOvFwMkvytruRR7K8h3yoPQ5gzmJxQj2+SRbDbkH1MWJXlmmkYu8jueEzMx1kknST1DrypLDNGxR43Q8JWVhrBBGkHGTQoL8WzyqLYtqMfXG5uXVl4FWsGAe3Zf3uFO07+gYzCYu3wIkGpIowdiINQHUXL5gwB9CVDqeKQb0caiMWU4FqsSC9SynvkL9q8sgjaSWV2CoiINLMxOoADacZM8kGUw7BLue0w6Zeo+cvHBm8G0RfAtIPhR4njWSKVGDK6ONKspG0EbDyu9GkufzRnXFXbWlXvk2v1JpRvLkEznXLAut63fFtTlSL4jLapOuzckB8WndvbsGMxtSWbU77XklPCY9ScutR2as6bUkiPCU4dfEZlVB0mtbiA8ZH3b07DynwbeWpQ4J8yxNsns\/OI0J1L8JHiqXuEfMrzbILPyE6H5R4UCWnUKnz4K+yxP9lepvgwIqdsudLz19lef7LcmhRnkkchVRFGksSdgAxE3keURH1KcBIQ9hkJLnqbM3kebxD16c5Ac9pjIDjE0avHIhDK6sNIII2gjkvhaXrtwTrioJ7+fn+h1P8EilcEnXJQf3g\/M9DkgGkk7AMV5PIcpG4VKxIVh\/aEl+p9mTyDNhuNSyQGc\/wBmQHwRpBGwjkfhETlVL4SLMp8fIO6Pqj4On8lXfhMsIBgk+dHyPwTg8iQbjbk0PZb7HVHwsg8iYbhbi0vWb7HIsoy2awqHV4yUDRFH89yFxdsy2LErbXllYu7HtJPVGlZisV5V2pLCwdGHaCMZvlsFgoD6EpGiSP5jgryHPbxt3AP5tS2A9jyN1TyG8LdMH+bXdoHYki8hyLgZPV7PJPfvpi\/VPPuHk9n\/AOX7z9MqcgyfKrVzgn12hjLKvzjqxZmeaaRtrvISzMe0k9U6s8c0Mg2pJGwZWHcRjN8qq2woPomaMOV71JIPH+Ema1qfb4mEmxIfowOqvg3m1mn2+Jm\/3iM\/SEcflGTyW5Oya9J\/4Qjqrm+UR3Iu2WjJo+rNx9fNDQi6AtBRW+x1Vs5oKEvQVvqa2vuL8dQqTWZf6kKF2\/YMXLMs8rdLysXY\/pPVWnZisRHoeJg6n9IxeqQ2Yv6kyB1\/YeNfJZaQPbfIq\/e9V0ySKmx7aBNX7vjc28IKFbvEYex91xma+V\/775P5T4ryatJY974cenT4vRj4v\/i8fF\/8Xj4v\/i8fF\/8AF4+L\/wCLx8X\/AMXj4v8A4vHxf\/F4+L\/4vHxf\/F4+L\/4vHxf\/ABeO3IvxWNwsQT1vqCXCb6F+P6tjxJxp0K1upJEj\/wBR2HBbl4bQzU6csyL\/AFnUELh99+\/H9WDxzY3ivBPZ+uIsdmR\/ihj4v\/i8fF\/8Xj4v\/i8fF\/8AF4+L\/wCLx8X\/AMXj4v8A4vHxf\/F4+L\/4vHxf\/F4+L\/4vHxf\/ABeMq8k\/33yfybxvlNaOx73w5NGjxmjjMpz+\/V7hIEs\/e8bZzDMbX6tHGn3vGe63s2xyCRSrxuoZWB3EHURh9lzJ9FXX2wgGI4jBYisniryL21yTw\/mE4hcpJFIpR0ZdRVlOsEcplcJHHGpZnZtQCgayTiTdZQyXnXsr+p88jEes3M3Ita+yHVEMRqFSNFCqoG4AagOQe5Ps2vxlbMcvtfrMbp9zxtfI7M\/6xPwPuuM91vZtjkfA0Q5zTAjtKRsD7pU7HxLJwaudVkPiH6ElG2KTsPJ4n0Ws6tIRAnSsW+WTsGPF6J85tgPZfpEe6JOxOR+5Ps2vxlnJK0\/+BPwPveNq+CtGL9M88nGe63s2xyS3E0VitOgkjlRtqsraQRgEyXKGuSfLR8Ib3g5KSJKdHXHPmXb0pBipEsVetBGI44kXYqquoDknuT7Nr8Za8FLsf6J4JONgyvLE+hD8Z7rezbHJWBBBGkEHccTy8LNMuiGrLnc++xjdA3\/ZyOCXTlmXyrqzGRD77J\/QKf7+FACqBoAA3DkvuT7Nr8ZPleZJ9CX41IcqH+QhPGe63s2xyaxE8U8Mih0kRxwWVlOogg6CMZ07y5XLt8Q216rnpTd0ryHJmSXNJhq8e21KqHpff0LivEkUMMahEjjQcFVUDUAANAHJvcn2bX4x4c1H+QmPG+5Ps6DjPdb2bY5PZj4dKyRpNW1HrimHcdvSuMsty1rMR9WSI8E946Dx+Z24q1aIes8p4I09AG87hitHw7lkDQbVqTXLMe87Ogcn9yfZtfjNGbezp+NeHKj\/AJCEcZ7rezbHKL5TLs44P8sikwTHvQFOPol8uyfhD+GdQZ5h3KQnKPcn2bX4xIc1P+RmHGz5Xlr\/AEITjPdb2bY5RmdB0ryNsjsJ58L\/ADHUHFeV4pY2GhkdDwWU9oI42xKkUMa6y7ueCqjtJOMsoRpO67JLD+fPJ85yTyj3J9m1+MgyrMn+hKcbb8FaUnyrPPHxiNmatPPKsUYMlCdEBZ+kkAYI0g8ozoxZvXH\/AFmuX6YPxuS+NzewP+k96+mKcoGGfLEE0EglQmKhAjgMu9SCDxlTwUuyfK08EfG2cks1\/wBXn4f3vGp\/F9gizU7hFLpCd642PmGUnxsfea8uODwmrJJwLKD8+CTgyLyZ0vZdO\/YhWaH7fGolHLoH7y0032OTBCy15JOHYkH5kCcKRvkGBpAzHNSYou9YIjwmxLty6sfJamjoMUWgP8\/ja2S1a\/6xNw\/uuNq5jmNX9Zjjf7nj4mDRyxOUdGGwqy6CDhP4DNgZpQOywCJMPvnBtVCeyaL7aDEvoWak6TxN3OhI5HlvhJSm09kkcsP2+NzLwkuz\/JGkUP2ORwjTLZtTpBEve7kAYTfWHk1MHtnl+whw+kCvlQMcpHbZbTJiVi0ksrl3djtLM2kk8fazHL6v6tG7\/fcblPhDQsk9jh633vIhtkqTvFwgNzhToYdhwNT24NFK5+wGJ8S7KGbAU5NPQHJMbnsVsEaQeQQ2MqcfLeiTjZp81c\/r0qcgGItuX5UBcl4Q3MykRoex2xut2NF233gECJMaTwXtztKEB3Ip1IOxeRZtn9+0D2IErfdcbHkktwDtokWvu+Sx\/wAXznymp3CGXSF71xsbMcpJki7zXlwV4RrJJwLKDpeCTgyLx3uT7Sr8b7re0rHHcDhLXkk4diQfmQJpkb5BjYL+anxUPeIIiWYYk\/i6sfJamjoMUWgP8\/kr5JFcYdt4m195xt6pNXl\/qTIUb9hxUsSwSr0PExVh+kcmiYNHLG5R0YbCrDWDhNtfNgZpAOywCJMPvnBtU\/kmi+0gxMP3uzVnSeJu50JHF+5PtKvxvut7SscXF6dm3OkESd7uQMJpANYeTUwe2eX7CnD6QIMqBjlI7bLEyf3CMTOXlmlcu7sdpZm0knk1yzFBEOl5WCqP0nFGpDXiX82FAi\/sHHWM0N+LuvqLP2+UjbJUneLhAbnCnQw7DgbbcGilc7zwQYnxJsoZsBTk7lckxOexWwRpB4j3J9pV+N91vaVjiBiLbl+VaLkvCG5mUiND2OwxsW5Z0XbfeAQIkxpPBe3O0oQHcinUg7F5TXzQX5e6ghs\/Y4\/OMnkqynpmoSf+Ew5ZHsy+w3lNTuEMukL3rjY+Y5SfGR\/LXlxweE1ZJOBZQfnwScGRf3XuT7Sr8b7re0rH7oIWWvJJw7Eg\/MgThSN8gxsXMM1Jhi7xBEeEwxJ\/F1Y+S1AOgxRaA\/z+WZPk8dSI9E1+T\/whPH+DmbV7Zb+gnPkzj9Lg8uiYNHLG5R0YbCrDWDiPUa+bAyyAdlgESYffODap\/JNF9pBiX0LNSdJ4m7nQkf7fcn2lX433W9pWP9sI0y2bU6QRL3u5AGE0gGsPJqYPbPL9hTh9kGVAxSkdtgkyYmcvLNK5d3Y7SzNpJPLvCTNrNsf2MB8nj+oTx+cZTaqBz6jyxlUf5ra8V5XiljbajoeCyntBHMA2yVJ3i4QG5wp0MOw4G23BopW+8hQYnxJsoZsBTk7lckxOexWwRlHtKvxvuv7SsYi25flWi5LwhuZlIjQ9jsMbEt2NF233gECJMaTwXtztKEB3Ip1IOxeYLEyRQxrtd3PBVR2knGT5VVqFh67wxhWf5x18gzp1zir2i5rl+lD8x3xEJ8seYyVz4mVZk4CPpCEOgOleNorKsGWRzGKvomlaZ+GiaA5LuT53MeSO+cWuwU9cX0pTkOT3XoXGH83ua4yexHXqnnN1KNMn+b09bkdju3Ic1y2aGF22RzAcKGT5kgBxWmeGaJhoZJIzwWU9oI6o2pkhgiUaWeSQhVUdpJxlOWwwyuuySb0ppPnuS3IvCqH8pRdAsehZTvL+f1R8FYfylL0Gz6FZf7\/n8j8FnOZ1ukwINFlP7nn9UfClxmdnpEDjRWT+55\/I5EKujAFWVhoIIO0HAn8qytz69Ox50XeU9A9o6nmfyrNHHqU6\/nS93D9AdpxGoVEUaFVVGgAAbAOSeCxItcHbJQmPn\/4T9T\/CkgVOFtjoQnzP8V+S268kFiBxpWSOVSrK3YQdBxDL4\/Lp2\/h6c2uJ\/st2jqbNL4\/MZ1\/gacOuV\/sr2nFOCOCvEg0JHHEoVVUdAA5N4KJJM4Qa56G2ZO+P0x1N8K0jmUNtgoDXCnfJ6Z5OwIZSNIIO0EYzkvdyd9yIT59fvhPUvJuBdzh9zoD5lfvmOFACqBoAA2ADlFTTbyewfUtIPQJ+BKPNbFSeSGxBIvBeOSNirIwOwgjQepNueOGvDGOE8kkjBVRQNpJOgYt6Lec2F9e049AH4EXoLyqyyQ+EEKD0JfQjtdz+g\/Umszw+D8Lj05fQktfM9BOV3q0lezXkGlJIpVKsp7wcW+FYya4w9+rk+ix+HHsfqPU4NnObqj3muD6Cn+Ul2JijWjr1oIxoWKKJeCqjuHLINM+U3iNdayB9R9jjGW2HgswPtV1\/1B2g7COouZWEgrQJtZ2\/0A2knUBibRPm14DXZskfUTYg5dlNf9\/gjGvMaqep2zR+pgHQQeoZOgAYzev+8QSDXltV\/U7JpPX5gmcy5\/QhHvDtttoPgN\/C9Q4nEmQUJl9\/ddlxx8BfU5hlRkkjdQyurDQVYHUQRi\/P58S6zlk77Im\/oW9Q9QaE\/mRnUczmTbEv9CvrnESKkcaKFVFUaAABsA5iuwPDYrTKHjkjcaCrA7jjMJj5JY9NqTt\/y0\/2H5\/y+YeV2PQa5Iv\/AC0H23xSgSGtXhUIkUaDQqqBzJfgaGzWmXhJKjbiMXJtFW56T1HfZBY+w\/PtKfRbubHtuu2Cv9t8UYFhrVol4KIi8zXYGhsVplDxyo+1WBxNL2vNlpfZHN0x7kk57hl2+hNmRTbHB0R\/DkxRhWGtWhQJHGi7AAOaJ42jlhkUOjo40MrKdRBG0HGl5b2SoC81LeXr73h551S0cmcFJr28PPvSHEEaxwwxIESNEGhVVRqAA2Dms6ZbmUjRFXvne0W6KbFWVop68yGOSJ0Ogq6toII51tSrFBXhQySSu50BUVdJJOBwZaWTnRJBRO55t0k3N0EWitmiJqnA2RWgPTTobauK+5taSodkkT7HRtxHOdg+iupIkG2SVzqRF3k4ni0WMzdPMg07YqoPoL0ttbnCMMaV+IBbNOQ+vE\/1lOo4syMMuzeFT4iyBuPwJelDzjXdfyjm8ynxFZftyHcgw4Vr2YSgGzckG+V\/qqNQ5yuR8CavMukHoI3qw2hhrBwz6TNtsUOywBtTok5vR\/f9k9\/pSsDsTpkxSTgw1oRoHaTvZjtLHWTzpIhR0cBlZWGggg7Qca5b3g7HrKdL0v8A1YUkMpGggjcea2IAAGkkncMapqPg9JqeToe70DoixEipHGihVRVGgKoGoADnfQXmT0K2ZdkvwJeiTFKUx2K06FHRh\/8Ah2g7COabsojr1YELu7HoA3DaTsAxqeGP062W9kfw5ul+eq0RWhnESAyxbwko1eMi7Ma2rWk0tXtxg++QvvHM+prNl9K16kZPvk77hizEBfziVAJJOmOIfwcXPkmuKT0Zq8u6WF9qOMWJdFLN401dkVgepLzLWl0Xc4kTSvbFAPXlwmuWQ+dNYk3yzPtdzz9ciaKxWnQSRyI20MpwC0tqjrks5aPvIOY\/NlrU9cdnMh9iDFSIRVqsCCOOJF3Ko6g65buQpojhs9L1dySdKYryNHNDKhR43Q6GVlbQQQdoPL55FjhhiQu8judCqqjSSSdgGNUtHIX0PFX6Htbnk\/M6iRR6I7oXRDc4OyO0B+x8VG0PDINo3OjDU6Hcw5bbbRHDGNg3u7HUqLvY6hiSLz7pGmGlwtsdUH9snUaAMaGaQgCzUc\/BO9DvQ4nZjQzSEE17aD4J3ON6HlcJU38zmBFaoh3ud7ncgxOqm\/mkwBs23HSdyDcg6kW00SQyDYdzow1o67mGJZf3u6F0zU+FsjtAfsflMUv77fK6JbfB2x1Af2vsGKq6EjjGtjvd2Ot3bex6lTxtHNDKgdJEcaGVlOogjaDjXLdyFAXmrdL1d7x\/mYHJtUtLIn0xzWuh7O9I+hMV41jhhiQIkaINCqqjQAANgHU08OW1S1R1syP3c+KcpisVp0MckbjcynkdyVYq9aBDJJI7bAqjACy1aOqStlx+9n6oVotFPOI00nsisD148R64pPShsR7pYX2Oh5DLrkf0Ya8e+WZ9iIMWItF3OHTZ0xVx6kfVLW1a0mhbFSXdJC+44sykUM4iQiKX8yUepLx9aUDMM4lQmKL8yMavGS9mNTWrb6GsW5B68z7z1UuRmOerOgdHXtB3jcdoxrkmT07OW9kvw4eh+N1PCPQs5l2RfAi6ZMUohHXrQIESNf8A9J2knWT1WYEEEaQQdxxrmveDyalfpel\/6sROySRupVkZToKsDsI4mRwiIgLMzMdAAA2k41TUfB2TYu8Pd\/8AVhQAqgaAANw6shCTNsgv9C2ANj9EmKUnAnrzLoYdBG5lO0MNRH7u7IEgrQrpYneTuCjaWOoDBTSJfTr5f2Vwdr9MnVysjfk7N4VHj6zfbiO9DhyzUr8QLVrkY9aJ\/rKdY\/cpwWu3pQVrU4z68r\/VXacWY1GY5vMo8fYI3D4EXQg6vWNYVtTxONkkTjWjjcRiaXRWzRU86DhbIrQHoP0Nsb\/bBLotZo6a5tG2KqD6b9uxcQDSQNbyvvllfa7tvJ6wWoWinrTIJI5EcaGV1bSCDgaZbmUjTJYoDe8W+SHGqWplJ0x2L43NLvihxViWKvWhQRxxog0BVVdAAHOP\/8QAFBEBAAAAAAAAAAAAAAAAAAAAsP\/aAAgBAgEBPwAcT\/\/EABQRAQAAAAAAAAAAAAAAAAAAALD\/2gAIAQMBAT8AHE\/\/2Q==" }, From 1a0a40634f90219a3d5a1c9afcd485d7904aeee3 Mon Sep 17 00:00:00 2001 From: Voiko Volodymyr Date: Wed, 17 Dec 2025 12:35:31 +0200 Subject: [PATCH 02/11] Enable fixed test --- .../SnappThemingDesignTokensParserTests.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/Tests/SnappThemingDesignTokensSupportTests/SnappThemingDesignTokensParserTests.swift b/Tests/SnappThemingDesignTokensSupportTests/SnappThemingDesignTokensParserTests.swift index b66e798..935e131 100644 --- a/Tests/SnappThemingDesignTokensSupportTests/SnappThemingDesignTokensParserTests.swift +++ b/Tests/SnappThemingDesignTokensSupportTests/SnappThemingDesignTokensParserTests.swift @@ -83,9 +83,7 @@ struct SnappThemingDesignTokensParserTests { } } - // TODO: Fix failing tests - https://github.com/Snapp-Mobile/SnappThemingDesignTokensSupport/issues/18 @Test( - .disabled(), arguments: [ ("design.tokens", "expected.snapptheming") ] From 4969a48314c6b3a44999d434b6fa403b79542ed5 Mon Sep 17 00:00:00 2001 From: Voiko Volodymyr Date: Wed, 17 Dec 2025 12:57:37 +0200 Subject: [PATCH 03/11] Test unresolved expression error --- .../SnappThemingDesignTokensParserTests.swift | 16 ++++++++++++++++ .../Utils/PassthroughTokenProcessor.swift | 18 ++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 Tests/SnappThemingDesignTokensSupportTests/Utils/PassthroughTokenProcessor.swift diff --git a/Tests/SnappThemingDesignTokensSupportTests/SnappThemingDesignTokensParserTests.swift b/Tests/SnappThemingDesignTokensSupportTests/SnappThemingDesignTokensParserTests.swift index 935e131..369fd5c 100644 --- a/Tests/SnappThemingDesignTokensSupportTests/SnappThemingDesignTokensParserTests.swift +++ b/Tests/SnappThemingDesignTokensSupportTests/SnappThemingDesignTokensParserTests.swift @@ -66,6 +66,21 @@ struct SnappThemingDesignTokensParserTests { forKey: "unsupported" ) ), + ( + #""" + { + "expression": { + "$type": "dimension", + "$value": "2 * 10px" + } + } + """#, + DesignTokensDimensionValueExtractorError.unresolvedExpression( + DimensionExpression(elements: [ + .value(2), .multiply, .value(10, .px) + ]) + ) + ), ] as [(String, Error)] ) func testFailingParsingDesignTokensJSONIntoSnappThemingDeclaration( @@ -78,6 +93,7 @@ struct SnappThemingDesignTokensParserTests { await #expect(throws: expectedError as NSError) { try await SnappThemingParser.parse( fromDesignTokens: designTokensJSON, + tokenProcessor: .passthrough, designTokensConverterConfiguration: configuration ) } diff --git a/Tests/SnappThemingDesignTokensSupportTests/Utils/PassthroughTokenProcessor.swift b/Tests/SnappThemingDesignTokensSupportTests/Utils/PassthroughTokenProcessor.swift new file mode 100644 index 0000000..fe972db --- /dev/null +++ b/Tests/SnappThemingDesignTokensSupportTests/Utils/PassthroughTokenProcessor.swift @@ -0,0 +1,18 @@ +// +// TokenProcessor+PassthroughTokenProcessor.swift +// SnappThemingDesignTokensSupport +// +// Created by Volodymyr Voiko on 17.12.2025. +// + +import SnappDesignTokens + +struct PassthroughTokenProcessor: TokenProcessor { + init() {} + + func process(_ token: Token) async throws -> Token { token } +} + +extension TokenProcessor where Self == PassthroughTokenProcessor { + static var passthrough: Self { PassthroughTokenProcessor() } +} From 3e3542ebe21a5be38190d87b73a2983d565482cf Mon Sep 17 00:00:00 2001 From: Voiko Volodymyr Date: Wed, 17 Dec 2025 17:37:02 +0200 Subject: [PATCH 04/11] Test typography extraction failures --- ...DesignTokensTypographyValueExtractor.swift | 25 ++-- .../SnappThemingDesignTokensParserTests.swift | 108 ++++++++++++++++++ 2 files changed, 122 insertions(+), 11 deletions(-) diff --git a/Sources/SnappThemingDesignTokensSupport/Convertor/Extractor/DesignTokensTypographyValueExtractor.swift b/Sources/SnappThemingDesignTokensSupport/Convertor/Extractor/DesignTokensTypographyValueExtractor.swift index 05c3560..fbafc11 100644 --- a/Sources/SnappThemingDesignTokensSupport/Convertor/Extractor/DesignTokensTypographyValueExtractor.swift +++ b/Sources/SnappThemingDesignTokensSupport/Convertor/Extractor/DesignTokensTypographyValueExtractor.swift @@ -8,14 +8,14 @@ import Foundation import SnappDesignTokens import SnappTheming -extension DesignTokensTokenValueExtractor { - private enum TypographyExtractionError: Error { - case unresolvedReferences - case unresolvedExpressions - case invalidFontSizeUnit - case fontsEmpty - } +enum DesignTokensTypographyValueExtractorError: Error, Equatable, Sendable { + case unresolvedReferences + case unresolvedExpressions + case invalidFontSizeUnit + case fontsEmpty +} +extension DesignTokensTokenValueExtractor { static func typography( fontWeightMapping: FontWeightMapping? = nil ) -> Self { @@ -25,19 +25,22 @@ extension DesignTokensTokenValueExtractor { case .value(let fontWeight) = value.fontWeight, case .value(let fontSizeValue) = value.fontSize else { - throw TypographyExtractionError.unresolvedReferences + throw DesignTokensTypographyValueExtractorError + .unresolvedReferences } guard let fontFamilyName = fontFamilyValue.names.first else { - throw TypographyExtractionError.fontsEmpty + throw DesignTokensTypographyValueExtractorError.fontsEmpty } guard case .constant(let fontSize) = fontSizeValue else { - throw TypographyExtractionError.unresolvedExpressions + throw DesignTokensTypographyValueExtractorError + .unresolvedExpressions } guard fontSize.unit == .px else { - throw TypographyExtractionError.invalidFontSizeUnit + throw DesignTokensTypographyValueExtractorError + .invalidFontSizeUnit } var fontName = fontFamilyName diff --git a/Tests/SnappThemingDesignTokensSupportTests/SnappThemingDesignTokensParserTests.swift b/Tests/SnappThemingDesignTokensSupportTests/SnappThemingDesignTokensParserTests.swift index 369fd5c..6c8d354 100644 --- a/Tests/SnappThemingDesignTokensSupportTests/SnappThemingDesignTokensParserTests.swift +++ b/Tests/SnappThemingDesignTokensSupportTests/SnappThemingDesignTokensParserTests.swift @@ -81,6 +81,78 @@ struct SnappThemingDesignTokensParserTests { ]) ) ), + ( + #""" + { + "typography": { + "$type": "typography", + "$value": { + "fontFamily": "{reference}", + "fontSize": "{reference}", + "fontWeight": "{reference}", + "letterSpacing": "{reference}", + "lineHeight": "{reference}" + } + + } + } + """#, + DesignTokensTypographyValueExtractorError.unresolvedReferences + ), + ( + #""" + { + "typography": { + "$type": "typography", + "$value": { + "fontFamily": "Arial", + "fontSize": "2 * 10px", + "fontWeight": 700, + "letterSpacing": "0.1px", + "lineHeight": 1.2 + } + + } + } + """#, + DesignTokensTypographyValueExtractorError.unresolvedExpressions + ), + ( + #""" + { + "typography": { + "$type": "typography", + "$value": { + "fontFamily": "Arial", + "fontSize": "10rem", + "fontWeight": 700, + "letterSpacing": "0.1px", + "lineHeight": 1.2 + } + + } + } + """#, + DesignTokensTypographyValueExtractorError.invalidFontSizeUnit + ), + ( + #""" + { + "typography": { + "$type": "typography", + "$value": { + "fontFamily": [], + "fontSize": "10px", + "fontWeight": 700, + "letterSpacing": "0.1px", + "lineHeight": 1.2 + } + + } + } + """#, + DesignTokensTypographyValueExtractorError.fontsEmpty + ), ] as [(String, Error)] ) func testFailingParsingDesignTokensJSONIntoSnappThemingDeclaration( @@ -99,6 +171,42 @@ struct SnappThemingDesignTokensParserTests { } } + @Test( + "Test FontWeight to FontPostscriptName mapping during parsing DesignTokens JSON into SnappThemingDeclaration", + arguments: [ + (nil, "Arial"), + ([100: "Thin"], "Arial-Thin"), + ] as [(FontWeightMapping?, String)] + ) + func testSuccessfulFontWeightToFontPostscriptNameMapping( + fontWeightMapping: FontWeightMapping?, + expectedPostscriptName: String + ) async throws { + let configuration = DesignTokensConverter.Configuration( + fontWeightMapping: fontWeightMapping + ) + let declaration = try await SnappThemingParser.parse( + fromDesignTokens: #""" + { + "typography": { + "$type": "typography", + "$value": { + "fontFamily": ["Arial"], + "fontSize": "10px", + "fontWeight": 100, + "letterSpacing": "0.1px", + "lineHeight": 1.2 + } + + } + } + """#, + designTokensConverterConfiguration: configuration + ) + let representation: SnappThemingTypographyRepresentation = try #require(declaration.typography.typography) + #expect(representation.font.value?.postScriptName == expectedPostscriptName) + } + @Test( arguments: [ ("design.tokens", "expected.snapptheming") From 56385987b0ce517901f763f469bed1a7180c2945 Mon Sep 17 00:00:00 2001 From: Voiko Volodymyr Date: Wed, 17 Dec 2025 17:43:24 +0200 Subject: [PATCH 05/11] Test gradient extraction failures --- .../DesignTokensGradientValueExtractor.swift | 10 +++++----- .../SnappThemingDesignTokensParserTests.swift | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/Sources/SnappThemingDesignTokensSupport/Convertor/Extractor/DesignTokensGradientValueExtractor.swift b/Sources/SnappThemingDesignTokensSupport/Convertor/Extractor/DesignTokensGradientValueExtractor.swift index 8e880ae..08f8586 100644 --- a/Sources/SnappThemingDesignTokensSupport/Convertor/Extractor/DesignTokensGradientValueExtractor.swift +++ b/Sources/SnappThemingDesignTokensSupport/Convertor/Extractor/DesignTokensGradientValueExtractor.swift @@ -8,11 +8,11 @@ import Foundation import SnappDesignTokens import SnappTheming -extension DesignTokensTokenValueExtractor { - private enum GradientExtractionError: Error { - case unresolvedReferences - } +enum DesignTokensGradientValueExtractionError: Error { + case unresolvedReferences +} +extension DesignTokensTokenValueExtractor { static func gradient(using format: ColorHexFormat) -> Self { .init(\.gradientsCache) { (value: GradientValue) in let colorsWithPositions = try value.map { gradientColorValue in @@ -20,7 +20,7 @@ extension DesignTokensTokenValueExtractor { case .value(let color) = gradientColorValue.color, case .value(let position) = gradientColorValue.position else { - throw GradientExtractionError.unresolvedReferences + throw DesignTokensGradientValueExtractionError.unresolvedReferences } return ( color, diff --git a/Tests/SnappThemingDesignTokensSupportTests/SnappThemingDesignTokensParserTests.swift b/Tests/SnappThemingDesignTokensSupportTests/SnappThemingDesignTokensParserTests.swift index 6c8d354..a6a66ae 100644 --- a/Tests/SnappThemingDesignTokensSupportTests/SnappThemingDesignTokensParserTests.swift +++ b/Tests/SnappThemingDesignTokensSupportTests/SnappThemingDesignTokensParserTests.swift @@ -153,6 +153,22 @@ struct SnappThemingDesignTokensParserTests { """#, DesignTokensTypographyValueExtractorError.fontsEmpty ), + ( + #""" + { + "blue-to-red": { + "$type": "gradient", + "$value": [ + { + "color": "{color}", + "position": 0 + } + ] + } + } + """#, + DesignTokensGradientValueExtractionError.unresolvedReferences + ), ] as [(String, Error)] ) func testFailingParsingDesignTokensJSONIntoSnappThemingDeclaration( From 45da4571b35e65c4b8433fbc0cf52714a4b74ef8 Mon Sep 17 00:00:00 2001 From: Voiko Volodymyr Date: Thu, 18 Dec 2025 10:28:01 +0200 Subject: [PATCH 06/11] Simplify file tokens extraction --- .../Extractor/DesignTokensFileValueExtractor.swift | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/Sources/SnappThemingDesignTokensSupport/Convertor/Extractor/DesignTokensFileValueExtractor.swift b/Sources/SnappThemingDesignTokensSupport/Convertor/Extractor/DesignTokensFileValueExtractor.swift index 6800564..734ff40 100644 --- a/Sources/SnappThemingDesignTokensSupport/Convertor/Extractor/DesignTokensFileValueExtractor.swift +++ b/Sources/SnappThemingDesignTokensSupport/Convertor/Extractor/DesignTokensFileValueExtractor.swift @@ -10,10 +10,6 @@ import SnappTheming import UniformTypeIdentifiers extension DesignTokensTokenValueExtractor { - private enum ExtractionError: Error { - case invalidData - } - static var file: Self { .init { ( @@ -26,12 +22,6 @@ extension DesignTokensTokenValueExtractor { options: .endLineWithLineFeed ) - guard - let base64EncodedData = Data(base64Encoded: base64EncodedString) - else { - throw ExtractionError.invalidData - } - guard let contentType = UTType( filenameExtension: fileValue.url.pathExtension @@ -67,7 +57,7 @@ extension DesignTokensTokenValueExtractor { source: SnappThemingDataURI( type: contentType, encoding: .base64, - data: base64EncodedData + data: data ) ) ) @@ -76,7 +66,7 @@ extension DesignTokensTokenValueExtractor { (_, _, .lotPathExtension): caches.animationCache[key] = .value( SnappThemingAnimationRepresentation( - animation: .lottie(base64EncodedData) + animation: .lottie(data) ) ) case (_, _, _): From 9ab53b5aace90703588abc9627812d5fd3f6815b Mon Sep 17 00:00:00 2001 From: Voiko Volodymyr Date: Thu, 18 Dec 2025 10:49:08 +0200 Subject: [PATCH 07/11] Test file value extraction failures --- Package.swift | 2 ++ .../DesignTokensFileValueExtractor.swift | 14 ++++++-- .../Resources/unknown | 1 + .../Resources/unsupported.extension | 0 .../SnappThemingDesignTokensParserTests.swift | 33 +++++++++++++++++++ 5 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 Tests/SnappThemingDesignTokensSupportTests/Resources/unknown create mode 100644 Tests/SnappThemingDesignTokensSupportTests/Resources/unsupported.extension diff --git a/Package.swift b/Package.swift index a5fb804..38d16ec 100644 --- a/Package.swift +++ b/Package.swift @@ -41,6 +41,8 @@ let package = Package( .copy("Resources/alien.jpg"), .copy("Resources/loading.lottie"), .copy("Resources/ArialBlack.ttf"), + .copy("Resources/unknown"), + .copy("Resources/unsupported.extension"), ] ), ] diff --git a/Sources/SnappThemingDesignTokensSupport/Convertor/Extractor/DesignTokensFileValueExtractor.swift b/Sources/SnappThemingDesignTokensSupport/Convertor/Extractor/DesignTokensFileValueExtractor.swift index 734ff40..8eb6e62 100644 --- a/Sources/SnappThemingDesignTokensSupport/Convertor/Extractor/DesignTokensFileValueExtractor.swift +++ b/Sources/SnappThemingDesignTokensSupport/Convertor/Extractor/DesignTokensFileValueExtractor.swift @@ -9,6 +9,11 @@ import SnappDesignTokens import SnappTheming import UniformTypeIdentifiers +enum DesignTokensFileValueExtractorError: Error, Equatable { + case unknownFileType(URL) + case unsupportedFileType(URL, UTType) +} + extension DesignTokensTokenValueExtractor { static var file: Self { .init { @@ -27,7 +32,9 @@ extension DesignTokensTokenValueExtractor { filenameExtension: fileValue.url.pathExtension ) else { - return + throw DesignTokensFileValueExtractorError.unknownFileType( + fileValue.url + ) } switch ( @@ -70,7 +77,10 @@ extension DesignTokensTokenValueExtractor { ) ) case (_, _, _): - break + throw DesignTokensFileValueExtractorError.unsupportedFileType( + fileValue.url, + contentType + ) } } } diff --git a/Tests/SnappThemingDesignTokensSupportTests/Resources/unknown b/Tests/SnappThemingDesignTokensSupportTests/Resources/unknown new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/Tests/SnappThemingDesignTokensSupportTests/Resources/unknown @@ -0,0 +1 @@ + diff --git a/Tests/SnappThemingDesignTokensSupportTests/Resources/unsupported.extension b/Tests/SnappThemingDesignTokensSupportTests/Resources/unsupported.extension new file mode 100644 index 0000000..e69de29 diff --git a/Tests/SnappThemingDesignTokensSupportTests/SnappThemingDesignTokensParserTests.swift b/Tests/SnappThemingDesignTokensSupportTests/SnappThemingDesignTokensParserTests.swift index a6a66ae..48df5f9 100644 --- a/Tests/SnappThemingDesignTokensSupportTests/SnappThemingDesignTokensParserTests.swift +++ b/Tests/SnappThemingDesignTokensSupportTests/SnappThemingDesignTokensParserTests.swift @@ -9,6 +9,7 @@ import Foundation import SnappDesignTokens import SnappTheming import Testing +import UniformTypeIdentifiers @testable import SnappThemingDesignTokensSupport @@ -169,6 +170,35 @@ struct SnappThemingDesignTokensParserTests { """#, DesignTokensGradientValueExtractionError.unresolvedReferences ), + ( + #""" + { + "unknown": { + "$type": "file", + "$value": "unknown" + } + } + """#, + DesignTokensFileValueExtractorError.unknownFileType( + Bundle.module.resourceURL!.appendingPathComponent("unknown") + ) + ), + ( + #""" + { + "unknown": { + "$type": "file", + "$value": "unsupported.extension" + } + } + """#, + DesignTokensFileValueExtractorError.unsupportedFileType( + Bundle.module.resourceURL!.appendingPathComponent( + "unsupported.extension" + ), + UTType(filenameExtension: "extension")! + ) + ), ] as [(String, Error)] ) func testFailingParsingDesignTokensJSONIntoSnappThemingDeclaration( @@ -181,6 +211,9 @@ struct SnappThemingDesignTokensParserTests { await #expect(throws: expectedError as NSError) { try await SnappThemingParser.parse( fromDesignTokens: designTokensJSON, + tokenDecodingConfiguration: TokenDecodingConfiguration( + file: .testResources + ), tokenProcessor: .passthrough, designTokensConverterConfiguration: configuration ) From f0758c49d1f623fb580971bf6bc0a7e6f14335b4 Mon Sep 17 00:00:00 2001 From: Voiko Volodymyr Date: Thu, 18 Dec 2025 11:00:27 +0200 Subject: [PATCH 08/11] Test type mistmacth during token value extraction --- ...DesignTokensTokenValueExtractorTests.swift | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Tests/SnappThemingDesignTokensSupportTests/DesignTokensTokenValueExtractorTests.swift diff --git a/Tests/SnappThemingDesignTokensSupportTests/DesignTokensTokenValueExtractorTests.swift b/Tests/SnappThemingDesignTokensSupportTests/DesignTokensTokenValueExtractorTests.swift new file mode 100644 index 0000000..3865c42 --- /dev/null +++ b/Tests/SnappThemingDesignTokensSupportTests/DesignTokensTokenValueExtractorTests.swift @@ -0,0 +1,23 @@ +// +// DesignTokensTokenValueExtractorTests.swift +// SnappThemingDesignTokensSupport +// +// Created by Volodymyr Voiko on 18.12.2025. +// + +import Testing +import SnappDesignTokens + +@testable import SnappThemingDesignTokensSupport + +struct DesignTokensTokenValueExtractorTests { + @Test + func testTypeMistmatchErrorHandling() async throws { + var caches = SnappThemingDeclarationCaches() + let extractor = DesignTokensTokenValueExtractor.color(using: .argb) + let tokenValue = TokenValue.number(0.5) + #expect(throws: DesignTokensTokenValueExtractorError.typeMistamatch) { + try extractor.extract(tokenValue, for: "key", into: &caches) + } + } +} From b196436ca636771a02afe6426fda902d7329b062 Mon Sep 17 00:00:00 2001 From: Voiko Volodymyr Date: Thu, 18 Dec 2025 11:12:48 +0200 Subject: [PATCH 09/11] Test font family extraction failures --- .../DesignTokensFontFamilyValueExtractor.swift | 10 +++++----- .../SnappThemingDesignTokensParserTests.swift | 11 +++++++++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/Sources/SnappThemingDesignTokensSupport/Convertor/Extractor/DesignTokensFontFamilyValueExtractor.swift b/Sources/SnappThemingDesignTokensSupport/Convertor/Extractor/DesignTokensFontFamilyValueExtractor.swift index 810ad44..d0162ae 100644 --- a/Sources/SnappThemingDesignTokensSupport/Convertor/Extractor/DesignTokensFontFamilyValueExtractor.swift +++ b/Sources/SnappThemingDesignTokensSupport/Convertor/Extractor/DesignTokensFontFamilyValueExtractor.swift @@ -8,11 +8,11 @@ import Foundation import SnappDesignTokens import SnappTheming -extension DesignTokensTokenValueExtractor { - private enum DesignTokensFontFamilyExtractionError: Error { - case fontsEmpty - } +enum DesignTokensFontFamilyValueExtractionError: Error { + case fontsEmpty +} +extension DesignTokensTokenValueExtractor { static var fontFamily: Self { .init(\.fontsCache) { (value: FontFamilyValue) in /// As a [translation tool](https://tr.designtokens.org/format/#translation-tool) we @@ -21,7 +21,7 @@ extension DesignTokensTokenValueExtractor { /// we will just try to use the primary font and ignore fallbacks. /// OS will just provided system font as fallback if required font is not available. guard let fontName = value.names.first else { - throw DesignTokensFontFamilyExtractionError.fontsEmpty + throw DesignTokensFontFamilyValueExtractionError.fontsEmpty } return SnappThemingFontInformation(postScriptName: fontName, source: nil) } diff --git a/Tests/SnappThemingDesignTokensSupportTests/SnappThemingDesignTokensParserTests.swift b/Tests/SnappThemingDesignTokensSupportTests/SnappThemingDesignTokensParserTests.swift index 48df5f9..d6c10bb 100644 --- a/Tests/SnappThemingDesignTokensSupportTests/SnappThemingDesignTokensParserTests.swift +++ b/Tests/SnappThemingDesignTokensSupportTests/SnappThemingDesignTokensParserTests.swift @@ -199,6 +199,17 @@ struct SnappThemingDesignTokensParserTests { UTType(filenameExtension: "extension")! ) ), + ( + #""" + { + "empty": { + "$type": "fontFamily", + "$value": [] + } + } + """#, + DesignTokensFontFamilyValueExtractionError.fontsEmpty + ), ] as [(String, Error)] ) func testFailingParsingDesignTokensJSONIntoSnappThemingDeclaration( From b43cb331b1e0e3919249b5855da32c5fb9cae41a Mon Sep 17 00:00:00 2001 From: Voiko Volodymyr Date: Thu, 18 Dec 2025 12:12:31 +0200 Subject: [PATCH 10/11] Improve test coverage by removing unachievable path --- .../Extensions/SnappThemingParser+DesignTokens.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Sources/SnappThemingDesignTokensSupport/Extensions/SnappThemingParser+DesignTokens.swift b/Sources/SnappThemingDesignTokensSupport/Extensions/SnappThemingParser+DesignTokens.swift index bb6c1cb..e459f00 100644 --- a/Sources/SnappThemingDesignTokensSupport/Extensions/SnappThemingParser+DesignTokens.swift +++ b/Sources/SnappThemingDesignTokensSupport/Extensions/SnappThemingParser+DesignTokens.swift @@ -37,9 +37,8 @@ extension SnappThemingParser { tokenProcessor processor: TokenProcessor = .defaultDesignTokensConversionProcessor(), designTokensConverterConfiguration configuration: DesignTokensConverter.Configuration = .default ) async throws -> SnappThemingDeclaration { - guard let data = input.data(using: .utf8) else { - throw SnappThemingParserError.invalidData - } + // It is safe to force unwrap since Strings in Swift use Unicode internally. + let data = input.data(using: .utf8)! let decoder = JSONDecoder() let token: Token From dbd03fbfd995dff3279901bed1d45133e4d79b19 Mon Sep 17 00:00:00 2001 From: Voiko Volodymyr Date: Thu, 18 Dec 2025 12:37:13 +0200 Subject: [PATCH 11/11] Cover more design tokens cnvertion failures --- .../Convertor/DesignTokensConverter.swift | 16 ++++-- .../SnappThemingDesignTokensParserTests.swift | 51 +++++++++++++++++-- 2 files changed, 57 insertions(+), 10 deletions(-) diff --git a/Sources/SnappThemingDesignTokensSupport/Convertor/DesignTokensConverter.swift b/Sources/SnappThemingDesignTokensSupport/Convertor/DesignTokensConverter.swift index 1062f58..4647992 100644 --- a/Sources/SnappThemingDesignTokensSupport/Convertor/DesignTokensConverter.swift +++ b/Sources/SnappThemingDesignTokensSupport/Convertor/DesignTokensConverter.swift @@ -30,12 +30,14 @@ public struct DesignTokensConverter: Sendable { /// - token: The `Token` that could not be processed. /// - forKey: The key associated with the unsupported token. case unsupportedToken(Token, forKey: String) + case malformedDynamicColorsGroup(TokenGroup, forKey: String) /// A localized description of the error. public var errorDescription: String? { switch self { case .invalidRootToken: "Invalid root token. Must be a group." case let .unsupportedToken(_, key): "Unsupported token value type with key: \(key)." + case let .malformedDynamicColorsGroup(_, key): "Malformed dynamic colors group for key: \(key)." } } } @@ -146,7 +148,7 @@ public struct DesignTokensConverter: Sendable { ) try extractor.extract(value, for: key, into: &caches) case .group(let group): - try await extract(group, for: key, into: &caches) + try await extractDynamicColorsGroup(group, for: key, into: &caches) case .alias, .array, .unknown: throw Error.unsupportedToken(token, forKey: key) } @@ -161,16 +163,20 @@ public struct DesignTokensConverter: Sendable { /// - key: The key associated with the token group. /// - caches: The `SnappThemingDeclarationCaches` to store the dynamic color. /// - Throws: `DesignTokensConverter.Error` if color conversion fails. - private func extract( + private func extractDynamicColorsGroup( _ group: TokenGroup, for key: String, into caches: inout SnappThemingDeclarationCaches ) async throws { guard - case .value(.color(let lightColorToken)) = group[configuration.dynamicColorKeys.light], - case .value(.color(let darkColorToken)) = group[configuration.dynamicColorKeys.dark] + case .value(.color(let lightColorToken)) = group[ + configuration.dynamicColorKeys.light + ], + case .value(.color(let darkColorToken)) = group[ + configuration.dynamicColorKeys.dark + ] else { - return + throw Error.malformedDynamicColorsGroup(group, forKey: key) } let lightColorHEX = try lightColorToken.hex( diff --git a/Tests/SnappThemingDesignTokensSupportTests/SnappThemingDesignTokensParserTests.swift b/Tests/SnappThemingDesignTokensSupportTests/SnappThemingDesignTokensParserTests.swift index d6c10bb..ee4cefa 100644 --- a/Tests/SnappThemingDesignTokensSupportTests/SnappThemingDesignTokensParserTests.swift +++ b/Tests/SnappThemingDesignTokensSupportTests/SnappThemingDesignTokensParserTests.swift @@ -17,13 +17,23 @@ struct SnappThemingDesignTokensParserTests { @Test( arguments: [ ( + #""" + {"red": {"$type": "color", "$value": "#FF0000"}} + """#, #""" { - "red": { - "$type": "color", - "$value": "#FF0000" + "colors" : { + "red" : "#FF0000" } } + """# + ), + ( + #""" + { + "red": {"$type": "color", "$value": "#FF0000"}, + "unsupported": {"$type": "fontWeight", "$value": 350} + } """#, #""" { @@ -32,14 +42,19 @@ struct SnappThemingDesignTokensParserTests { } } """# - ) + ), ] as [(String, String)] ) func testSuccessfulParsingDesignTokensJSONIntoSnappThemingDeclaration( _ designTokensJSON: String, _ expectedSnappThemingJSON: String ) async throws { - let declaration = try await SnappThemingParser.parse(fromDesignTokens: designTokensJSON) + let declaration = try await SnappThemingParser.parse( + fromDesignTokens: designTokensJSON, + designTokensConverterConfiguration: DesignTokensConverter.Configuration( + unsupportedTokenHandlingStrategy: .skip + ) + ) let encodedSnappThemingData = try SnappThemingParser.encode(declaration) let encodedSnappThemingJSON = try #require(String(data: encodedSnappThemingData, encoding: .utf8)) #expect(encodedSnappThemingJSON == expectedSnappThemingJSON) @@ -53,6 +68,15 @@ struct SnappThemingDesignTokensParserTests { """#, DesignTokensConverter.Error.invalidRootToken ), + ( + #""" + {"unsupported": []} + """#, + DesignTokensConverter.Error.unsupportedToken( + .array([]), + forKey: "unsupported" + ) + ), ( #""" { @@ -210,6 +234,23 @@ struct SnappThemingDesignTokensParserTests { """#, DesignTokensFontFamilyValueExtractionError.fontsEmpty ), + ( + #""" + { + "malformed": { + "light_color": {"$type": "color", "$value": "#FFFFFF"}, + "dark_color": {"$type": "color", "$value": "#000000"} + } + } + """#, + DesignTokensConverter.Error.malformedDynamicColorsGroup( + [ + "light_color": .value(.color(.white)), + "dark_color": .value(.color(.black)), + ], + forKey: "malformed" + ) + ), ] as [(String, Error)] ) func testFailingParsingDesignTokensJSONIntoSnappThemingDeclaration(