diff options
author | Wouter Admiraal <wouter.admiraal@sonarsource.com> | 2020-04-21 17:42:30 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2020-05-04 20:03:53 +0000 |
commit | b7d68dca795cf2127efbaa8b5c2cba0ee32192ee (patch) | |
tree | f33a9307a7b07f0077782a10887e4a5699d714d7 | |
parent | 1d854eec0d3e223aee54412503fcccf50ee10f4a (diff) | |
download | sonarqube-b7d68dca795cf2127efbaa8b5c2cba0ee32192ee.tar.gz sonarqube-b7d68dca795cf2127efbaa8b5c2cba0ee32192ee.zip |
SONAR-13296 Add new Jenkins tutorial for Bitbucket Server
69 files changed, 3946 insertions, 118 deletions
diff --git a/server/sonar-web/public/images/tutorials/commit.svg b/server/sonar-web/public/images/tutorials/commit.svg new file mode 100644 index 00000000000..f60f7e5500e --- /dev/null +++ b/server/sonar-web/public/images/tutorials/commit.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="27" height="27"><g fill="none" fill-rule="evenodd" transform="rotate(-90 13.5 18.5)"><path fill="#236A97" fill-rule="nonzero" d="M23.755102 18.5408163L19.7526885 14.5v3.0306122H12v2.0204082h7.7526885v3.0306123z"/><path d="M0 0h36v36H0z"/><circle cx="18.5" cy="18.5" r="12" stroke="#236A97" stroke-width="3"/></g></svg>
\ No newline at end of file diff --git a/server/sonar-web/public/images/tutorials/jenkins.svg b/server/sonar-web/public/images/tutorials/jenkins.svg new file mode 100644 index 00000000000..3ffa20bced1 --- /dev/null +++ b/server/sonar-web/public/images/tutorials/jenkins.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="56" height="76"><path fill="#236A97" fill-rule="nonzero" d="M17.0487743 26.4874701c-.2633309-.4728576-1.091713.3613828-1.79162.7607377-2.0687996.6851018-3.9790932-1.532657-4.4992041-4.0925528-.3969505-1.9537243.6064908-3.6941751 1.7771685-4.1474922 2.1227488-.8219843 3.5884107-.2877 4.1680091 2.8376465 1.1349544.0049922 1.9983633-.1127172 2.3897069-.6850429-.0684018-1.529288-.7478783-2.8505691-.7226231-4.0184879.0659164-3.0482709 1.46717-5.1785694.4644218-8.35489055.0664461-.87419458 2.0685712-3.12052482 4.7722246-4.86588792 1.9747258-1.27479955 3.7726433-2.2680363 6.5660309-2.29279903 2.423728-.02148573 4.703752.44235801 8.5545414 2.25344223.9477408.4457359 2.8432176 2.82299404 4.1298563 4.4905583.0345611.08007787.014399.0857984-.192986.05719856-1.8308574-.32752321-2.8607584.55403556-2.7853569 1.76171361.9885263-.77163072 2.8770115-.60270742 4.1852372.7550201 3.0931417 2.8159886 1.0480218 16.8814333-1.4373276 20.0309168-2.0538241 3.9080016-4.3234108 5.2699709-8.6354801 6.2460792-1.8609157.2797775-3.8362101-.0940405-5.7140137-1.1865931-2.1407315-1.2455341-4.4144694-3.503981-6.0553618-6.1948739.0187808 2.0723495 3.7764384 6.0576603 5.9342381 7.2376698-.9367737.0237142-2.4691968-.3932156-3.1200741.7386627-5.9998256-.1512471-6.5604195-1.8928926-8.2563742-5.9540435-.3390647-.8119272-.7232751-1.9810477-.756441-2.6943729-.4627059-1.3040079 1.3941192-1.5492592 1.0254278-2.6826088zm1.4114021-18.4179207c-.7504505.44015027-1.1164727-.15117563-2.4875059.28615942-1.4904753.47543417-1.7618711.51125029-1.7438222.42310144 1.1400862-3.0500199 7.9781043-6.31313247 8.6498836-5.70269013h.0028816c.2534771.20019524.0555727.54255567-.3888558.69496208-.269705.09248869-1.768848 1.30622226-2.1944501 1.75372426-.4601907.48386846-.9379839 1.74136047-1.8381312 2.54474293zm-6.552946 9.7466289c-.2453848-3.0207694-.3236691-5.386481 1.5237371-6.8752622 1.8657474-1.5035618 4.4617843-2.02415494 4.5452929-1.6015585h-.0028736c.7186455 2.3687677-.3154615 4.9265824-.2585473 7.8946002.0184245.960804.6820579 2.3650825.4947415 3.0188752-.1190424.2205283-.3172893.2902448-.6567364.3431914-.1298428-2.3835495-2.5874789-4.2332759-5.6456142-2.7798461zm32.4737865 34.630842c1.0554108-3.1369598 4.2353252-11.7545206 4.0642596-13.2528985.040128-1.658918-2.5599197-2.6618238-5.334522-5.7627521-.2239439-.3911208.1247224-1.0979212.5605312-1.8527667 1.9562556-3.0924062 2.7360681-8.5637912 3.0284629-13.0388633 9.9973171 10.8607839 7.8274932 25.1614774 3.1482943 32.2027661-3.3426731.4176008-5.4734514 2.0281428-5.467026 1.7045145zm-3.9864971-16.3044329c.0779954-.1555206 1.260896-1.1362674 1.4862967-1.3212856.4301278.2716838 1.38033 1.2429404 2.383406 2.1319682 1.1109469.9846328 2.2727537 1.9048948 2.5593899 2.4582061.1526616.3031561.1526616.3088738.1238546.8408196-.836367 4.9767902-3.0647228 10.5983295-4.0252504 12.2770078-.2847638.4976759-1.1681317.5097189-2.4528022.5583352-1.1838459.0486209-1.5813443.1058206-1.9413954.2859939-.2246724.1143994-.2880451.1201171-.2880451.0200188 0-.1000938 4.6173036-7.6760398 4.8074125-8.0478265.2808776-.2329702-.8649593-1.0602052-.8785271-1.5129012.5691258-1.4867022.7019038-3.7086756.1826514-5.4432341-.6463905-2.1592441-1.4993073-1.6584963-1.9569909-2.2471022zM7.42243884 42.1312658c2.96225546-2.586478 6.14876306-4.4512718 9.48232206-5.8428294.5016567.0859968 1.2529385 1.2522493 2.1113377 2.1363665.9583995.987113 3.0928106 2.0244973 5.5447976 3.1401973 1.0727713 4.0182114 1.3969963 6.8164438 3.2692673 6.0230073.1814641-.0657785.3600511-.1201171.3888559-.1201171.4755212.2040689-1.4584967 1.587195-.8468409 2.5596351.927405.9815222 3.901969 2.1249765 6.2677861 3.2202814-4.3627853.216851-9.7240752.8353422-12.6450164 1.0381506-.3479927-.3262174-.5738478-1.5709715-.1526594-6.5864073 0-1.1983142-.5789648-1.1897309-.8007555.0142966-.3498102.8189554.7433036 5.0602025-.2010874 6.2322951-.7221824-.1516801-1.6541154-1.166919-1.9736252-.9986391-.1152167.1773168-.0057598.4747507.308206.8379585.4796679.6176388.3551967 1.0049462-.4579872 1.1296701-.6279317.1344181-.9015687.3317491-.9015687.6663639-.1395094.3793679 1.7505102-.2829514 3.5342674.2173543.2443345 2.3134203.7231506 6.0003224 1.2126529 9.7494889-.9334102.1562189-2.6924146-.144593-5.3689015-1.2736192-1.3569528-.5724096-2.9659282-1.5385969-4.7903192-2.4042461-.200343-6.1900766-1.0420005-13.0804379-3.98073156-19.7392074zm33.34942636-4.1568963c1.0761987.6798942 1.2887029 4.6628109.5588004 5.5525415-1.6636188-.111969-3.5862152-.3158021-4.634591-.6463406l.0144058-1.5100401c.2330021-.2415718.7888386-.3189489 1.4641343-.335567.1722553-.0263259.0618314-.1123771-.7209905-.256435-.3190395-.0503213-.3024234-.0644636-.5357543-.0228799-.296683.0571997-.3686937.0085833-.4464653-.2659706-.2473759-.6561483-.3081684-.5911451-.0748878-.6806605 1.7038609-1.0030418 3.0355483-2.2834828 4.3753484-1.8346478zm-5.570723 1.5515014c-2.185086.1358646-2.8748468-.4421992-3.5372803-.9069699-.1847635-.1519839-.0382396-.2665147.2881729-.1941026 1.500481.5652363 3.0031449-.2086077 5.104091-.5147882.1092616.0026072.1216968.0972554-.0086357.1887613l-1.8463479 1.4270994zm-10.7900283.366069c-.0288047.2345164-.0432083.2516741-.1584226.2516741-2.0120338-.3476962-3.7995849-1.358682-3.5745951-1.9418921.0921728-.2950715.0477019-.2362622.6610565.0114399 1.2486549.4270956 3.4091261.2227999 3.2937519.5605479l-.2217907 1.1182302zm1.5928594-1.3184178c.528331-.508549 1.3135619-.2434308 3.3405929.4603 2.1563543.7486335 2.3527608 1.4729544 3.6588196 1.62458.0561572.2824799.1364588.3496232-.1526616.5033482-.6745057.2826975-.9914879.3036775-1.4488495.4375697-.2534725.0714962-.3110842.1916133-.0662497.1429924.2794002-.0571997 1.6764011.3346103 1.8204201.5090659.0483778.1974263.0158878.6200102-.0057632 1.0124142-1.0173949.6903864-1.5958346 1.4047092-2.4893247 1.9470792-1.6727499 1.0154068-3.4106559 1.5628416-3.8879125 1.1702382-1.2952561-2.9770501-1.932367-5.7144963-.7690714-7.8075878zm8.583643 4.0925506c-.3825812 0-.5086758.0036728-.592898-.1884484l-.3086707-1.4931862c-.0375089-.2543493.0086426-.3002905.1353811-.3632124.8101859-.491804 1.505968-.2981457 1.7599291.2945727.3145116.7340378.0616898 1.7664571-.9937415 1.7502743zm2.6931749 9.5121068c-1.4713976-2.4595278-2.2944343-5.4311004-4.0585054-7.8190368l.4925506-.6492017c1.0905427.2081633 1.2475496-.429535 2.1902373-.3751193.5379942.0310551 1.0523054.9250978 2.2427228 1.0128856-.1457864 3.9609981-.3897989 6.9955018-.8670053 7.8304722zm1.6090782-7.7096341c1.3227297-.7345955 2.8101361.0347732 3.3798098 1.1032127-1.610471 2.9352215-3.4830136 5.9623388-4.3829794 6.8766044.5539163-2.3396918.9406874-5.2615781 1.0031696-7.9798171zm-10.4749847 4.7267303c.558451-2.0553211 2.0712576-2.9049145 3.9116025-4.0982773 1.4246776 1.5691577 2.6177963 4.4670215 3.8050284 7.3986337-.2971831.24412-5.4596849-1.8637622-7.7166309-3.3003564zm23.1182041 12.895413c-1.3330231.5543042-4.0072895.5021466-4.1852372.1315615.0739037-.3542844 3.1411702-.7231918 3.1396495-.8608383-.6133912-.3748428-2.7728487-.3046524-5.4785479-.077223-.270648-2.6463211-.537597-5.3210721-.9793426-6.6521858.7334235-.8522005 3.4006662-1.4868246 4.5191437-1.9646812.815803-.3485441 2.1127077-1.3390668 3.4019951.8207101.9678298 2.8423689 2.7158877 6.8163441-.4176606 8.6026567zm-9.3987815-.1401404c-.1433729-1.4407521-.5979511-4.2829396-.9707023-7.1269364.0110378-.0756541-.1462499-.1899945 1.5642693-.1007512.9756754 1.6290689 1.2601698 4.0207551 1.2412544 6.7472193-.8914739.3505799-1.4238694.4422762-1.8348214.4804683zM24.6559484 73.4016815c-1.8657839-5.9032529-3.0647183-11.7852764-3.5861148-17.6457396 4.4640711-.8617996 10.0507435-1.5304034 15.1941792-.643475.7175837 8.3497085.7617578 15.8041862 1.3480363 18.5580554-3.4600635.9393037-7.5809416 1.0867443-12.9561007-.2688408zm12.9647364-9.9096299c-.2218158-2.9470242-.3887851-5.8165669-.3226039-8.3567048.6615704-.1614876 1.3503996-.2862705 2.2747495-.1747186.5406201 2.071853.6619904 5.487357.888204 7.8893586.0043704.0464353.0030917.1411424-.0233041.1901941-1.1417039.2140125-2.6601893.5458614-2.8170455.4518707zm2.7940109 6.8009004c-.8881082.3303344-1.7105329.5320546-2.4109114.4947649-.1643206-2.1891635-.2022245-4.4026532-.2534748-5.9429278 1.1125659-.0275274 1.9020826-.6572183 1.8549904.0600654.2541758 1.6986744.8236669 3.3178768.8093958 5.3880975zm.8612375-52.6483682c-.1872274.0486197-.2073895.0657774-.2073895.1944744 0 .4575884.3312441.8751371.7949877 1.0124141.7517909.2144932 2.0422154-.1773156 2.7219934-.8179397.4176606-.4003899.4118974-.37179.0720084-.9323357-.2765209-.4518684-.8324396-1.5557987-1.4142792-2.8084481-.1296224-.2859928-.2506-.514787-.2678805-.514787-.0230438 0-.0360042.1673074-.0360042.3675006 0 .6463429.1598679 1.2855387.5861688 2.3437103.1296179.3231715.2304333.632043.2189114.6892427-.047622.3048632-1.7124007.2862909-2.4685163.4661684zm-8.8054178-.674945c-.6048834-.6291842-.7345035-.8379596-1.2068942-1.9418898-.1785871-.4146899-.5357612-1.1697088-.5357612-1.1697088-.3586149 1.3439377 1.1471681 3.4797556 1.3221155 4.0782541-.1473871.3515298-.8972714.4688277-1.686263.3515298-1.1793841-.1753364-1.6776272-.344411-1.750074-.0969969-.1583062.3134421-.1793109.5622358.0695743.8514627.5033395.584924 2.0079877.7810739 3.0671136.5706162.5009488-.0995429.7407462-.3381674 1.0283939-.5784016.4119202-.3440245.5241525-.9569113.2809826-1.3840749-.1231057-.2162525-.3522101-.4109649-.5891875-.6807908zm2.4483473 10.2070751c3.5064365.196941 5.3796962-1.1430348 7.4055398-2.3394175.0751549-.4534056.3155985-1.0462102.4608666-1.5929808.0950567-.4575862.0576094-.5290869-.7517864-1.3184268-2.0854214-2.0276848-4.3580678-5.1621667-4.6288254-6.3947939-.1719059-.8159288-.6864408-.820938-.599127-.2087743-.0130198 2.2932077 2.1214256 4.9591893 4.0786677 7.0954765.5821227.5774902.8298593 1.3924554.146903 2.2450436-1.2912055.6016759-2.579438 1.2011618-3.3412803 1.4128052-2.725544.4427795-2.5725262.0994839-3.0445949-1.5701032 0-.0085788-.0172852-.0200187-.0489669-.0200187-.2396513.1542918-.8132822 1.5595995.3226038 2.6911899zm-18.9272026-3.7121806c.5120563-3.2591491-3.2341125-4.7353352-3.8154246-2.0384129.0684612.2191362.3203958-.6146734 1.1193578-.3667876-.4047299 1.4989357-.30906 2.6578608.2144074 3.5278289.1771645.219805.0729697-1.9784269.2378166-2.4439163.2146791-.578057.6621457-.8122356 1.0242599-.5554809.3348872.2374501.7232739 1.3000631 1.2195829 1.8767688zM37.977868 29.985162c.0058957-.6745097-.0058591-1.3032075-.1008131-1.7159567-2.5228263-.0387385-5.4137344-.0493554-7.1319052-.8579772-.0777716-.0343199-.0720107-.0042895.0489646.2216437.6669865 1.320313 3.4760563 2.5471818 7.1837537 2.3522902zm-8.3186418-.5205058c.605251.5591037 1.2321735 1.5441173 1.797381 2.2164415 1.5968598 1.9539169 5.8118059 1.4710364 7.0598944.6377641.1497801-.1229782.4925506-.6005854.4925506-.6806627-4.1099886-.0213768-7.289903.2138719-8.6412433-2.3079588-.1630898-.3833694-.2429941-.3504598-.5720645-.2768302-.3105339.0694807-.2772858.2201542-.1365182.4112461zm19.8505261 24.8374977c-.7082744-.0782477-2.9402106.3236555-3.846942 1.6425538 1.1316115.177081 2.5202484-1.1793474 3.846942-1.6425538zm.879815 2.3975761c.1807562-.0576803-1.3810242-.4067458-2.5775565-.3445675-.6858425.0356393-1.3818187.3913883-1.2303536.5447597.7647057.0825235 2.1757607-.0261355 3.8079101-.2001922zm.37445 2.0334071c-1.2844649-.396648-3.5702567-.7666799-4.297579-.0858018.204348.1373155 4.7274557.2154227 4.297579.0858018zM10.00905 17.2442234c.4131133-.5564388.4900949.7719747.3360872 1.1800536-2.26958619 2.8168025-.64580712 9.8906178 4.1429491 10.7114866-.0527059 2.1839014 1.7782406 4.0514339 1.7922055 5.0245428.0131558.9233499-2.643008 1.7219601-8.20474079 5.4054705-1.01301658.6709164-2.16982393.9905273-2.26615024 2.0049455-.14060831 1.7961928 3.26377063 7.4421808 3.76758284 14.0451018-7.74279379-6.602921-13.29829129-25.1133417.43206639-38.3716008zm11.3344206 49.95143c.7744808 2.7034618 1.5656256 5.539773 2.540527 7.8562177 4.1938386 1.2252114 8.3597722 1.0468518 12.3713795.6577851 1.7400089-.2462784 2.3531033-.8003196 2.67046-1.6274548.0736981-.1920849-.1473962-.978711-.1212927-1.8530705 1.3465269-.278195 2.5070002-.3087876 2.9177925-1.6649395.3717465-1.2272791-1.1306023-4.8176195-.5932976-6.1483841.7907772-.1828803 1.3238805-.563613 1.2385829-1.3870664.8995479-.5029854 2.2333473-.6780895 3.8107871-.6692205.1209867.2266949.2695702.4509186.4522262.6720862.1785871.2059143.1872274.2087754.679778.2945727 1.5064589.2573964 2.3908406.2226684 4.2313293.0142965 2.9667936-.3240363 3.6401873-5.0599803 2.8055191-7.278503-.4205377-1.4499839-.9678207-2.9257087-1.376841-3.7121852-.6888338-1.1635772-1.1668804-1.1161126-1.3509111-1.3699042 6.1624679-10.1759497 5.1983554-25.3322172-5.0119272-34.8939595-.1491178-6.56000758-2.9313374-10.88118462-8.5286046-14.55627608-3.0926211-2.03057428-12.370489-2.17915292-16.7556673.40250145-3.8552272 2.07365627-7.4997837 3.46897675-8.5519475 7.22703241-2.0096215 1.14435482-2.545466 3.32852382-2.6701461 5.80279012-13.67288175 11.6898471-13.14913114 31.9336859-.34277054 43.1705844.19339699 2.1792606-.08949447 4.8812579.69418034 5.3966763 1.3862439.792693 2.8089087 1.2135222 3.9831199 1.8179844 1.9821925 1.020399 3.7363561 1.9764568 6.9077238 1.8484365zm7.9065686-56.6769982c.2992861-.1857233.3606471-1.38743884-1.4839585-1.54769376-.5022504-.04363432-1.4704433.40739137-2.2695867 1.07719506-.9602103.8048006-1.3603391 1.5809877-1.5712519 2.2658242-.0901726.2927942-.1088871.5476445-.0862246.748247.0238726.2113237.2608089.4837171.3231587.4775267.457599-.0454344.9477407-3.6401765 5.087863-3.0210992zm9.4623346 17.6904915c-.043206.0714985-.0518462 1.2183285-.0086449 1.4757226.0288071.1773168.0374473.1801733.2304333.1801733 2.3590241-.1859341 3.7712802-1.9131607 4.6201829-3.6149456-4.1535211 2.3084802-4.3352318 1.6143485-4.8419713 1.9590497zm-1.955767 17.3298377c0-.4171746-.4541626-.6793274-.8155312-.4707378-.3613687.2085896-.3613664.7328906 0 .9414802.3613686.2085895.8155312-.0535633.8155312-.4707424zm.5558502 2.590917c0-.4113662-.4514727-.6698689-.8107019-.4641858-.3592291.2056831-.3592291.722684 0 .9283671.3592292.2056831.8107019-.0528151.8107019-.4641813z"/></svg>
\ No newline at end of file diff --git a/server/sonar-web/public/images/tutorials/refresh.svg b/server/sonar-web/public/images/tutorials/refresh.svg new file mode 100644 index 00000000000..04579ae7e54 --- /dev/null +++ b/server/sonar-web/public/images/tutorials/refresh.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28"><g fill="none" fill-rule="evenodd" transform="translate(-4 -4)"><path d="M0 0h36v36H0z"/><path fill="#236A97" fill-rule="nonzero" d="M31.3333333 15.2148148H21.28821l4.0595336-4.1777778c-4.0447178-3.99999996-10.5933086-4.14814811-14.6380264-.1481481-4.04471779 4.0148148-4.04471779 10.4888889 0 14.5037037 4.0447178 4.0148148 10.5933086 4.0148148 14.6380264 0 2.014951-1.9851852 3.0224265-4.3111111 3.0224265-7.2444445h2.9631632c0 2.9333334-1.3037918 6.7407408-3.9113754 9.3185186-5.2003515 5.1555555-13.6453667 5.1555555-18.84571821 0-5.18553566-5.1407408-5.2299831-13.4962963-.02963163-18.63703707 5.20035144-5.14074074 13.54165594-5.14074074 18.74200744 0l4.0447178-4.16296296V15.2148148zm-12.5934437-3.1407407v6.2962963l5.1855356 3.0814815-1.0667387 1.7925925-6.3411693-3.7629629v-7.4074074h2.2223724z"/></g></svg>
\ No newline at end of file diff --git a/server/sonar-web/src/main/js/api/alm-settings.ts b/server/sonar-web/src/main/js/api/alm-settings.ts index 5b70298a7c4..56691948c8c 100644 --- a/server/sonar-web/src/main/js/api/alm-settings.ts +++ b/server/sonar-web/src/main/js/api/alm-settings.ts @@ -34,7 +34,11 @@ import { } from '../types/alm-settings'; export function getAlmDefinitions(): Promise<AlmSettingsBindingDefinitions> { - return getJSON('/api/alm_settings/list_definitions').catch(throwGlobalError); + return getAlmDefinitionsNoCatch().catch(throwGlobalError); +} + +export function getAlmDefinitionsNoCatch(): Promise<AlmSettingsBindingDefinitions> { + return getJSON('/api/alm_settings/list_definitions'); } export function getAlmSettings(project?: string): Promise<AlmSettingsInstance[]> { diff --git a/server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx b/server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx index c60873f126c..278ab8ea5cb 100644 --- a/server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx +++ b/server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx @@ -22,7 +22,7 @@ import { FormattedMessage } from 'react-intl'; import { connect } from 'react-redux'; import { Alert } from 'sonar-ui-common/components/ui/Alert'; import { translate } from 'sonar-ui-common/helpers/l10n'; -import AnalyzeTutorial from '../../../components/tutorials/manual/AnalyzeTutorial'; +import TutorialSelection from '../../../components/tutorials/TutorialSelection'; import { getBranchLikeDisplayName, isBranch, isMainBranch } from '../../../helpers/branch-like'; import { isLoggedIn } from '../../../helpers/users'; import { getCurrentUser, Store } from '../../../store/rootReducer'; @@ -87,7 +87,7 @@ export function EmptyOverview(props: Props) { {isLoggedIn(currentUser) ? ( <> {showWarning && <Alert variant="warning">{warning}</Alert>} - {showTutorial && <AnalyzeTutorial component={component} currentUser={currentUser} />} + {showTutorial && <TutorialSelection component={component} currentUser={currentUser} />} </> ) : ( <Alert variant="warning">{warning}</Alert> diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/EmptyOverview-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/EmptyOverview-test.tsx.snap index b26d0feb600..7dc937d251d 100644 --- a/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/EmptyOverview-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/EmptyOverview-test.tsx.snap @@ -4,7 +4,7 @@ exports[`renders correctly 1`] = ` <div className="page page-limited" > - <AnalyzeTutorial + <withRouter(TutorialSelection) component={ Object { "breadcrumbs": Array [], diff --git a/server/sonar-web/src/main/js/components/common/CodeSnippet.css b/server/sonar-web/src/main/js/components/common/CodeSnippet.css index 23ff4f0b9e3..ae7c34ae69c 100644 --- a/server/sonar-web/src/main/js/components/common/CodeSnippet.css +++ b/server/sonar-web/src/main/js/components/common/CodeSnippet.css @@ -40,6 +40,7 @@ font-size: 11px; font-weight: normal; user-select: none; + background: var(--gray40); } .code-snippet > button:hover, diff --git a/server/sonar-web/src/main/js/components/tutorials/TutorialSelection.tsx b/server/sonar-web/src/main/js/components/tutorials/TutorialSelection.tsx new file mode 100644 index 00000000000..44891c3a6ff --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/TutorialSelection.tsx @@ -0,0 +1,109 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { WithRouterProps } from 'react-router'; +import { getAlmDefinitionsNoCatch, getProjectAlmBinding } from '../../api/alm-settings'; +import { AlmBindingDefinition, AlmKeys, ProjectAlmBindingResponse } from '../../types/alm-settings'; +import { withRouter } from '../hoc/withRouter'; +import './styles.css'; +import TutorialSelectionRenderer from './TutorialSelectionRenderer'; +import { TutorialModes } from './types'; + +interface Props extends Pick<WithRouterProps, 'router' | 'location'> { + component: T.Component; + currentUser: T.LoggedInUser; +} + +interface State { + almBinding?: AlmBindingDefinition; + forceManual: boolean; + loading: boolean; + projectBinding?: ProjectAlmBindingResponse; +} + +export class TutorialSelection extends React.PureComponent<Props, State> { + mounted = false; + state: State = { + forceManual: true, + loading: true + }; + + componentDidMount() { + this.mounted = true; + this.fetchAlmBindings(); + } + + fetchAlmBindings = async () => { + const { component } = this.props; + + const [almDefinitions, projectBinding] = await Promise.all([ + getAlmDefinitionsNoCatch().catch(() => undefined), + getProjectAlmBinding(component.key).catch(() => undefined) + ]); + + if (this.mounted) { + // We only support Bitbucket for now. + if (projectBinding === undefined || projectBinding.alm !== AlmKeys.Bitbucket) { + this.setState({ loading: false, forceManual: true }); + } else { + let almBinding; + if (almDefinitions !== undefined) { + almBinding = almDefinitions[projectBinding.alm].find(d => d.key === projectBinding.key); + } + this.setState({ almBinding, forceManual: false, projectBinding, loading: false }); + } + } + }; + + handleSelectTutorial = (selectedTutorial: TutorialModes) => { + const { + router, + location: { pathname, query } + } = this.props; + + router.push({ + pathname, + query: { ...query, selectedTutorial } + }); + }; + + render() { + const { component, currentUser, location } = this.props; + const { almBinding, forceManual, loading, projectBinding } = this.state; + + const selectedTutorial: TutorialModes | undefined = forceManual + ? TutorialModes.Manual + : location.query?.selectedTutorial; + + return ( + <TutorialSelectionRenderer + almBinding={almBinding} + component={component} + currentUser={currentUser} + loading={loading} + onSelectTutorial={this.handleSelectTutorial} + projectBinding={projectBinding} + selectedTutorial={selectedTutorial} + /> + ); + } +} + +export default withRouter(TutorialSelection); diff --git a/server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx b/server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx new file mode 100644 index 00000000000..ca68491326e --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx @@ -0,0 +1,101 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import * as React from 'react'; +import { translate } from 'sonar-ui-common/helpers/l10n'; +import { getBaseUrl } from 'sonar-ui-common/helpers/urls'; +import { AlmBindingDefinition, ProjectAlmBindingResponse } from '../../types/alm-settings'; +import JenkinsTutorial from './jenkins/JenkinsTutorial'; +import ManualTutorial from './manual/ManualTutorial'; +import { TutorialModes } from './types'; + +export interface TutorialSelectionRendererProps { + almBinding?: AlmBindingDefinition; + component: T.Component; + currentUser: T.LoggedInUser; + loading: boolean; + onSelectTutorial: (mode: TutorialModes) => void; + projectBinding?: ProjectAlmBindingResponse; + selectedTutorial?: TutorialModes; +} + +export default function TutorialSelectionRenderer(props: TutorialSelectionRendererProps) { + const { almBinding, component, currentUser, loading, projectBinding, selectedTutorial } = props; + + if (loading) { + return <i className="spinner" />; + } + + return ( + <> + {selectedTutorial === undefined && ( + <div className="tutorial-selection"> + <header className="spacer-top spacer-bottom padded"> + <h1 className="text-center big-spacer-bottom"> + {translate('onboarding.tutorial.choose_method')} + </h1> + </header> + + <div className="display-flex-space-around"> + <button + className="button button-huge display-flex-column tutorial-mode-jenkins" + onClick={() => props.onSelectTutorial(TutorialModes.Jenkins)} + type="button"> + <img + alt="" // Should be ignored by screen readers + height={80} + src={`${getBaseUrl()}/images/tutorials/jenkins.svg`} + /> + <div className="medium big-spacer-top"> + {translate('onboarding.tutorial.choose_method.jenkins')} + </div> + </button> + + <button + className="button button-huge display-flex-column tutorial-mode-manual" + onClick={() => props.onSelectTutorial(TutorialModes.Manual)} + type="button"> + <img + alt="" // Should be ignored by screen readers + height={80} + src={`${getBaseUrl()}/images/sonarcloud/analysis/manual.svg`} + /> + <div className="medium big-spacer-top"> + {translate('onboarding.tutorial.choose_method.manual')} + </div> + </button> + </div> + </div> + )} + + {selectedTutorial === TutorialModes.Manual && ( + <ManualTutorial component={component} currentUser={currentUser} /> + )} + + {selectedTutorial === TutorialModes.Jenkins && projectBinding !== undefined && ( + <JenkinsTutorial + almBinding={almBinding} + component={component} + projectBinding={projectBinding} + /> + )} + </> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelection-test.tsx b/server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelection-test.tsx new file mode 100644 index 00000000000..77290701736 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelection-test.tsx @@ -0,0 +1,109 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import { shallow } from 'enzyme'; +import * as React from 'react'; +import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; +import { getAlmDefinitionsNoCatch, getProjectAlmBinding } from '../../../api/alm-settings'; +import { mockBitbucketBindingDefinition } from '../../../helpers/mocks/alm-settings'; +import { + mockComponent, + mockLocation, + mockLoggedInUser, + mockRouter +} from '../../../helpers/testMocks'; +import { AlmKeys } from '../../../types/alm-settings'; +import { TutorialSelection } from '../TutorialSelection'; +import { TutorialModes } from '../types'; + +jest.mock('../../../api/alm-settings', () => ({ + getProjectAlmBinding: jest.fn().mockRejectedValue(null), + getAlmDefinitionsNoCatch: jest.fn().mockRejectedValue(null) +})); + +beforeEach(jest.clearAllMocks); + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot(); +}); + +it('should select manual if project is not bound', async () => { + const wrapper = shallowRender(); + await waitAndUpdate(wrapper); + expect(wrapper.state().forceManual).toBe(true); +}); + +it('should not select anything if project is bound to Bitbucket', async () => { + (getProjectAlmBinding as jest.Mock).mockResolvedValueOnce({ alm: AlmKeys.Bitbucket }); + const wrapper = shallowRender(); + await waitAndUpdate(wrapper); + expect(wrapper.state().forceManual).toBe(false); +}); + +it('should select manual if project is bound to any other ALM', async () => { + (getProjectAlmBinding as jest.Mock).mockResolvedValueOnce({ alm: AlmKeys.GitLab }); + const wrapper = shallowRender(); + await waitAndUpdate(wrapper); + expect(wrapper.state().forceManual).toBe(true); +}); + +it('should correctly find the global ALM binding definition', async () => { + const key = 'foo'; + const almBinding = mockBitbucketBindingDefinition({ key }); + (getProjectAlmBinding as jest.Mock).mockResolvedValueOnce({ alm: AlmKeys.Bitbucket, key }); + (getAlmDefinitionsNoCatch as jest.Mock).mockResolvedValueOnce({ + [AlmKeys.Bitbucket]: [almBinding] + }); + const wrapper = shallowRender(); + await waitAndUpdate(wrapper); + expect(wrapper.state().almBinding).toBe(almBinding); +}); + +it('should handle selection', () => { + const push = jest.fn(); + const wrapper = shallowRender({ router: mockRouter({ push }) }); + const instance = wrapper.instance(); + + instance.handleSelectTutorial(TutorialModes.Manual); + expect(push).toHaveBeenLastCalledWith( + expect.objectContaining({ + query: { selectedTutorial: TutorialModes.Manual } + }) + ); + + instance.handleSelectTutorial(TutorialModes.Jenkins); + expect(push).toHaveBeenLastCalledWith( + expect.objectContaining({ + query: { selectedTutorial: TutorialModes.Jenkins } + }) + ); +}); + +function shallowRender(props: Partial<TutorialSelection['props']> = {}) { + return shallow<TutorialSelection>( + <TutorialSelection + component={mockComponent()} + currentUser={mockLoggedInUser()} + location={mockLocation()} + router={mockRouter()} + {...props} + /> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelectionRenderer-test.tsx b/server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelectionRenderer-test.tsx new file mode 100644 index 00000000000..151db95278f --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelectionRenderer-test.tsx @@ -0,0 +1,70 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import { shallow } from 'enzyme'; +import * as React from 'react'; +import { click } from 'sonar-ui-common/helpers/testUtils'; +import { + mockBitbucketBindingDefinition, + mockProjectBitbucketBindingGet +} from '../../../helpers/mocks/alm-settings'; +import { mockComponent, mockLoggedInUser } from '../../../helpers/testMocks'; +import TutorialSelectionRenderer, { + TutorialSelectionRendererProps +} from '../TutorialSelectionRenderer'; +import { TutorialModes } from '../types'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot('selection'); + expect(shallowRender({ loading: true })).toMatchSnapshot('loading'); + expect(shallowRender({ selectedTutorial: TutorialModes.Manual })).toMatchSnapshot( + 'manual tutorial' + ); + expect( + shallowRender({ + selectedTutorial: TutorialModes.Jenkins, + projectBinding: mockProjectBitbucketBindingGet() + }) + ).toMatchSnapshot('jenkins tutorial'); +}); + +it('should allow mode selection', () => { + const onSelectTutorial = jest.fn(); + const wrapper = shallowRender({ onSelectTutorial }); + + click(wrapper.find('button.tutorial-mode-jenkins')); + expect(onSelectTutorial).toHaveBeenLastCalledWith(TutorialModes.Jenkins); + + click(wrapper.find('button.tutorial-mode-manual')); + expect(onSelectTutorial).toHaveBeenLastCalledWith(TutorialModes.Manual); +}); + +function shallowRender(props: Partial<TutorialSelectionRendererProps> = {}) { + return shallow<TutorialSelectionRendererProps>( + <TutorialSelectionRenderer + almBinding={mockBitbucketBindingDefinition()} + component={mockComponent()} + currentUser={mockLoggedInUser()} + loading={false} + onSelectTutorial={jest.fn()} + {...props} + /> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/__tests__/__snapshots__/TutorialSelection-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/__tests__/__snapshots__/TutorialSelection-test.tsx.snap new file mode 100644 index 00000000000..ecdac5be7ad --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/__tests__/__snapshots__/TutorialSelection-test.tsx.snap @@ -0,0 +1,41 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<TutorialSelectionRenderer + component={ + Object { + "breadcrumbs": Array [], + "key": "my-project", + "name": "MyProject", + "organization": "foo", + "qualifier": "TRK", + "qualityGate": Object { + "isDefault": true, + "key": "30", + "name": "Sonar way", + }, + "qualityProfiles": Array [ + Object { + "deleted": false, + "key": "my-qp", + "language": "ts", + "name": "Sonar way", + }, + ], + "tags": Array [], + } + } + currentUser={ + Object { + "groups": Array [], + "isLoggedIn": true, + "login": "luke", + "name": "Skywalker", + "scmAccounts": Array [], + } + } + loading={true} + onSelectTutorial={[Function]} + selectedTutorial="manual" +/> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/__tests__/__snapshots__/TutorialSelectionRenderer-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/__tests__/__snapshots__/TutorialSelectionRenderer-test.tsx.snap new file mode 100644 index 00000000000..b78bf9196d3 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/__tests__/__snapshots__/TutorialSelectionRenderer-test.tsx.snap @@ -0,0 +1,145 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly: jenkins tutorial 1`] = ` +<Fragment> + <JenkinsTutorial + almBinding={ + Object { + "key": "key", + "personalAccessToken": "asdf1234", + "url": "http://bbs.enterprise.com", + } + } + component={ + Object { + "breadcrumbs": Array [], + "key": "my-project", + "name": "MyProject", + "organization": "foo", + "qualifier": "TRK", + "qualityGate": Object { + "isDefault": true, + "key": "30", + "name": "Sonar way", + }, + "qualityProfiles": Array [ + Object { + "deleted": false, + "key": "my-qp", + "language": "ts", + "name": "Sonar way", + }, + ], + "tags": Array [], + } + } + projectBinding={ + Object { + "alm": "bitbucket", + "key": "foo", + "repository": "PROJECT_KEY", + "slug": "repo-slug", + } + } + /> +</Fragment> +`; + +exports[`should render correctly: loading 1`] = ` +<i + className="spinner" +/> +`; + +exports[`should render correctly: manual tutorial 1`] = ` +<Fragment> + <ManualTutorial + component={ + Object { + "breadcrumbs": Array [], + "key": "my-project", + "name": "MyProject", + "organization": "foo", + "qualifier": "TRK", + "qualityGate": Object { + "isDefault": true, + "key": "30", + "name": "Sonar way", + }, + "qualityProfiles": Array [ + Object { + "deleted": false, + "key": "my-qp", + "language": "ts", + "name": "Sonar way", + }, + ], + "tags": Array [], + } + } + currentUser={ + Object { + "groups": Array [], + "isLoggedIn": true, + "login": "luke", + "name": "Skywalker", + "scmAccounts": Array [], + } + } + /> +</Fragment> +`; + +exports[`should render correctly: selection 1`] = ` +<Fragment> + <div + className="tutorial-selection" + > + <header + className="spacer-top spacer-bottom padded" + > + <h1 + className="text-center big-spacer-bottom" + > + onboarding.tutorial.choose_method + </h1> + </header> + <div + className="display-flex-space-around" + > + <button + className="button button-huge display-flex-column tutorial-mode-jenkins" + onClick={[Function]} + type="button" + > + <img + alt="" + height={80} + src="/images/tutorials/jenkins.svg" + /> + <div + className="medium big-spacer-top" + > + onboarding.tutorial.choose_method.jenkins + </div> + </button> + <button + className="button button-huge display-flex-column tutorial-mode-manual" + onClick={[Function]} + type="button" + > + <img + alt="" + height={80} + src="/images/sonarcloud/analysis/manual.svg" + /> + <div + className="medium big-spacer-top" + > + onboarding.tutorial.choose_method.manual + </div> + </button> + </div> + </div> +</Fragment> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/components/LabelActionPair.tsx b/server/sonar-web/src/main/js/components/tutorials/components/LabelActionPair.tsx new file mode 100644 index 00000000000..b26c13f3d7d --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/components/LabelActionPair.tsx @@ -0,0 +1,33 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { translate } from 'sonar-ui-common/helpers/l10n'; + +export interface LabelActionPairProps { + translationKey: string; +} + +export default function LabelActionPair({ translationKey }: LabelActionPairProps) { + return ( + <> + <strong>{translate(translationKey, 'label')}:</strong> {translate(translationKey, 'action')} + </> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/components/LabelValuePair.tsx b/server/sonar-web/src/main/js/components/tutorials/components/LabelValuePair.tsx new file mode 100644 index 00000000000..68ab8ebf920 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/components/LabelValuePair.tsx @@ -0,0 +1,36 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { ClipboardIconButton } from 'sonar-ui-common/components/controls/clipboard'; +import { translate } from 'sonar-ui-common/helpers/l10n'; + +export interface LabelValuePairProps { + translationKey: string; + value: string; +} + +export default function LabelValuePair({ translationKey, value }: LabelValuePairProps) { + return ( + <div className="display-flex-center"> + <strong className="little-spacer-right">{translate(translationKey, 'label')}:</strong> {value} + <ClipboardIconButton className="little-spacer-left" copyValue={value} /> + </div> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/components/SentenceWithFilename.tsx b/server/sonar-web/src/main/js/components/tutorials/components/SentenceWithFilename.tsx new file mode 100644 index 00000000000..0e4c6696f8c --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/components/SentenceWithFilename.tsx @@ -0,0 +1,44 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { FormattedMessage } from 'react-intl'; +import { translate } from 'sonar-ui-common/helpers/l10n'; + +export interface SentenceWithFilenameProps { + filename: string; + translationKey: string; +} + +export default function SentenceWithFilename({ + filename, + translationKey +}: SentenceWithFilenameProps) { + return ( + <span className="markdown"> + <FormattedMessage + defaultMessage={translate(translationKey, 'sentence')} + id={`${translationKey}.sentence`} + values={{ + file: <code>{filename}</code> + }} + /> + </span> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/components/SentenceWithHighlights.tsx b/server/sonar-web/src/main/js/components/tutorials/components/SentenceWithHighlights.tsx new file mode 100644 index 00000000000..45c6a28d67e --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/components/SentenceWithHighlights.tsx @@ -0,0 +1,44 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { FormattedMessage } from 'react-intl'; +import { translate } from 'sonar-ui-common/helpers/l10n'; + +export interface SentenceWithHighlightsProps { + highlightKeys: string[]; + translationKey: string; +} + +export default function SentenceWithHighlights({ + highlightKeys, + translationKey +}: SentenceWithHighlightsProps) { + const values: T.Dict<JSX.Element> = {}; + highlightKeys.forEach(key => { + values[key] = <strong>{translate(translationKey, 'sentence', key)}</strong>; + }); + return ( + <FormattedMessage + defaultMessage={translate(translationKey, 'sentence')} + id={`${translationKey}.sentence`} + values={values} + /> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/components/Step.css b/server/sonar-web/src/main/js/components/tutorials/components/Step.css index ec206ba8afd..5e150f8b666 100644 --- a/server/sonar-web/src/main/js/components/tutorials/components/Step.css +++ b/server/sonar-web/src/main/js/components/tutorials/components/Step.css @@ -20,6 +20,11 @@ .onboarding-step { position: relative; padding-left: 34px; + margin-bottom: var(--gridSize); +} + +.onboarding-step.no-step-number { + padding-left: 0; } .onboarding-step:not(.is-open):not(.is-finished) { @@ -57,3 +62,40 @@ cursor: pointer; outline: none; } + +.onboarding-step ol.list-styled { + list-style: none; + padding-left: 0; + counter-reset: step-counter; +} + +.onboarding-step .markdown { + line-height: inherit; +} + +.onboarding-step ul.list-styled li { + margin-top: var(--gridSize); + margin-bottom: var(--gridSize); +} + +.onboarding-step ol.list-styled > li { + position: relative; + counter-increment: step-counter; + margin-bottom: calc(2 * var(--gridSize)); + padding-left: calc(4 * var(--gridSize)); +} + +.onboarding-step ol.list-styled > li::before { + content: counter(step-counter); + color: white; + background-color: var(--blue); + display: inline-flex; + border-radius: 50%; + width: 16px; + position: absolute; + left: 0; + font-size: 10px; + height: 16px; + justify-content: center; + align-items: center; +} diff --git a/server/sonar-web/src/main/js/components/tutorials/components/Step.tsx b/server/sonar-web/src/main/js/components/tutorials/components/Step.tsx index 106d8c3b6a7..dba05b1bedc 100644 --- a/server/sonar-web/src/main/js/components/tutorials/components/Step.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/components/Step.tsx @@ -24,25 +24,29 @@ import './Step.css'; interface Props { finished?: boolean; - onOpen: VoidFunction; + onOpen?: VoidFunction; open: boolean; renderForm: () => React.ReactNode; - renderResult: () => React.ReactNode; - stepNumber: number; + renderResult?: () => React.ReactNode; + stepNumber?: number; stepTitle: React.ReactNode; } export default function Step(props: Props) { + const { finished, open, stepNumber, stepTitle } = props; const className = classNames('boxed-group', 'onboarding-step', { - 'is-open': props.open, - 'is-finished': props.finished + 'is-open': open, + 'is-finished': finished, + 'no-step-number': stepNumber === undefined }); - const clickable = !props.open && props.finished; + const clickable = !open && finished && props.onOpen !== undefined; const handleClick = (event: React.MouseEvent<HTMLDivElement>) => { event.preventDefault(); - props.onOpen(); + if (props.onOpen !== undefined) { + props.onOpen(); + } }; return ( @@ -51,13 +55,13 @@ export default function Step(props: Props) { onClick={clickable ? handleClick : undefined} role={clickable ? 'button' : undefined} tabIndex={clickable ? 0 : undefined}> - <div className="onboarding-step-number">{props.stepNumber}</div> - {!props.open && props.renderResult()} + {stepNumber !== undefined && <div className="onboarding-step-number">{stepNumber}</div>} + {!open && props.renderResult && props.renderResult()} <div className="boxed-group-header"> - <h2>{props.stepTitle}</h2> + <h2>{stepTitle}</h2> </div> - {!props.open && <div className="boxed-group-inner" />} - <div className={classNames({ hidden: !props.open })}>{props.renderForm()}</div> + {!open && <div className="boxed-group-inner" />} + <div className={classNames({ hidden: !open })}>{props.renderForm()}</div> </div> ); } diff --git a/server/sonar-web/src/main/js/components/tutorials/components/__tests__/LabelActionPair-test.tsx b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/LabelActionPair-test.tsx new file mode 100644 index 00000000000..4e38e3616bf --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/LabelActionPair-test.tsx @@ -0,0 +1,31 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import { shallow } from 'enzyme'; +import * as React from 'react'; +import LabelActionPair, { LabelActionPairProps } from '../LabelActionPair'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot(); +}); + +function shallowRender(props: Partial<LabelActionPairProps> = {}) { + return shallow<LabelActionPairProps>(<LabelActionPair translationKey="foo" {...props} />); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/components/__tests__/LabelValuePair-test.tsx b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/LabelValuePair-test.tsx new file mode 100644 index 00000000000..3794e837de6 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/LabelValuePair-test.tsx @@ -0,0 +1,33 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import { shallow } from 'enzyme'; +import * as React from 'react'; +import LabelValuePair, { LabelValuePairProps } from '../LabelValuePair'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot(); +}); + +function shallowRender(props: Partial<LabelValuePairProps> = {}) { + return shallow<LabelValuePairProps>( + <LabelValuePair translationKey="foo" value="bar" {...props} /> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/components/__tests__/SentenceWithFilename-test.tsx b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/SentenceWithFilename-test.tsx new file mode 100644 index 00000000000..0271f218975 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/SentenceWithFilename-test.tsx @@ -0,0 +1,33 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import { shallow } from 'enzyme'; +import * as React from 'react'; +import SentenceWithFilename, { SentenceWithFilenameProps } from '../SentenceWithFilename'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot(); +}); + +function shallowRender(props: Partial<SentenceWithFilenameProps> = {}) { + return shallow<SentenceWithFilenameProps>( + <SentenceWithFilename filename="foo.txt" translationKey="bar" {...props} /> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/components/__tests__/SentenceWithHighlights-test.tsx b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/SentenceWithHighlights-test.tsx new file mode 100644 index 00000000000..005656b4423 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/SentenceWithHighlights-test.tsx @@ -0,0 +1,33 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import { shallow } from 'enzyme'; +import * as React from 'react'; +import SentenceWithHighlights, { SentenceWithHighlightsProps } from '../SentenceWithHighlights'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot(); +}); + +function shallowRender(props: Partial<SentenceWithHighlightsProps> = {}) { + return shallow<SentenceWithHighlightsProps>( + <SentenceWithHighlights highlightKeys={['hello', 'world']} translationKey="foo" {...props} /> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/LabelActionPair-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/LabelActionPair-test.tsx.snap new file mode 100644 index 00000000000..574d189ee6a --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/LabelActionPair-test.tsx.snap @@ -0,0 +1,12 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<Fragment> + <strong> + foo.label + : + </strong> + + foo.action +</Fragment> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/LabelValuePair-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/LabelValuePair-test.tsx.snap new file mode 100644 index 00000000000..99305242996 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/LabelValuePair-test.tsx.snap @@ -0,0 +1,20 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<div + className="display-flex-center" +> + <strong + className="little-spacer-right" + > + foo.label + : + </strong> + + bar + <ClipboardIconButton + className="little-spacer-left" + copyValue="bar" + /> +</div> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/SentenceWithFilename-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/SentenceWithFilename-test.tsx.snap new file mode 100644 index 00000000000..54c401e7633 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/SentenceWithFilename-test.tsx.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<span + className="markdown" +> + <FormattedMessage + defaultMessage="bar.sentence" + id="bar.sentence" + values={ + Object { + "file": <code> + foo.txt + </code>, + } + } + /> +</span> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/SentenceWithHighlights-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/SentenceWithHighlights-test.tsx.snap new file mode 100644 index 00000000000..dc9ab667a9d --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/SentenceWithHighlights-test.tsx.snap @@ -0,0 +1,18 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<FormattedMessage + defaultMessage="foo.sentence" + id="foo.sentence" + values={ + Object { + "hello": <strong> + foo.sentence.hello + </strong>, + "world": <strong> + foo.sentence.world + </strong>, + } + } +/> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/BitbucketWebhookStep.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/BitbucketWebhookStep.tsx new file mode 100644 index 00000000000..5ce09416424 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/BitbucketWebhookStep.tsx @@ -0,0 +1,143 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { FormattedMessage } from 'react-intl'; +import { Button, ButtonLink } from 'sonar-ui-common/components/controls/buttons'; +import { Alert } from 'sonar-ui-common/components/ui/Alert'; +import { translate } from 'sonar-ui-common/helpers/l10n'; +import { + BitbucketBindingDefinition, + ProjectBitbucketBindingResponse +} from '../../../types/alm-settings'; +import CodeSnippet from '../../common/CodeSnippet'; +import LabelActionPair from '../components/LabelActionPair'; +import SentenceWithHighlights from '../components/SentenceWithHighlights'; +import Step from '../components/Step'; + +export interface BitbucketWebhookStepProps { + almBinding?: BitbucketBindingDefinition; + finished: boolean; + onDone: () => void; + onOpen: () => void; + open: boolean; + projectBinding: ProjectBitbucketBindingResponse; +} + +export default function BitbucketWebhookStep(props: BitbucketWebhookStepProps) { + const { almBinding, finished, open, projectBinding } = props; + return ( + <Step + finished={finished} + onOpen={props.onOpen} + open={open} + renderForm={() => ( + <div className="boxed-group-inner"> + <p className="big-spacer-bottom"> + <FormattedMessage + defaultMessage={translate( + 'onboarding.tutorial.with.jenkins.bitbucket_webhook.intro.sentence' + )} + id="onboarding.tutorial.with.jenkins.bitbucket_webhook.intro.sentence" + values={{ + link: ( + <ButtonLink onClick={props.onDone}> + {translate('onboarding.tutorial.with.jenkins.bitbucket_webhook.intro.link')} + </ButtonLink> + ) + }} + /> + </p> + <ol className="list-styled"> + <li> + <FormattedMessage + defaultMessage={translate( + 'onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.sentence' + )} + id="onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.sentence" + values={{ + link: + almBinding !== undefined ? ( + <a + href={`${almBinding.url.replace( + /\/$/, + '' + )}/plugins/servlet/webhooks/projects/${projectBinding.repository}/repos/${ + projectBinding.slug + }/create`} + rel="noopener noreferrer" + target="_blank"> + {translate('onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.link')} + </a> + ) : ( + translate('onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.link') + ) + }} + /> + <ul className="list-styled"> + <li> + <LabelActionPair translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.name" /> + </li> + <li className="abs-width-600"> + <p> + <LabelActionPair translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.url" /> + </p> + <CodeSnippet + isOneLine={true} + snippet={`***JENKINS_URL***/bitbucket-scmsource-hook/notify?server_url=${ + almBinding !== undefined ? almBinding.url : '***BITBUCKET_URL***' + }`} + /> + <Alert variant="info"> + {translate( + 'onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.url.warning' + )} + </Alert> + </li> + </ul> + </li> + <li> + <SentenceWithHighlights + highlightKeys={['events']} + translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step2" + /> + <ul className="list-styled"> + <li> + <LabelActionPair translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step2.repo" /> + </li> + <li> + <LabelActionPair translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step2.pr" /> + </li> + </ul> + </li> + <li> + <SentenceWithHighlights + highlightKeys={['create']} + translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step3" + /> + </li> + </ol> + <Button onClick={props.onDone}>{translate('continue')}</Button> + </div> + )} + stepNumber={2} + stepTitle={translate('onboarding.tutorial.with.jenkins.bitbucket_webhook.title')} + /> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx new file mode 100644 index 00000000000..47d994cc6aa --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx @@ -0,0 +1,89 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { Alert } from 'sonar-ui-common/components/ui/Alert'; +import { translate } from 'sonar-ui-common/helpers/l10n'; +import { + isBitbucketBindingDefinition, + isProjectBitbucketBindingResponse +} from '../../../helpers/alm-settings'; +import { AlmBindingDefinition, ProjectAlmBindingResponse } from '../../../types/alm-settings'; +import BitbucketWebhookStep from './BitbucketWebhookStep'; +import JenkinsfileStep from './JenkinsfileStep'; +import MultiBranchPipelineStep from './MultiBranchPipelineStep'; +import PreRequisitesStep from './PreRequisitesStep'; + +export interface JenkinsTutorialProps { + almBinding?: AlmBindingDefinition; + component: T.Component; + projectBinding: ProjectAlmBindingResponse; +} + +enum Steps { + PreRequisites = 0, + MultiBranchPipeline = 1, + BitbucketWebhook = 2, + Jenkinsfile = 3 +} + +export default function JenkinsTutorial(props: JenkinsTutorialProps) { + const { almBinding, component, projectBinding } = props; + const [step, setStep] = React.useState(Steps.PreRequisites); + + // Failsafe; should never happen. + if (!isProjectBitbucketBindingResponse(projectBinding)) { + return ( + <Alert variant="error">{translate('onboarding.tutorial.with.jenkins.only_bitbucket')}</Alert> + ); + } + + return ( + <> + <div className="page-header big-spacer-bottom"> + <h1 className="page-title">{translate('onboarding.tutorial.with.jenkins.title')}</h1> + </div> + + <PreRequisitesStep + onDone={() => setStep(Steps.MultiBranchPipeline)} + onOpen={() => setStep(Steps.PreRequisites)} + open={step === Steps.PreRequisites} + /> + + <MultiBranchPipelineStep + finished={step > Steps.MultiBranchPipeline} + onDone={() => setStep(Steps.BitbucketWebhook)} + onOpen={() => setStep(Steps.MultiBranchPipeline)} + open={step === Steps.MultiBranchPipeline} + projectBinding={projectBinding} + /> + + <BitbucketWebhookStep + almBinding={almBinding && isBitbucketBindingDefinition(almBinding) ? almBinding : undefined} + finished={step > Steps.BitbucketWebhook} + onDone={() => setStep(Steps.Jenkinsfile)} + onOpen={() => setStep(Steps.BitbucketWebhook)} + open={step === Steps.BitbucketWebhook} + projectBinding={projectBinding} + /> + + <JenkinsfileStep component={component} open={step === Steps.Jenkinsfile} /> + </> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsfileStep.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsfileStep.tsx new file mode 100644 index 00000000000..7873a2e9f51 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsfileStep.tsx @@ -0,0 +1,126 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { translate } from 'sonar-ui-common/helpers/l10n'; +import { getBaseUrl } from 'sonar-ui-common/helpers/urls'; +import RenderOptions from '../components/RenderOptions'; +import SentenceWithHighlights from '../components/SentenceWithHighlights'; +import Step from '../components/Step'; +import Gradle from './buildtool-steps/Gradle'; +import Maven from './buildtool-steps/Maven'; +import MSBuild from './buildtool-steps/MSBuild'; +import Other from './buildtool-steps/Other'; + +export interface JenkinsfileStepProps { + component: T.Component; + open: boolean; +} + +export enum BuildTools { + Maven = 'maven', + Gradle = 'gradle', + MSBuild = 'msbuild', + Other = 'other' +} + +const BUILDTOOL_COMPONENT_MAP: { + [x in BuildTools]: React.ComponentType<{ component: T.Component }>; +} = { + [BuildTools.Maven]: Maven, + [BuildTools.Gradle]: Gradle, + [BuildTools.MSBuild]: MSBuild, + [BuildTools.Other]: Other +}; + +export default function JenkinsfileStep(props: JenkinsfileStepProps) { + const { component, open } = props; + const [buildTool, setBuildTool] = React.useState<BuildTools | undefined>(undefined); + return ( + <Step + finished={false} + open={open} + renderForm={() => ( + <div className="boxed-group-inner"> + <ol className="list-styled"> + <li> + {translate('onboarding.build')} + <RenderOptions + checked={buildTool} + name="buildtool" + onCheck={value => setBuildTool(value as BuildTools)} + optionLabelKey="onboarding.build" + options={Object.values(BuildTools)} + /> + </li> + {buildTool !== undefined && + React.createElement(BUILDTOOL_COMPONENT_MAP[buildTool], { component })} + </ol> + {buildTool !== undefined && ( + <> + <hr className="huge-spacer-top huge-spacer-bottom" /> + <div className="abs-width-600"> + <p className="big-spacer-bottom"> + <SentenceWithHighlights + highlightKeys={['all_set']} + translationKey="onboarding.tutorial.with.jenkins.all_set" + /> + </p> + <div className="display-flex-row big-spacer-bottom"> + <div> + <img + alt="" // Should be ignored by screen readers + className="big-spacer-right" + width={30} + src={`${getBaseUrl()}/images/tutorials/commit.svg`} + /> + </div> + <div> + <p className="little-spacer-bottom"> + <strong>{translate('onboarding.tutorial.with.jenkins.commit')}</strong> + </p> + <p>{translate('onboarding.tutorial.with.jenkins.commit.why')}</p> + </div> + </div> + <div className="display-flex-row huge-spacer-bottom"> + <div> + <img + alt="" // Should be ignored by screen readers + className="big-spacer-right" + width={30} + src={`${getBaseUrl()}/images/tutorials/refresh.svg`} + /> + </div> + <div> + <p className="little-spacer-bottom"> + <strong>{translate('onboarding.tutorial.with.jenkins.refresh')}</strong> + </p> + <p>{translate('onboarding.tutorial.with.jenkins.refresh.why')}</p> + </div> + </div> + </div> + </> + )} + </div> + )} + stepNumber={3} + stepTitle={translate('onboarding.tutorial.with.jenkins.jenkinsfile.title')} + /> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/MultiBranchPipelineStep.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/MultiBranchPipelineStep.tsx new file mode 100644 index 00000000000..6deaaca4ac2 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/MultiBranchPipelineStep.tsx @@ -0,0 +1,113 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { Button } from 'sonar-ui-common/components/controls/buttons'; +import { translate } from 'sonar-ui-common/helpers/l10n'; +import { ProjectBitbucketBindingResponse } from '../../../types/alm-settings'; +import LabelActionPair from '../components/LabelActionPair'; +import LabelValuePair from '../components/LabelValuePair'; +import SentenceWithHighlights from '../components/SentenceWithHighlights'; +import Step from '../components/Step'; + +export interface MultiBranchPipelineStepProps { + finished: boolean; + onDone: () => void; + onOpen: () => void; + open: boolean; + projectBinding: ProjectBitbucketBindingResponse; +} + +export default function MultiBranchPipelineStep(props: MultiBranchPipelineStepProps) { + const { finished, open, projectBinding } = props; + return ( + <Step + finished={finished} + onOpen={props.onOpen} + open={open} + renderForm={() => ( + <div className="boxed-group-inner"> + <p className="big-spacer-bottom"> + {translate('onboarding.tutorial.with.jenkins.multi_branch_pipeline.intro')} + </p> + <ol className="list-styled"> + <li> + <SentenceWithHighlights + highlightKeys={['new_item', 'type']} + translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step1" + /> + </li> + <li> + <SentenceWithHighlights + highlightKeys={['tab']} + translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2" + /> + <ul className="list-styled"> + <li> + <LabelActionPair translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.server" /> + </li> + <li> + <LabelActionPair translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.creds" /> + </li> + <li> + <LabelValuePair + translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.owner" + value={projectBinding.repository} + /> + </li> + <li> + <LabelValuePair + translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.repo" + value={projectBinding.slug} + /> + </li> + <li> + <LabelActionPair translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.behaviour" /> + </li> + </ul> + </li> + <li> + <SentenceWithHighlights + highlightKeys={['tab']} + translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step3" + /> + <ul className="list-styled"> + <li> + <LabelActionPair translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step3.mode" /> + </li> + <li> + <LabelActionPair translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step3.script_path" /> + </li> + </ul> + </li> + <li> + <SentenceWithHighlights + highlightKeys={['save']} + translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step4" + /> + </li> + </ol> + <Button onClick={props.onDone}>{translate('continue')}</Button> + </div> + )} + stepNumber={1} + stepTitle={translate('onboarding.tutorial.with.jenkins.multi_branch_pipeline.title')} + /> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/PreRequisitesStep.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/PreRequisitesStep.tsx new file mode 100644 index 00000000000..5dbe039be20 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/PreRequisitesStep.tsx @@ -0,0 +1,79 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { FormattedMessage } from 'react-intl'; +import { Link } from 'react-router'; +import { Button } from 'sonar-ui-common/components/controls/buttons'; +import { translate } from 'sonar-ui-common/helpers/l10n'; +import SentenceWithHighlights from '../components/SentenceWithHighlights'; +import Step from '../components/Step'; + +export interface PreRequisitesStepProps { + onDone: () => void; + onOpen: () => void; + open: boolean; +} + +export default function PreRequisitesStep(props: PreRequisitesStepProps) { + const { open } = props; + return ( + <Step + finished={!open} + onOpen={props.onOpen} + open={open} + renderForm={() => ( + <div className="boxed-group-inner"> + <p className="big-spacer-bottom"> + <SentenceWithHighlights + highlightKeys={['must_have']} + translationKey="onboarding.tutorial.with.jenkins.prereqs.intro" + /> + </p> + <ul className="list-styled big-spacer-bottom"> + <li>{translate('onboarding.tutorial.with.jenkins.prereqs.plugins.branch_source')}</li> + <li>{translate('onboarding.tutorial.with.jenkins.prereqs.plugins.sonar_scanner')}</li> + </ul> + <p className="big-spacer-bottom"> + <FormattedMessage + defaultMessage={translate( + 'onboarding.tutorial.with.jenkins.prereqs.step_by_step_guide' + )} + id="onboarding.tutorial.with.jenkins.prereqs.step_by_step_guide" + values={{ + link: ( + <Link target="_blank" to="/documentation/analysis/jenkins/"> + {translate('onboarding.tutorial.with.jenkins.prereqs.step_by_step_guide.link')} + </Link> + ) + }} + /> + </p> + <p className="big-spacer-bottom"> + {translate('onboarding.tutorial.with.jenkins.prereqs.following_are_recommendations')} + </p> + <Button onClick={props.onDone}> + {translate('onboarding.tutorial.with.jenkins.prereqs.done')} + </Button> + </div> + )} + stepTitle={translate('onboarding.tutorial.with.jenkins.prereqs.title')} + /> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/BitbucketWebhookStep-test.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/BitbucketWebhookStep-test.tsx new file mode 100644 index 00000000000..6b9ed13c361 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/BitbucketWebhookStep-test.tsx @@ -0,0 +1,51 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import { shallow } from 'enzyme'; +import * as React from 'react'; +import { + mockBitbucketBindingDefinition, + mockProjectBitbucketBindingGet +} from '../../../../helpers/mocks/alm-settings'; +import BitbucketWebhookStep, { BitbucketWebhookStepProps } from '../BitbucketWebhookStep'; +import { renderStepContent } from '../test-utils'; + +it('should render correctly', () => { + const wrapper = shallowRender(); + expect(wrapper).toMatchSnapshot('Step wrapper'); + expect(renderStepContent(wrapper)).toMatchSnapshot('content'); + expect(renderStepContent(wrapper.setProps({ almBinding: undefined }))).toMatchSnapshot( + 'no alm binding' + ); +}); + +function shallowRender(props: Partial<BitbucketWebhookStepProps> = {}) { + return shallow<BitbucketWebhookStepProps>( + <BitbucketWebhookStep + almBinding={mockBitbucketBindingDefinition()} + finished={false} + onDone={jest.fn()} + onOpen={jest.fn()} + open={true} + projectBinding={mockProjectBitbucketBindingGet()} + {...props} + /> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsTutorial-test.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsTutorial-test.tsx new file mode 100644 index 00000000000..495218b8a49 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsTutorial-test.tsx @@ -0,0 +1,91 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import { shallow } from 'enzyme'; +import * as React from 'react'; +import { + mockProjectAlmBindingResponse, + mockProjectBitbucketBindingGet +} from '../../../../helpers/mocks/alm-settings'; +import { mockComponent } from '../../../../helpers/testMocks'; +import BitbucketWebhookStep from '../BitbucketWebhookStep'; +import JenkinsfileStep from '../JenkinsfileStep'; +import JenkinsTutorial, { JenkinsTutorialProps } from '../JenkinsTutorial'; +import MultiBranchPipelineStep from '../MultiBranchPipelineStep'; +import PreRequisitesStep from '../PreRequisitesStep'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot('default'); + expect(shallowRender({ projectBinding: mockProjectAlmBindingResponse() })).toMatchSnapshot( + 'not Bitbucket binding' + ); +}); + +it('should correctly navigate between steps', () => { + const wrapper = shallowRender(); + + expect(wrapper.find(PreRequisitesStep).prop('open')).toBe(true); + expect(wrapper.find(MultiBranchPipelineStep).prop('open')).toBe(false); + expect(wrapper.find(BitbucketWebhookStep).prop('open')).toBe(false); + expect(wrapper.find(JenkinsfileStep).prop('open')).toBe(false); + + // Pre-reqs done. + wrapper.find(PreRequisitesStep).prop('onDone')(); + expect(wrapper.find(PreRequisitesStep).prop('open')).toBe(false); + expect(wrapper.find(MultiBranchPipelineStep).prop('open')).toBe(true); + expect(wrapper.find(BitbucketWebhookStep).prop('open')).toBe(false); + expect(wrapper.find(JenkinsfileStep).prop('open')).toBe(false); + + // Multibranch done. + wrapper.find(MultiBranchPipelineStep).prop('onDone')(); + expect(wrapper.find(PreRequisitesStep).prop('open')).toBe(false); + expect(wrapper.find(MultiBranchPipelineStep).prop('open')).toBe(false); + expect(wrapper.find(BitbucketWebhookStep).prop('open')).toBe(true); + expect(wrapper.find(JenkinsfileStep).prop('open')).toBe(false); + + // Webhook done. + wrapper.find(BitbucketWebhookStep).prop('onDone')(); + expect(wrapper.find(PreRequisitesStep).prop('open')).toBe(false); + expect(wrapper.find(MultiBranchPipelineStep).prop('open')).toBe(false); + expect(wrapper.find(BitbucketWebhookStep).prop('open')).toBe(false); + expect(wrapper.find(JenkinsfileStep).prop('open')).toBe(true); + + // Open Pre-reqs. + wrapper.find(PreRequisitesStep).prop('onOpen')(); + expect(wrapper.find(PreRequisitesStep).prop('open')).toBe(true); + + // Open Multibranch. + wrapper.find(MultiBranchPipelineStep).prop('onOpen')(); + expect(wrapper.find(MultiBranchPipelineStep).prop('open')).toBe(true); + + // Open Webhook. + wrapper.find(BitbucketWebhookStep).prop('onOpen')(); + expect(wrapper.find(BitbucketWebhookStep).prop('open')).toBe(true); +}); + +function shallowRender(props: Partial<JenkinsTutorialProps> = {}) { + return shallow<JenkinsTutorialProps>( + <JenkinsTutorial + component={mockComponent()} + projectBinding={mockProjectBitbucketBindingGet()} + {...props} + /> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsfileStep-test.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsfileStep-test.tsx new file mode 100644 index 00000000000..d3dc2e7fbe1 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsfileStep-test.tsx @@ -0,0 +1,73 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import { shallow, ShallowWrapper } from 'enzyme'; +import * as React from 'react'; +import { mockComponent } from '../../../../helpers/testMocks'; +import RenderOptions from '../../components/RenderOptions'; +import Step from '../../components/Step'; +import JenkinsfileStep, { BuildTools, JenkinsfileStepProps } from '../JenkinsfileStep'; +import { renderStepContent } from '../test-utils'; + +it('should render correctly', () => { + const wrapper = shallowRender(); + expect(wrapper).toMatchSnapshot('Step wrapper'); + expect(renderStepContent(wrapper)).toMatchSnapshot('initial content'); +}); + +it('should render correctly for Maven', () => { + const wrapper = shallowRender(); + selectBuildTool(wrapper, BuildTools.Maven); + expect( + wrapper + .find(Step) + .props() + .renderForm() + ).toMatchSnapshot(); +}); + +it('should render correctly for Gradle', () => { + const wrapper = shallowRender(); + selectBuildTool(wrapper, BuildTools.Gradle); + expect(renderStepContent(wrapper)).toMatchSnapshot(); +}); + +it('should render correctly for MSBuild', () => { + const wrapper = shallowRender(); + selectBuildTool(wrapper, BuildTools.MSBuild); + expect(renderStepContent(wrapper)).toMatchSnapshot(); +}); + +it('should render correctly for Other', () => { + const wrapper = shallowRender(); + selectBuildTool(wrapper, BuildTools.Other); + expect(renderStepContent(wrapper)).toMatchSnapshot(); +}); + +function selectBuildTool(wrapper: ShallowWrapper<JenkinsfileStepProps>, tool: BuildTools) { + const content = new ShallowWrapper(renderStepContent(wrapper) as JSX.Element); + content.find(RenderOptions).prop('onCheck')(tool); +} + +function shallowRender(props: Partial<JenkinsfileStepProps> = {}) { + return shallow<JenkinsfileStepProps>( + <JenkinsfileStep component={mockComponent()} open={true} {...props} /> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/MultiBranchPipelineStep-test.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/MultiBranchPipelineStep-test.tsx new file mode 100644 index 00000000000..85f5e7e7e15 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/MultiBranchPipelineStep-test.tsx @@ -0,0 +1,44 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import { shallow } from 'enzyme'; +import * as React from 'react'; +import { mockProjectBitbucketBindingGet } from '../../../../helpers/mocks/alm-settings'; +import MultiBranchPipelineStep, { MultiBranchPipelineStepProps } from '../MultiBranchPipelineStep'; +import { renderStepContent } from '../test-utils'; + +it('should render correctly', () => { + const wrapper = shallowRender(); + expect(wrapper).toMatchSnapshot('Step wrapper'); + expect(renderStepContent(wrapper)).toMatchSnapshot('content'); +}); + +function shallowRender(props: Partial<MultiBranchPipelineStepProps> = {}) { + return shallow<MultiBranchPipelineStepProps>( + <MultiBranchPipelineStep + finished={false} + onDone={jest.fn()} + onOpen={jest.fn()} + open={true} + projectBinding={mockProjectBitbucketBindingGet()} + {...props} + /> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/PreRequisitesStep-test.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/PreRequisitesStep-test.tsx new file mode 100644 index 00000000000..fcfcda72e88 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/PreRequisitesStep-test.tsx @@ -0,0 +1,36 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import { shallow } from 'enzyme'; +import * as React from 'react'; +import PreRequisitesStep, { PreRequisitesStepProps } from '../PreRequisitesStep'; +import { renderStepContent } from '../test-utils'; + +it('should render correctly', () => { + const wrapper = shallowRender(); + expect(wrapper).toMatchSnapshot('Step wrapper'); + expect(renderStepContent(wrapper)).toMatchSnapshot('content'); +}); + +function shallowRender(props: Partial<PreRequisitesStepProps> = {}) { + return shallow<PreRequisitesStepProps>( + <PreRequisitesStep onDone={jest.fn()} onOpen={jest.fn()} open={false} {...props} /> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/BitbucketWebhookStep-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/BitbucketWebhookStep-test.tsx.snap new file mode 100644 index 00000000000..c736b265b92 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/BitbucketWebhookStep-test.tsx.snap @@ -0,0 +1,228 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly: Step wrapper 1`] = ` +<Step + finished={false} + onOpen={[MockFunction]} + open={true} + renderForm={[Function]} + stepNumber={2} + stepTitle="onboarding.tutorial.with.jenkins.bitbucket_webhook.title" +/> +`; + +exports[`should render correctly: content 1`] = ` +<div + className="boxed-group-inner" +> + <p + className="big-spacer-bottom" + > + <FormattedMessage + defaultMessage="onboarding.tutorial.with.jenkins.bitbucket_webhook.intro.sentence" + id="onboarding.tutorial.with.jenkins.bitbucket_webhook.intro.sentence" + values={ + Object { + "link": <ButtonLink + onClick={[MockFunction]} + > + onboarding.tutorial.with.jenkins.bitbucket_webhook.intro.link + </ButtonLink>, + } + } + /> + </p> + <ol + className="list-styled" + > + <li> + <FormattedMessage + defaultMessage="onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.sentence" + id="onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.sentence" + values={ + Object { + "link": <a + href="http://bbs.enterprise.com/plugins/servlet/webhooks/projects/PROJECT_KEY/repos/repo-slug/create" + rel="noopener noreferrer" + target="_blank" + > + onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.link + </a>, + } + } + /> + <ul + className="list-styled" + > + <li> + <LabelActionPair + translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.name" + /> + </li> + <li + className="abs-width-600" + > + <p> + <LabelActionPair + translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.url" + /> + </p> + <CodeSnippet + isOneLine={true} + snippet="***JENKINS_URL***/bitbucket-scmsource-hook/notify?server_url=http://bbs.enterprise.com" + /> + <Alert + variant="info" + > + onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.url.warning + </Alert> + </li> + </ul> + </li> + <li> + <SentenceWithHighlights + highlightKeys={ + Array [ + "events", + ] + } + translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step2" + /> + <ul + className="list-styled" + > + <li> + <LabelActionPair + translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step2.repo" + /> + </li> + <li> + <LabelActionPair + translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step2.pr" + /> + </li> + </ul> + </li> + <li> + <SentenceWithHighlights + highlightKeys={ + Array [ + "create", + ] + } + translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step3" + /> + </li> + </ol> + <Button + onClick={[MockFunction]} + > + continue + </Button> +</div> +`; + +exports[`should render correctly: no alm binding 1`] = ` +<div + className="boxed-group-inner" +> + <p + className="big-spacer-bottom" + > + <FormattedMessage + defaultMessage="onboarding.tutorial.with.jenkins.bitbucket_webhook.intro.sentence" + id="onboarding.tutorial.with.jenkins.bitbucket_webhook.intro.sentence" + values={ + Object { + "link": <ButtonLink + onClick={[MockFunction]} + > + onboarding.tutorial.with.jenkins.bitbucket_webhook.intro.link + </ButtonLink>, + } + } + /> + </p> + <ol + className="list-styled" + > + <li> + <FormattedMessage + defaultMessage="onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.sentence" + id="onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.sentence" + values={ + Object { + "link": "onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.link", + } + } + /> + <ul + className="list-styled" + > + <li> + <LabelActionPair + translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.name" + /> + </li> + <li + className="abs-width-600" + > + <p> + <LabelActionPair + translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.url" + /> + </p> + <CodeSnippet + isOneLine={true} + snippet="***JENKINS_URL***/bitbucket-scmsource-hook/notify?server_url=***BITBUCKET_URL***" + /> + <Alert + variant="info" + > + onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.url.warning + </Alert> + </li> + </ul> + </li> + <li> + <SentenceWithHighlights + highlightKeys={ + Array [ + "events", + ] + } + translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step2" + /> + <ul + className="list-styled" + > + <li> + <LabelActionPair + translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step2.repo" + /> + </li> + <li> + <LabelActionPair + translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step2.pr" + /> + </li> + </ul> + </li> + <li> + <SentenceWithHighlights + highlightKeys={ + Array [ + "create", + ] + } + translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step3" + /> + </li> + </ol> + <Button + onClick={[MockFunction]} + > + continue + </Button> +</div> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/JenkinsTutorial-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/JenkinsTutorial-test.tsx.snap new file mode 100644 index 00000000000..7e1ac0e500c --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/JenkinsTutorial-test.tsx.snap @@ -0,0 +1,82 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly: default 1`] = ` +<Fragment> + <div + className="page-header big-spacer-bottom" + > + <h1 + className="page-title" + > + onboarding.tutorial.with.jenkins.title + </h1> + </div> + <PreRequisitesStep + onDone={[Function]} + onOpen={[Function]} + open={true} + /> + <MultiBranchPipelineStep + finished={false} + onDone={[Function]} + onOpen={[Function]} + open={false} + projectBinding={ + Object { + "alm": "bitbucket", + "key": "foo", + "repository": "PROJECT_KEY", + "slug": "repo-slug", + } + } + /> + <BitbucketWebhookStep + finished={false} + onDone={[Function]} + onOpen={[Function]} + open={false} + projectBinding={ + Object { + "alm": "bitbucket", + "key": "foo", + "repository": "PROJECT_KEY", + "slug": "repo-slug", + } + } + /> + <JenkinsfileStep + component={ + Object { + "breadcrumbs": Array [], + "key": "my-project", + "name": "MyProject", + "organization": "foo", + "qualifier": "TRK", + "qualityGate": Object { + "isDefault": true, + "key": "30", + "name": "Sonar way", + }, + "qualityProfiles": Array [ + Object { + "deleted": false, + "key": "my-qp", + "language": "ts", + "name": "Sonar way", + }, + ], + "tags": Array [], + } + } + open={false} + /> +</Fragment> +`; + +exports[`should render correctly: not Bitbucket binding 1`] = ` +<Alert + variant="error" +> + onboarding.tutorial.with.jenkins.only_bitbucket +</Alert> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/JenkinsfileStep-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/JenkinsfileStep-test.tsx.snap new file mode 100644 index 00000000000..2ef32e08297 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/JenkinsfileStep-test.tsx.snap @@ -0,0 +1,526 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly for Gradle 1`] = ` +<div + className="boxed-group-inner" +> + <ol + className="list-styled" + > + <li> + onboarding.build + <RenderOptions + checked="gradle" + name="buildtool" + onCheck={[Function]} + optionLabelKey="onboarding.build" + options={ + Array [ + "maven", + "gradle", + "msbuild", + "other", + ] + } + /> + </li> + <Gradle + component={ + Object { + "breadcrumbs": Array [], + "key": "my-project", + "name": "MyProject", + "organization": "foo", + "qualifier": "TRK", + "qualityGate": Object { + "isDefault": true, + "key": "30", + "name": "Sonar way", + }, + "qualityProfiles": Array [ + Object { + "deleted": false, + "key": "my-qp", + "language": "ts", + "name": "Sonar way", + }, + ], + "tags": Array [], + } + } + /> + </ol> + <React.Fragment> + <hr + className="huge-spacer-top huge-spacer-bottom" + /> + <div + className="abs-width-600" + > + <p + className="big-spacer-bottom" + > + <SentenceWithHighlights + highlightKeys={ + Array [ + "all_set", + ] + } + translationKey="onboarding.tutorial.with.jenkins.all_set" + /> + </p> + <div + className="display-flex-row big-spacer-bottom" + > + <div> + <img + alt="" + className="big-spacer-right" + src="/images/tutorials/commit.svg" + width={30} + /> + </div> + <div> + <p + className="little-spacer-bottom" + > + <strong> + onboarding.tutorial.with.jenkins.commit + </strong> + </p> + <p> + onboarding.tutorial.with.jenkins.commit.why + </p> + </div> + </div> + <div + className="display-flex-row huge-spacer-bottom" + > + <div> + <img + alt="" + className="big-spacer-right" + src="/images/tutorials/refresh.svg" + width={30} + /> + </div> + <div> + <p + className="little-spacer-bottom" + > + <strong> + onboarding.tutorial.with.jenkins.refresh + </strong> + </p> + <p> + onboarding.tutorial.with.jenkins.refresh.why + </p> + </div> + </div> + </div> + </React.Fragment> +</div> +`; + +exports[`should render correctly for MSBuild 1`] = ` +<div + className="boxed-group-inner" +> + <ol + className="list-styled" + > + <li> + onboarding.build + <RenderOptions + checked="msbuild" + name="buildtool" + onCheck={[Function]} + optionLabelKey="onboarding.build" + options={ + Array [ + "maven", + "gradle", + "msbuild", + "other", + ] + } + /> + </li> + <MSBuild + component={ + Object { + "breadcrumbs": Array [], + "key": "my-project", + "name": "MyProject", + "organization": "foo", + "qualifier": "TRK", + "qualityGate": Object { + "isDefault": true, + "key": "30", + "name": "Sonar way", + }, + "qualityProfiles": Array [ + Object { + "deleted": false, + "key": "my-qp", + "language": "ts", + "name": "Sonar way", + }, + ], + "tags": Array [], + } + } + /> + </ol> + <React.Fragment> + <hr + className="huge-spacer-top huge-spacer-bottom" + /> + <div + className="abs-width-600" + > + <p + className="big-spacer-bottom" + > + <SentenceWithHighlights + highlightKeys={ + Array [ + "all_set", + ] + } + translationKey="onboarding.tutorial.with.jenkins.all_set" + /> + </p> + <div + className="display-flex-row big-spacer-bottom" + > + <div> + <img + alt="" + className="big-spacer-right" + src="/images/tutorials/commit.svg" + width={30} + /> + </div> + <div> + <p + className="little-spacer-bottom" + > + <strong> + onboarding.tutorial.with.jenkins.commit + </strong> + </p> + <p> + onboarding.tutorial.with.jenkins.commit.why + </p> + </div> + </div> + <div + className="display-flex-row huge-spacer-bottom" + > + <div> + <img + alt="" + className="big-spacer-right" + src="/images/tutorials/refresh.svg" + width={30} + /> + </div> + <div> + <p + className="little-spacer-bottom" + > + <strong> + onboarding.tutorial.with.jenkins.refresh + </strong> + </p> + <p> + onboarding.tutorial.with.jenkins.refresh.why + </p> + </div> + </div> + </div> + </React.Fragment> +</div> +`; + +exports[`should render correctly for Maven 1`] = ` +<div + className="boxed-group-inner" +> + <ol + className="list-styled" + > + <li> + onboarding.build + <RenderOptions + checked="maven" + name="buildtool" + onCheck={[Function]} + optionLabelKey="onboarding.build" + options={ + Array [ + "maven", + "gradle", + "msbuild", + "other", + ] + } + /> + </li> + <Maven + component={ + Object { + "breadcrumbs": Array [], + "key": "my-project", + "name": "MyProject", + "organization": "foo", + "qualifier": "TRK", + "qualityGate": Object { + "isDefault": true, + "key": "30", + "name": "Sonar way", + }, + "qualityProfiles": Array [ + Object { + "deleted": false, + "key": "my-qp", + "language": "ts", + "name": "Sonar way", + }, + ], + "tags": Array [], + } + } + /> + </ol> + <React.Fragment> + <hr + className="huge-spacer-top huge-spacer-bottom" + /> + <div + className="abs-width-600" + > + <p + className="big-spacer-bottom" + > + <SentenceWithHighlights + highlightKeys={ + Array [ + "all_set", + ] + } + translationKey="onboarding.tutorial.with.jenkins.all_set" + /> + </p> + <div + className="display-flex-row big-spacer-bottom" + > + <div> + <img + alt="" + className="big-spacer-right" + src="/images/tutorials/commit.svg" + width={30} + /> + </div> + <div> + <p + className="little-spacer-bottom" + > + <strong> + onboarding.tutorial.with.jenkins.commit + </strong> + </p> + <p> + onboarding.tutorial.with.jenkins.commit.why + </p> + </div> + </div> + <div + className="display-flex-row huge-spacer-bottom" + > + <div> + <img + alt="" + className="big-spacer-right" + src="/images/tutorials/refresh.svg" + width={30} + /> + </div> + <div> + <p + className="little-spacer-bottom" + > + <strong> + onboarding.tutorial.with.jenkins.refresh + </strong> + </p> + <p> + onboarding.tutorial.with.jenkins.refresh.why + </p> + </div> + </div> + </div> + </React.Fragment> +</div> +`; + +exports[`should render correctly for Other 1`] = ` +<div + className="boxed-group-inner" +> + <ol + className="list-styled" + > + <li> + onboarding.build + <RenderOptions + checked="other" + name="buildtool" + onCheck={[Function]} + optionLabelKey="onboarding.build" + options={ + Array [ + "maven", + "gradle", + "msbuild", + "other", + ] + } + /> + </li> + <Other + component={ + Object { + "breadcrumbs": Array [], + "key": "my-project", + "name": "MyProject", + "organization": "foo", + "qualifier": "TRK", + "qualityGate": Object { + "isDefault": true, + "key": "30", + "name": "Sonar way", + }, + "qualityProfiles": Array [ + Object { + "deleted": false, + "key": "my-qp", + "language": "ts", + "name": "Sonar way", + }, + ], + "tags": Array [], + } + } + /> + </ol> + <React.Fragment> + <hr + className="huge-spacer-top huge-spacer-bottom" + /> + <div + className="abs-width-600" + > + <p + className="big-spacer-bottom" + > + <SentenceWithHighlights + highlightKeys={ + Array [ + "all_set", + ] + } + translationKey="onboarding.tutorial.with.jenkins.all_set" + /> + </p> + <div + className="display-flex-row big-spacer-bottom" + > + <div> + <img + alt="" + className="big-spacer-right" + src="/images/tutorials/commit.svg" + width={30} + /> + </div> + <div> + <p + className="little-spacer-bottom" + > + <strong> + onboarding.tutorial.with.jenkins.commit + </strong> + </p> + <p> + onboarding.tutorial.with.jenkins.commit.why + </p> + </div> + </div> + <div + className="display-flex-row huge-spacer-bottom" + > + <div> + <img + alt="" + className="big-spacer-right" + src="/images/tutorials/refresh.svg" + width={30} + /> + </div> + <div> + <p + className="little-spacer-bottom" + > + <strong> + onboarding.tutorial.with.jenkins.refresh + </strong> + </p> + <p> + onboarding.tutorial.with.jenkins.refresh.why + </p> + </div> + </div> + </div> + </React.Fragment> +</div> +`; + +exports[`should render correctly: Step wrapper 1`] = ` +<Step + finished={false} + open={true} + renderForm={[Function]} + stepNumber={3} + stepTitle="onboarding.tutorial.with.jenkins.jenkinsfile.title" +/> +`; + +exports[`should render correctly: initial content 1`] = ` +<div + className="boxed-group-inner" +> + <ol + className="list-styled" + > + <li> + onboarding.build + <RenderOptions + name="buildtool" + onCheck={[Function]} + optionLabelKey="onboarding.build" + options={ + Array [ + "maven", + "gradle", + "msbuild", + "other", + ] + } + /> + </li> + </ol> +</div> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/MultiBranchPipelineStep-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/MultiBranchPipelineStep-test.tsx.snap new file mode 100644 index 00000000000..ba91752f0fc --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/MultiBranchPipelineStep-test.tsx.snap @@ -0,0 +1,119 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly: Step wrapper 1`] = ` +<Step + finished={false} + onOpen={[MockFunction]} + open={true} + renderForm={[Function]} + stepNumber={1} + stepTitle="onboarding.tutorial.with.jenkins.multi_branch_pipeline.title" +/> +`; + +exports[`should render correctly: content 1`] = ` +<div + className="boxed-group-inner" +> + <p + className="big-spacer-bottom" + > + onboarding.tutorial.with.jenkins.multi_branch_pipeline.intro + </p> + <ol + className="list-styled" + > + <li> + <SentenceWithHighlights + highlightKeys={ + Array [ + "new_item", + "type", + ] + } + translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step1" + /> + </li> + <li> + <SentenceWithHighlights + highlightKeys={ + Array [ + "tab", + ] + } + translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2" + /> + <ul + className="list-styled" + > + <li> + <LabelActionPair + translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.server" + /> + </li> + <li> + <LabelActionPair + translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.creds" + /> + </li> + <li> + <LabelValuePair + translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.owner" + value="PROJECT_KEY" + /> + </li> + <li> + <LabelValuePair + translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.repo" + value="repo-slug" + /> + </li> + <li> + <LabelActionPair + translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.behaviour" + /> + </li> + </ul> + </li> + <li> + <SentenceWithHighlights + highlightKeys={ + Array [ + "tab", + ] + } + translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step3" + /> + <ul + className="list-styled" + > + <li> + <LabelActionPair + translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step3.mode" + /> + </li> + <li> + <LabelActionPair + translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step3.script_path" + /> + </li> + </ul> + </li> + <li> + <SentenceWithHighlights + highlightKeys={ + Array [ + "save", + ] + } + translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step4" + /> + </li> + </ol> + <Button + onClick={[MockFunction]} + > + continue + </Button> +</div> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/PreRequisitesStep-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/PreRequisitesStep-test.tsx.snap new file mode 100644 index 00000000000..01eb4afe997 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/PreRequisitesStep-test.tsx.snap @@ -0,0 +1,70 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly: Step wrapper 1`] = ` +<Step + finished={true} + onOpen={[MockFunction]} + open={false} + renderForm={[Function]} + stepTitle="onboarding.tutorial.with.jenkins.prereqs.title" +/> +`; + +exports[`should render correctly: content 1`] = ` +<div + className="boxed-group-inner" +> + <p + className="big-spacer-bottom" + > + <SentenceWithHighlights + highlightKeys={ + Array [ + "must_have", + ] + } + translationKey="onboarding.tutorial.with.jenkins.prereqs.intro" + /> + </p> + <ul + className="list-styled big-spacer-bottom" + > + <li> + onboarding.tutorial.with.jenkins.prereqs.plugins.branch_source + </li> + <li> + onboarding.tutorial.with.jenkins.prereqs.plugins.sonar_scanner + </li> + </ul> + <p + className="big-spacer-bottom" + > + <FormattedMessage + defaultMessage="onboarding.tutorial.with.jenkins.prereqs.step_by_step_guide" + id="onboarding.tutorial.with.jenkins.prereqs.step_by_step_guide" + values={ + Object { + "link": <Link + onlyActiveOnIndex={false} + style={Object {}} + target="_blank" + to="/documentation/analysis/jenkins/" + > + onboarding.tutorial.with.jenkins.prereqs.step_by_step_guide.link + </Link>, + } + } + /> + </p> + <p + className="big-spacer-bottom" + > + onboarding.tutorial.with.jenkins.prereqs.following_are_recommendations + </p> + <Button + onClick={[MockFunction]} + > + onboarding.tutorial.with.jenkins.prereqs.done + </Button> +</div> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/CreateJenkinsfileBulletPoint.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/CreateJenkinsfileBulletPoint.tsx new file mode 100644 index 00000000000..0afb84c644a --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/CreateJenkinsfileBulletPoint.tsx @@ -0,0 +1,73 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import HelpTooltip from 'sonar-ui-common/components/controls/HelpTooltip'; +import { Alert } from 'sonar-ui-common/components/ui/Alert'; +import CodeSnippet from '../../../common/CodeSnippet'; +import SentenceWithFilename from '../../components/SentenceWithFilename'; +import SentenceWithHighlights from '../../components/SentenceWithHighlights'; + +export interface CreateJenkinsfileBulletPointProps { + snippet: string; + alertTranslationKeyPart?: string; +} + +export default function CreateJenkinsfileBulletPoint(props: CreateJenkinsfileBulletPointProps) { + const { snippet, alertTranslationKeyPart } = props; + + return ( + <li className="abs-width-600"> + <SentenceWithFilename + filename="Jenkinsfile" + translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.jenkinsfile_step" + /> + {alertTranslationKeyPart !== undefined && ( + <Alert className="spacer-top" variant="info"> + <p className="text-middle"> + <SentenceWithHighlights + highlightKeys={['default', 'in_jenkins']} + translationKey={`${alertTranslationKeyPart}.replace`} + /> + <HelpTooltip + className="little-spacer-left" + overlay={ + <> + <p className="spacer-bottom"> + <SentenceWithHighlights + highlightKeys={['path']} + translationKey={`${alertTranslationKeyPart}.help1`} + /> + </p> + <p> + <SentenceWithHighlights + highlightKeys={['path', 'name']} + translationKey={`${alertTranslationKeyPart}.help2`} + /> + </p> + </> + } + /> + </p> + </Alert> + )} + <CodeSnippet snippet={snippet} /> + </li> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Gradle.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Gradle.tsx new file mode 100644 index 00000000000..acf803a44fe --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Gradle.tsx @@ -0,0 +1,64 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import * as React from 'react'; +import CodeSnippet from '../../../common/CodeSnippet'; +import SentenceWithFilename from '../../components/SentenceWithFilename'; +import CreateJenkinsfileBulletPoint from './CreateJenkinsfileBulletPoint'; + +export interface GradleProps { + component: T.Component; +} + +const buildGradleSnippet = (key: string) => `plugins { + id "org.sonarqube" version "2.7" +} + +sonarqube { + properties { + property "sonar.projectKey", "${key}" + } +}`; + +const JENKINSFILE_SNIPPET = `node { + stage('SCM') { + checkout scm + } + stage('SonarQube Analysis') { + withSonarQubeEnv() { + sh "./gradlew sonarqube" + } + } +}`; + +export default function Gradle({ component }: GradleProps) { + return ( + <> + <li className="abs-width-600"> + <SentenceWithFilename + filename="build.gradle" + translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.gradle.step2" + /> + <CodeSnippet snippet={buildGradleSnippet(component.key)} /> + </li> + <CreateJenkinsfileBulletPoint snippet={JENKINSFILE_SNIPPET} /> + </> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/MSBuild.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/MSBuild.tsx new file mode 100644 index 00000000000..3200d3eb3f2 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/MSBuild.tsx @@ -0,0 +1,90 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import HelpTooltip from 'sonar-ui-common/components/controls/HelpTooltip'; +import { Alert } from 'sonar-ui-common/components/ui/Alert'; +import CodeSnippet from '../../../common/CodeSnippet'; +import SentenceWithFilename from '../../components/SentenceWithFilename'; +import SentenceWithHighlights from '../../components/SentenceWithHighlights'; + +export interface MSBuildProps { + component: T.Component; +} + +const jenkinsfileSnippet = (key: string) => `node { + stage('SCM') { + checkout scm + } + stage('SonarQube Analysis') { + def msbuildHome = tool 'Default MSBuild' + def scannerHome = tool 'SonarScanner for MSBuild' + withSonarQubeEnv() { + bat "\\"\${scannerHome}\\\\SonarScanner.MSBuild.exe\\" begin /k:\\"${key}\\"" + bat "\\"\${msbuildHome}\\\\MSBuild.exe\\" /t:Rebuild" + bat "\\"\${scannerHome}\\\\SonarScanner.MSBuild.exe\\" end" + } + } +} +`; + +export default function MSBuild({ component }: MSBuildProps) { + return ( + <li className="abs-width-600"> + <SentenceWithFilename + filename="Jenkinsfile" + translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.jenkinsfile_step" + /> + <Alert className="spacer-top" variant="info"> + <p className="text-middle"> + <SentenceWithHighlights + highlightKeys={['default_msbuild', 'default_scanner', 'in_jenkins']} + translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.replace" + /> + <HelpTooltip + className="little-spacer-left" + overlay={ + <> + <p className="spacer-bottom"> + <SentenceWithHighlights + highlightKeys={['path']} + translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.help1" + /> + </p> + <p className="spacer-bottom"> + <SentenceWithHighlights + highlightKeys={['path', 'name']} + translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.help2" + /> + </p> + <p> + <SentenceWithHighlights + highlightKeys={['path', 'name']} + translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.help3" + /> + </p> + </> + } + /> + </p> + </Alert> + <CodeSnippet snippet={jenkinsfileSnippet(component.key)} /> + </li> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Maven.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Maven.tsx new file mode 100644 index 00000000000..98a018dc5a0 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Maven.tsx @@ -0,0 +1,62 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import * as React from 'react'; +import CodeSnippet from '../../../common/CodeSnippet'; +import SentenceWithFilename from '../../components/SentenceWithFilename'; +import CreateJenkinsfileBulletPoint from './CreateJenkinsfileBulletPoint'; + +export interface MavenProps { + component: T.Component; +} + +const pomSnippet = (key: string) => `<properties> + <sonar.projectKey>${key}</sonar.projectKey> +</properties>`; + +const JENKINSFILE_SNIPPET = `node { + stage('SCM') { + checkout scm + } + stage('SonarQube Analysis') { + def mvn = tool 'Default Maven'; + withSonarQubeEnv() { + sh "\${mvn}/bin/mvn sonar:sonar" + } + } +}`; + +export default function Maven({ component }: MavenProps) { + return ( + <> + <li className="abs-width-600"> + <SentenceWithFilename + filename="pom.xml" + translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.maven.step2" + /> + <CodeSnippet snippet={pomSnippet(component.key)} /> + </li> + <CreateJenkinsfileBulletPoint + alertTranslationKeyPart="onboarding.tutorial.with.jenkins.jenkinsfile.maven.step3" + snippet={JENKINSFILE_SNIPPET} + /> + </> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Other.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Other.tsx new file mode 100644 index 00000000000..aa4f20748f3 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Other.tsx @@ -0,0 +1,60 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import * as React from 'react'; +import CodeSnippet from '../../../common/CodeSnippet'; +import SentenceWithFilename from '../../components/SentenceWithFilename'; +import CreateJenkinsfileBulletPoint from './CreateJenkinsfileBulletPoint'; + +export interface OtherProps { + component: T.Component; +} + +const sonarProjectSnippet = (key: string) => `sonar.projectKey=${key}`; + +const JENKINSFILE_SNIPPET = `node { + stage('SCM') { + checkout scm + } + stage('SonarQube Analysis') { + def scannerHome = tool 'SonarScanner'; + withSonarQubeEnv() { + sh "\${scannerHome}/bin/sonar-scanner" + } + } +}`; + +export default function Other({ component }: OtherProps) { + return ( + <> + <li className="abs-width-600"> + <SentenceWithFilename + filename="sonar-project.properties" + translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.other.step2" + /> + <CodeSnippet snippet={sonarProjectSnippet(component.key)} /> + </li> + <CreateJenkinsfileBulletPoint + alertTranslationKeyPart="onboarding.tutorial.with.jenkins.jenkinsfile.other.step3" + snippet={JENKINSFILE_SNIPPET} + /> + </> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/CreateJenkinsfileBulletPoint-test.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/CreateJenkinsfileBulletPoint-test.tsx new file mode 100644 index 00000000000..69fc5bae206 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/CreateJenkinsfileBulletPoint-test.tsx @@ -0,0 +1,36 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import { shallow } from 'enzyme'; +import * as React from 'react'; +import CreateJenkinsfileBulletPoint, { + CreateJenkinsfileBulletPointProps +} from '../CreateJenkinsfileBulletPoint'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot('default'); + expect(shallowRender({ alertTranslationKeyPart: 'baz' })).toMatchSnapshot('with alert'); +}); + +function shallowRender(props: Partial<CreateJenkinsfileBulletPointProps> = {}) { + return shallow<CreateJenkinsfileBulletPointProps>( + <CreateJenkinsfileBulletPoint snippet="foo { bar() }" {...props} /> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/Gradle-test.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/Gradle-test.tsx new file mode 100644 index 00000000000..5bc3adf1a49 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/Gradle-test.tsx @@ -0,0 +1,32 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import { shallow } from 'enzyme'; +import * as React from 'react'; +import { mockComponent } from '../../../../../helpers/testMocks'; +import Gradle, { GradleProps } from '../Gradle'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot(); +}); + +function shallowRender(props: Partial<GradleProps> = {}) { + return shallow<GradleProps>(<Gradle component={mockComponent()} {...props} />); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/MSBuild-test.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/MSBuild-test.tsx new file mode 100644 index 00000000000..a391815e625 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/MSBuild-test.tsx @@ -0,0 +1,32 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import { shallow } from 'enzyme'; +import * as React from 'react'; +import { mockComponent } from '../../../../../helpers/testMocks'; +import MSBuild, { MSBuildProps } from '../MSBuild'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot(); +}); + +function shallowRender(props: Partial<MSBuildProps> = {}) { + return shallow<MSBuildProps>(<MSBuild component={mockComponent()} {...props} />); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/Maven-test.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/Maven-test.tsx new file mode 100644 index 00000000000..13632ee97b9 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/Maven-test.tsx @@ -0,0 +1,32 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import { shallow } from 'enzyme'; +import * as React from 'react'; +import { mockComponent } from '../../../../../helpers/testMocks'; +import Maven, { MavenProps } from '../Maven'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot(); +}); + +function shallowRender(props: Partial<MavenProps> = {}) { + return shallow<MavenProps>(<Maven component={mockComponent()} {...props} />); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/Other-test.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/Other-test.tsx new file mode 100644 index 00000000000..c9c5b30e297 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/Other-test.tsx @@ -0,0 +1,32 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import { shallow } from 'enzyme'; +import * as React from 'react'; +import { mockComponent } from '../../../../../helpers/testMocks'; +import Other, { OtherProps } from '../Other'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot(); +}); + +function shallowRender(props: Partial<OtherProps> = {}) { + return shallow<OtherProps>(<Other component={mockComponent()} {...props} />); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/CreateJenkinsfileBulletPoint-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/CreateJenkinsfileBulletPoint-test.tsx.snap new file mode 100644 index 00000000000..b56d3e42589 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/CreateJenkinsfileBulletPoint-test.tsx.snap @@ -0,0 +1,77 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly: default 1`] = ` +<li + className="abs-width-600" +> + <SentenceWithFilename + filename="Jenkinsfile" + translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.jenkinsfile_step" + /> + <CodeSnippet + snippet="foo { bar() }" + /> +</li> +`; + +exports[`should render correctly: with alert 1`] = ` +<li + className="abs-width-600" +> + <SentenceWithFilename + filename="Jenkinsfile" + translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.jenkinsfile_step" + /> + <Alert + className="spacer-top" + variant="info" + > + <p + className="text-middle" + > + <SentenceWithHighlights + highlightKeys={ + Array [ + "default", + "in_jenkins", + ] + } + translationKey="baz.replace" + /> + <HelpTooltip + className="little-spacer-left" + overlay={ + <React.Fragment> + <p + className="spacer-bottom" + > + <SentenceWithHighlights + highlightKeys={ + Array [ + "path", + ] + } + translationKey="baz.help1" + /> + </p> + <p> + <SentenceWithHighlights + highlightKeys={ + Array [ + "path", + "name", + ] + } + translationKey="baz.help2" + /> + </p> + </React.Fragment> + } + /> + </p> + </Alert> + <CodeSnippet + snippet="foo { bar() }" + /> +</li> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/Gradle-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/Gradle-test.tsx.snap new file mode 100644 index 00000000000..db1fcec352e --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/Gradle-test.tsx.snap @@ -0,0 +1,37 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<Fragment> + <li + className="abs-width-600" + > + <SentenceWithFilename + filename="build.gradle" + translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.gradle.step2" + /> + <CodeSnippet + snippet="plugins { + id \\"org.sonarqube\\" version \\"2.7\\" +} + +sonarqube { + properties { + property \\"sonar.projectKey\\", \\"my-project\\" + } +}" + /> + </li> + <CreateJenkinsfileBulletPoint + snippet="node { + stage('SCM') { + checkout scm + } + stage('SonarQube Analysis') { + withSonarQubeEnv() { + sh \\"./gradlew sonarqube\\" + } + } +}" + /> +</Fragment> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/MSBuild-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/MSBuild-test.tsx.snap new file mode 100644 index 00000000000..822c34ce98e --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/MSBuild-test.tsx.snap @@ -0,0 +1,91 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<li + className="abs-width-600" +> + <SentenceWithFilename + filename="Jenkinsfile" + translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.jenkinsfile_step" + /> + <Alert + className="spacer-top" + variant="info" + > + <p + className="text-middle" + > + <SentenceWithHighlights + highlightKeys={ + Array [ + "default_msbuild", + "default_scanner", + "in_jenkins", + ] + } + translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.replace" + /> + <HelpTooltip + className="little-spacer-left" + overlay={ + <React.Fragment> + <p + className="spacer-bottom" + > + <SentenceWithHighlights + highlightKeys={ + Array [ + "path", + ] + } + translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.help1" + /> + </p> + <p + className="spacer-bottom" + > + <SentenceWithHighlights + highlightKeys={ + Array [ + "path", + "name", + ] + } + translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.help2" + /> + </p> + <p> + <SentenceWithHighlights + highlightKeys={ + Array [ + "path", + "name", + ] + } + translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.help3" + /> + </p> + </React.Fragment> + } + /> + </p> + </Alert> + <CodeSnippet + snippet="node { + stage('SCM') { + checkout scm + } + stage('SonarQube Analysis') { + def msbuildHome = tool 'Default MSBuild' + def scannerHome = tool 'SonarScanner for MSBuild' + withSonarQubeEnv() { + bat \\"\\\\\\"\${scannerHome}\\\\\\\\SonarScanner.MSBuild.exe\\\\\\" begin /k:\\\\\\"my-project\\\\\\"\\" + bat \\"\\\\\\"\${msbuildHome}\\\\\\\\MSBuild.exe\\\\\\" /t:Rebuild\\" + bat \\"\\\\\\"\${scannerHome}\\\\\\\\SonarScanner.MSBuild.exe\\\\\\" end\\" + } + } +} +" + /> +</li> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/Maven-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/Maven-test.tsx.snap new file mode 100644 index 00000000000..a619e3b65a0 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/Maven-test.tsx.snap @@ -0,0 +1,33 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<Fragment> + <li + className="abs-width-600" + > + <SentenceWithFilename + filename="pom.xml" + translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.maven.step2" + /> + <CodeSnippet + snippet="<properties> + <sonar.projectKey>my-project</sonar.projectKey> +</properties>" + /> + </li> + <CreateJenkinsfileBulletPoint + alertTranslationKeyPart="onboarding.tutorial.with.jenkins.jenkinsfile.maven.step3" + snippet="node { + stage('SCM') { + checkout scm + } + stage('SonarQube Analysis') { + def mvn = tool 'Default Maven'; + withSonarQubeEnv() { + sh \\"\${mvn}/bin/mvn sonar:sonar\\" + } + } +}" + /> +</Fragment> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/Other-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/Other-test.tsx.snap new file mode 100644 index 00000000000..c5a549388dd --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/Other-test.tsx.snap @@ -0,0 +1,31 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<Fragment> + <li + className="abs-width-600" + > + <SentenceWithFilename + filename="sonar-project.properties" + translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.other.step2" + /> + <CodeSnippet + snippet="sonar.projectKey=my-project" + /> + </li> + <CreateJenkinsfileBulletPoint + alertTranslationKeyPart="onboarding.tutorial.with.jenkins.jenkinsfile.other.step3" + snippet="node { + stage('SCM') { + checkout scm + } + stage('SonarQube Analysis') { + def scannerHome = tool 'SonarScanner'; + withSonarQubeEnv() { + sh \\"\${scannerHome}/bin/sonar-scanner\\" + } + } +}" + /> +</Fragment> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/test-utils.ts b/server/sonar-web/src/main/js/components/tutorials/jenkins/test-utils.ts new file mode 100644 index 00000000000..572caf9b5d1 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/test-utils.ts @@ -0,0 +1,28 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { ShallowWrapper } from 'enzyme'; +import Step from '../components/Step'; + +export function renderStepContent(wrapper: ShallowWrapper<React.ReactNode>) { + return wrapper + .find(Step) + .props() + .renderForm(); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/ManualTutorial.tsx b/server/sonar-web/src/main/js/components/tutorials/manual/ManualTutorial.tsx index f1b4c1dcee3..1714244e00e 100644 --- a/server/sonar-web/src/main/js/components/tutorials/manual/ManualTutorial.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/manual/ManualTutorial.tsx @@ -18,8 +18,6 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import Tooltip from 'sonar-ui-common/components/controls/Tooltip'; -import BackIcon from 'sonar-ui-common/components/icons/BackIcon'; import { translate } from 'sonar-ui-common/helpers/l10n'; import { isVSTS } from '../../../helpers/almIntegrations'; import InstanceMessage from '../../common/InstanceMessage'; @@ -34,7 +32,6 @@ export enum Steps { interface Props { component: T.Component; currentUser: T.LoggedInUser; - onBack?: () => void; } interface State { @@ -61,19 +58,7 @@ export default class ManualTutorial extends React.PureComponent<Props, State> { return ( <> <div className="page-header big-spacer-bottom"> - <h1 className="page-title"> - {this.props.onBack !== undefined && ( - <Tooltip overlay={translate('onboarding.tutorial.return_to_list')}> - <a - aria-label={translate('onboarding.tutorial.return_to_list')} - className="link-no-underline big-spacer-right" - onClick={this.props.onBack}> - <BackIcon /> - </a> - </Tooltip> - )} - {translate('onboarding.project_analysis.header')} - </h1> + <h1 className="page-title">{translate('onboarding.project_analysis.header')}</h1> <p className="page-description"> <InstanceMessage message={translate('onboarding.project_analysis.description')} /> </p> diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/ManualTutorial-test.tsx b/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/ManualTutorial-test.tsx index 79a269a35c4..188227b8db9 100644 --- a/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/ManualTutorial-test.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/ManualTutorial-test.tsx @@ -26,7 +26,6 @@ import TokenStep from '../TokenStep'; it('renders correctly', () => { expect(shallowRender()).toMatchSnapshot('default'); - expect(shallowRender({ onBack: jest.fn() })).toMatchSnapshot('with back button'); }); it('allows to navigate between steps', () => { diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/__snapshots__/ManualTutorial-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/__snapshots__/ManualTutorial-test.tsx.snap index cc1cf2fc27a..77e51dfb064 100644 --- a/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/__snapshots__/ManualTutorial-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/__snapshots__/ManualTutorial-test.tsx.snap @@ -65,80 +65,3 @@ exports[`renders correctly: default 1`] = ` /> </Fragment> `; - -exports[`renders correctly: with back button 1`] = ` -<Fragment> - <div - className="page-header big-spacer-bottom" - > - <h1 - className="page-title" - > - <Tooltip - overlay="onboarding.tutorial.return_to_list" - > - <a - aria-label="onboarding.tutorial.return_to_list" - className="link-no-underline big-spacer-right" - onClick={[MockFunction]} - > - <BackIcon /> - </a> - </Tooltip> - onboarding.project_analysis.header - </h1> - <p - className="page-description" - > - <InstanceMessage - message="onboarding.project_analysis.description" - /> - </p> - </div> - <TokenStep - currentUser={ - Object { - "groups": Array [], - "isLoggedIn": true, - "login": "luke", - "name": "Skywalker", - "scmAccounts": Array [], - } - } - finished={false} - initialTokenName="Analyze \\"MyProject\\"" - onContinue={[Function]} - onOpen={[Function]} - open={true} - stepNumber={1} - /> - <ProjectAnalysisStep - component={ - Object { - "breadcrumbs": Array [], - "key": "my-project", - "name": "MyProject", - "organization": "foo", - "qualifier": "TRK", - "qualityGate": Object { - "isDefault": true, - "key": "30", - "name": "Sonar way", - }, - "qualityProfiles": Array [ - Object { - "deleted": false, - "key": "my-qp", - "language": "ts", - "name": "Sonar way", - }, - ], - "tags": Array [], - } - } - displayRowLayout={true} - open={false} - stepNumber={2} - /> -</Fragment> -`; diff --git a/server/sonar-web/src/main/js/components/tutorials/styles.css b/server/sonar-web/src/main/js/components/tutorials/styles.css new file mode 100644 index 00000000000..295fb42fd23 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/styles.css @@ -0,0 +1,23 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +.tutorial-selection { + margin: 0 auto; + max-width: 500px; +} diff --git a/server/sonar-web/src/main/js/components/tutorials/types.ts b/server/sonar-web/src/main/js/components/tutorials/types.ts new file mode 100644 index 00000000000..2ad939070bc --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/types.ts @@ -0,0 +1,31 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +export enum TutorialModes { + Manual = 'manual', + Jenkins = 'jenkins' +} + +export interface LanguageConfig { + language?: string; + javaBuild?: string; + cFamilyCompiler?: string; + os?: string; + projectKey?: string; +} diff --git a/server/sonar-web/src/main/js/components/tutorials/utils.ts b/server/sonar-web/src/main/js/components/tutorials/utils.ts index a1a28edc712..89394ecb80b 100644 --- a/server/sonar-web/src/main/js/components/tutorials/utils.ts +++ b/server/sonar-web/src/main/js/components/tutorials/utils.ts @@ -17,13 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -export interface LanguageConfig { - language?: string; - javaBuild?: string; - cFamilyCompiler?: string; - os?: string; - projectKey?: string; -} +import { LanguageConfig } from './types'; export function isLanguageConfigured(config?: LanguageConfig) { if (!config) { diff --git a/server/sonar-web/src/main/js/helpers/__tests__/alm-settings.ts b/server/sonar-web/src/main/js/helpers/__tests__/alm-settings.ts new file mode 100644 index 00000000000..f6be96670fa --- /dev/null +++ b/server/sonar-web/src/main/js/helpers/__tests__/alm-settings.ts @@ -0,0 +1,41 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import { isBitbucketBindingDefinition, isProjectBitbucketBindingResponse } from '../alm-settings'; +import { + mockBitbucketBindingDefinition, + mockGithubBindingDefinition, + mockProjectAlmBindingResponse, + mockProjectBitbucketBindingGet +} from '../mocks/alm-settings'; + +describe('isProjectBitbucketBindingResponse', () => { + it('works as expected', () => { + expect(isProjectBitbucketBindingResponse(mockProjectAlmBindingResponse())).toBe(false); + expect(isProjectBitbucketBindingResponse(mockProjectBitbucketBindingGet())).toBe(true); + }); +}); + +describe('isBitbucketBindingDefinition', () => { + it('works as expected', () => { + expect(isBitbucketBindingDefinition(mockGithubBindingDefinition())).toBe(false); + expect(isBitbucketBindingDefinition(mockBitbucketBindingDefinition())).toBe(true); + }); +}); diff --git a/server/sonar-web/src/main/js/helpers/alm-settings.ts b/server/sonar-web/src/main/js/helpers/alm-settings.ts new file mode 100644 index 00000000000..0bb508267ab --- /dev/null +++ b/server/sonar-web/src/main/js/helpers/alm-settings.ts @@ -0,0 +1,38 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { + AlmBindingDefinition, + AlmKeys, + BitbucketBindingDefinition, + ProjectAlmBindingResponse, + ProjectBitbucketBindingResponse +} from '../types/alm-settings'; + +export function isProjectBitbucketBindingResponse( + binding: ProjectAlmBindingResponse +): binding is ProjectBitbucketBindingResponse { + return binding.alm === AlmKeys.Bitbucket; +} + +export function isBitbucketBindingDefinition( + binding: AlmBindingDefinition & { url?: string; personalAccessToken?: string } +): binding is BitbucketBindingDefinition { + return binding.url !== undefined && binding.personalAccessToken !== undefined; +} diff --git a/server/sonar-web/src/main/js/helpers/mocks/alm-settings.ts b/server/sonar-web/src/main/js/helpers/mocks/alm-settings.ts index 2eab0248c9d..2a495addc9d 100644 --- a/server/sonar-web/src/main/js/helpers/mocks/alm-settings.ts +++ b/server/sonar-web/src/main/js/helpers/mocks/alm-settings.ts @@ -23,7 +23,9 @@ import { AzureBindingDefinition, BitbucketBindingDefinition, GithubBindingDefinition, - GitlabBindingDefinition + GitlabBindingDefinition, + ProjectAlmBindingResponse, + ProjectBitbucketBindingResponse } from '../../types/alm-settings'; export function mockAlmSettingsInstance( @@ -78,3 +80,25 @@ export function mockGitlabBindingDefinition( ...overrides }; } + +export function mockProjectAlmBindingResponse( + overrides: Partial<ProjectAlmBindingResponse> = {} +): ProjectAlmBindingResponse { + return { + alm: AlmKeys.GitHub, + key: 'foo', + ...overrides + }; +} + +export function mockProjectBitbucketBindingGet( + overrides: Partial<ProjectBitbucketBindingResponse> = {} +): ProjectBitbucketBindingResponse { + return { + alm: AlmKeys.Bitbucket, + key: 'foo', + repository: 'PROJECT_KEY', + slug: 'repo-slug', + ...overrides + }; +} diff --git a/server/sonar-web/src/main/js/types/alm-settings.ts b/server/sonar-web/src/main/js/types/alm-settings.ts index 1fbff623de2..534471f4741 100644 --- a/server/sonar-web/src/main/js/types/alm-settings.ts +++ b/server/sonar-web/src/main/js/types/alm-settings.ts @@ -56,6 +56,12 @@ export interface ProjectAlmBindingResponse { summaryCommentEnabled?: boolean; } +export interface ProjectBitbucketBindingResponse extends ProjectAlmBindingResponse { + alm: AlmKeys.Bitbucket; + repository: string; + slug: string; +} + export interface ProjectAlmBindingParams { almSetting: string; project: string; diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index d111d22a497..d13c038a540 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -3180,6 +3180,7 @@ onboarding.build=What is your build technology? onboarding.build.maven=Maven onboarding.build.gradle=Gradle onboarding.build.make=Make +onboarding.build.msbuild=MSBuild onboarding.build.other=Other (for JS, TS, Go, Python, PHP, ...) onboarding.language=What is your project's main language? @@ -3252,6 +3253,104 @@ onboarding.analysis.sqscanner.docs.gradle.example_project.title=live Gradle-base onboarding.tutorial.return_to_list=Choose another option +onboarding.tutorial.choose_method=How do you want to analyze your repository? +onboarding.tutorial.choose_method.manual=Manually +onboarding.tutorial.choose_method.jenkins=With Jenkins + +onboarding.tutorial.with.jenkins.title=Analyze your project with Jenkins +onboarding.tutorial.with.jenkins.only_bitbucket=This tutorial is only available for projects bound to Bitbucket Server. +onboarding.tutorial.with.jenkins.prereqs.title=Prerequisites +onboarding.tutorial.with.jenkins.prereqs.intro.sentence=To run your project analyses with Jenkins, the following plugins {must_have} +onboarding.tutorial.with.jenkins.prereqs.intro.sentence.must_have=must be installed and configured: +onboarding.tutorial.with.jenkins.prereqs.plugins.branch_source=Bitbucket Branch Source plugin for Jenkins - version 2.7 or later +onboarding.tutorial.with.jenkins.prereqs.plugins.sonar_scanner=SonarQube Scanner plugin for Jenkins - version 2.11 or later +onboarding.tutorial.with.jenkins.prereqs.step_by_step_guide=For a step by step guide on installing and configuring those plugins in Jenkins, visit the {link} documentation page. +onboarding.tutorial.with.jenkins.prereqs.step_by_step_guide.link=Analysis Prerequisites +onboarding.tutorial.with.jenkins.prereqs.following_are_recommendations=We recommend using the configuration in the following steps for the best results, but you can customize it as needed. +onboarding.tutorial.with.jenkins.prereqs.done=Configure Analysis +onboarding.tutorial.with.jenkins.multi_branch_pipeline.title=Create a Multibranch Pipeline Job +onboarding.tutorial.with.jenkins.multi_branch_pipeline.intro=Create a Multibranch Pipeline in order to automatically analyze all your branches and pull requests. +onboarding.tutorial.with.jenkins.multi_branch_pipeline.step1.sentence=From Jenkins' dashboard, click {new_item} and create a {type}. +onboarding.tutorial.with.jenkins.multi_branch_pipeline.step1.sentence.new_item=New Item +onboarding.tutorial.with.jenkins.multi_branch_pipeline.step1.sentence.type=Multibranch Pipeline Job +onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.sentence=Under {tab}, add a Bitbucket source and enter the following information: +onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.sentence.tab=Branch Sources +onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.server.label=Server +onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.server.action=select the instance hosting the repository you want to analyze. +onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.creds.label=Credentials +onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.creds.action=select the Bitbucket Server credentials. +onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.owner.label=Owner +onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.owner.action=enter your project key. +onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.repo.label=Repository +onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.repo.action=select the repository you want to analyze. +onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.behaviour.label=Behavior +onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.behaviour.action=select Exclude branches that are also filed as PRs. +onboarding.tutorial.with.jenkins.multi_branch_pipeline.step3.sentence=Jump to the {tab} section and set the following parameters: +onboarding.tutorial.with.jenkins.multi_branch_pipeline.step3.sentence.tab=Build Configuration +onboarding.tutorial.with.jenkins.multi_branch_pipeline.step3.mode.label=Mode +onboarding.tutorial.with.jenkins.multi_branch_pipeline.step3.mode.action=by Jenkinsfile +onboarding.tutorial.with.jenkins.multi_branch_pipeline.step3.script_path.label=Script Path +onboarding.tutorial.with.jenkins.multi_branch_pipeline.step3.script_path.action=Jenkinsfile +onboarding.tutorial.with.jenkins.multi_branch_pipeline.step4.sentence=Click {save}. +onboarding.tutorial.with.jenkins.multi_branch_pipeline.step4.sentence.save=Save +onboarding.tutorial.with.jenkins.bitbucket_webhook.title=Create a Bitbucket Server Webhook +onboarding.tutorial.with.jenkins.bitbucket_webhook.intro.sentence=Create a Webhook in your repository to trigger the Jenkins job on push. Already have a Webhook configured? {link} +onboarding.tutorial.with.jenkins.bitbucket_webhook.intro.link=Skip this step. +onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.sentence=Go to the {link} and enter the following information: +onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.link=Bitbucket Server Webhook creation page for your repository +onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.name.label=Name +onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.name.action=give a unique name. +onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.url.label=URL +onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.url.action=Enter the following URL, replacing the tokens as needed: +onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.url.warning=The Bitbucket Server URL must be identical to the one in your Jenkins configuration. Watch out for any missing or extra "/" at the end. +onboarding.tutorial.with.jenkins.bitbucket_webhook.step2.sentence=Under {events}, make sure the following options are checked: +onboarding.tutorial.with.jenkins.bitbucket_webhook.step2.sentence.events=Events +onboarding.tutorial.with.jenkins.bitbucket_webhook.step2.repo.label=Repository +onboarding.tutorial.with.jenkins.bitbucket_webhook.step2.repo.action=Push +onboarding.tutorial.with.jenkins.bitbucket_webhook.step2.pr.label=Pull Request +onboarding.tutorial.with.jenkins.bitbucket_webhook.step2.pr.action=Opened +onboarding.tutorial.with.jenkins.bitbucket_webhook.step3.sentence=Click {create}. +onboarding.tutorial.with.jenkins.bitbucket_webhook.step3.sentence.create=Create +onboarding.tutorial.with.jenkins.jenkinsfile.title=Create a Jenkinsfile +onboarding.tutorial.with.jenkins.jenkinsfile.jenkinsfile_step.sentence=Create a {file} file in your repository and paste the following code: +onboarding.tutorial.with.jenkins.jenkinsfile.maven.step2.sentence=Add the following to your {file} file: +onboarding.tutorial.with.jenkins.jenkinsfile.maven.step3.replace.sentence=Make sure to replace {default} with the name you gave to your Maven tool {in_jenkins}. +onboarding.tutorial.with.jenkins.jenkinsfile.maven.step3.replace.sentence.default=Default Maven +onboarding.tutorial.with.jenkins.jenkinsfile.maven.step3.replace.sentence.in_jenkins=in Jenkins +onboarding.tutorial.with.jenkins.jenkinsfile.maven.step3.help1.sentence=To get the name of your Maven tool in Jenkins, navigate to {path}. +onboarding.tutorial.with.jenkins.jenkinsfile.maven.step3.help1.sentence.path=Manage Jenkins > Global Tool Configuration +onboarding.tutorial.with.jenkins.jenkinsfile.maven.step3.help2.sentence=The name is located under the {path} section, in the {name} field. +onboarding.tutorial.with.jenkins.jenkinsfile.maven.step3.help2.sentence.path=Maven > Maven installations +onboarding.tutorial.with.jenkins.jenkinsfile.maven.step3.help2.sentence.name=Name +onboarding.tutorial.with.jenkins.jenkinsfile.gradle.step2.sentence=Add the following to your {file} file: +onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.replace.sentence=Make sure to replace {default_msbuild} and {default_scanner} with the names you gave to your MSBuild and SonarScanner for MSBuild tools {in_jenkins}. +onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.replace.sentence.default_msbuild=Default MSBuild +onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.replace.sentence.default_scanner=SonarScanner for MSBuild +onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.replace.sentence.in_jenkins=in Jenkins +onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.help1.sentence=To get the name of these tools in Jenkins, navigate to {path}. +onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.help1.sentence.path=Manage Jenkins > Global Tool Configuration +onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.help2.sentence=For your MSBuild tool, the name is located under the {path} section, in the {name} field. +onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.help2.sentence.path=MSBuild > MSBuild installations +onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.help2.sentence.name=Name +onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.help3.sentence=For your SonarScanner for MSBuild tool, the name is located under the {path} section, in the {name} field. +onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.help3.sentence.path=SonarScanner for MSBuild > SonarScanner for MSBuild installations +onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.help3.sentence.name=Name +onboarding.tutorial.with.jenkins.jenkinsfile.other.step2.sentence=Create a {file} file in your repository and paste the following code: +onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.replace.sentence=Make sure to replace {default} with the name you gave to your SonarQube Scanner tool {in_jenkins}. +onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.replace.sentence.default=SonarScanner +onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.replace.sentence.in_jenkins=in Jenkins +onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.help1.sentence=To get the name of your SonarQube Scanner tool in Jenkins, navigate to {path}. +onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.help1.sentence.path=Manage Jenkins > Global Tool Configuration +onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.help2.sentence=The name is located under the {path} section, in the {name} field. +onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.help2.sentence.path=SonarQube Scanner > SonarQube Scanner installations +onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.help2.sentence.name=Name +onboarding.tutorial.with.jenkins.all_set.sentence={all_set} and ready to improve the quality and security of your code! +onboarding.tutorial.with.jenkins.all_set.sentence.all_set=You're all set +onboarding.tutorial.with.jenkins.commit=Commit and push your code to start the analysis. +onboarding.tutorial.with.jenkins.commit.why=Each new push you make on your branches or pull requests will trigger a new analysis in SonarQube. +onboarding.tutorial.with.jenkins.refresh=This page will then refresh with your analysis results. +onboarding.tutorial.with.jenkins.refresh.why=If the page doesn't refresh after a while, please double-check the analysis configuration. + #------------------------------------------------------------------------------ # # BRANCHES |