From 5ca5d7a20c103051e824f956cb44ce5d8cf84e45 Mon Sep 17 00:00:00 2001 From: Sergei Garin Date: Tue, 17 Dec 2024 13:43:07 +0400 Subject: [PATCH] Fix tests --- .../integration-test/dashboard/actions/api.ts | 29 +++++++++++------- .../dashboard/assetPanel.spec.ts | 23 +++++++++++++- .../dashboard/mock/example.png | Bin 0 -> 9033 bytes .../MarkdownViewer/MarkdownViewer.tsx | 12 +++++++- .../MarkdownViewer/defaultRenderer.ts | 6 ++-- 5 files changed, 53 insertions(+), 17 deletions(-) create mode 100644 app/gui/integration-test/dashboard/mock/example.png diff --git a/app/gui/integration-test/dashboard/actions/api.ts b/app/gui/integration-test/dashboard/actions/api.ts index 9944c8bbc14f..09f7c15cbb5f 100644 --- a/app/gui/integration-test/dashboard/actions/api.ts +++ b/app/gui/integration-test/dashboard/actions/api.ts @@ -15,6 +15,7 @@ import * as actions from '.' import { readFileSync } from 'node:fs' import { dirname, join } from 'node:path' import { fileURLToPath } from 'node:url' +import invariant from 'tiny-invariant' const __dirname = dirname(fileURLToPath(import.meta.url)) @@ -1129,17 +1130,23 @@ async function mockApiInternal({ page, setupAPI }: MockParams) { }) }) - await get(remoteBackendPaths.getProjectAssetPath(GLOB_PROJECT_ID, '*'), (route, request) => { - const maybeId = request.url().match(/[/]projects[/]([^?/]+)/)?.[1] - if (!maybeId) return - const projectId = backend.ProjectId(maybeId) - called('getProjectAsset', { projectId }) - return route.fulfill({ - // This is a mock SVG image. Just a square with a black background. - body: '/mock/svg.svg', - contentType: 'text/plain', - }) - }) + await get( + remoteBackendPaths.getProjectAssetPath(GLOB_PROJECT_ID, '*'), + async (route, request) => { + const maybeId = request.url().match(/[/]projects[/]([^?/]+)/)?.[1] + + invariant(maybeId, 'Unable to parse the ID provided') + + const projectId = backend.ProjectId(maybeId) + + called('getProjectAsset', { projectId }) + + return route.fulfill({ + // This is a mock SVG image. Just a square with a black background. + path: join(__dirname, '../mock/example.png'), + }) + }, + ) await page.route('mock/svg.svg', (route) => { return route.fulfill({ body: MOCK_SVG, contentType: 'image/svg+xml' }) diff --git a/app/gui/integration-test/dashboard/assetPanel.spec.ts b/app/gui/integration-test/dashboard/assetPanel.spec.ts index 9282cf573724..37e8ffd05102 100644 --- a/app/gui/integration-test/dashboard/assetPanel.spec.ts +++ b/app/gui/integration-test/dashboard/assetPanel.spec.ts @@ -5,7 +5,7 @@ import { EmailAddress, UserId } from '#/services/Backend' import { PermissionAction } from '#/utilities/permissions' -import { mockAllAndLogin } from './actions' +import { mockAllAndLogin, TEXT } from './actions' /** Find an asset panel. */ function locateAssetPanel(page: Page) { @@ -87,4 +87,25 @@ test('Asset Panel documentation view', ({ page }) => await expect(assetPanel.getByTestId('asset-panel-tab-panel-docs')).toBeVisible() await expect(assetPanel.getByTestId('asset-docs-content')).toBeVisible() await expect(assetPanel.getByTestId('asset-docs-content')).toHaveText(/Project Goal/) + await expect(assetPanel.getByText(TEXT.arbitraryFetchImageError)).not.toBeVisible() })) + +test('Assets Panel docs images', ({ page }) => { + return mockAllAndLogin({ + page, + setupAPI: (api) => { + api.addProject({}) + }, + }) + .do(() => {}) + .driveTable.clickRow(0) + .toggleDocsAssetPanel() + .withAssetPanel(async (assetPanel) => { + await expect(assetPanel.getByTestId('asset-docs-content')).toBeVisible() + + for (const image of await assetPanel.getByRole('img').all()) { + await expect(image).toBeVisible() + await expect(image).toHaveJSProperty('complete', true) + } + }) +}) diff --git a/app/gui/integration-test/dashboard/mock/example.png b/app/gui/integration-test/dashboard/mock/example.png new file mode 100644 index 0000000000000000000000000000000000000000..b4d6d8b3cb9ce9011614edf353a4ede9919f057e GIT binary patch literal 9033 zcmbt)XH-)`+iff;N>wS+1(YsTL8*cu5}I@bLJrx1OlN@d9J7pfn2%+ zzFU!A1fN@C?Y|+AI}jB`dEIwu_-QW_b>i98e}heLO2lcPyu7N6FO4GalUs(7{xam~ zNUo^(J|`6`@(A6=bjV^^3l7Os?K^^E7_HJAD%&*3jA7(3f!JE$fY5 zNE_d_aDI4h(@I?BCA+FeH50 z@cd`S5C|Ck;8l=!}7` z?oTUi!K62@&0oK=du2Bq*te)nlZThQ2npN+YaIw9)Dlltcgu{cwyy1)wV?TUz06+n z(x~25V{yPMi@oh)Kh zmMXO0!^Q5QOJhZ5rciVr#+>2;WKJv;LN>>itG$AGGj4E7cX-^{XNO{8rYy(~RvO9I z$5~qoh#TLbZt+XqUv>Zdw9;FECk|92h4n2R$WbqmJ>Ry zCP)N_@Kc746k4>84QR}?HhZp2RoTXI`O0Sc>v6@=($jOxskD0I5BsrIkDXjpRaI%* zUcy?XpPp{(M9_J>LsLVZ(BAV=IY_{ck3NA1Im|xTcS6KXkx8*c6D@QO&_qXk(ox8Qo*LP z-dBaZp@Kj%89;aiQM<^A*p8(UD@cm zSNyR7yS?z++mw)ha7&?=)r!FFL)F8>)%W`R{f-Z=r4JAN@Es@(AW>%=Tqt6@hDc6! zd+Tr}$}hz)m8ya69jF$TaKf*5yt2sVg9HlEozhb^YjDNkKmYcT+7nEFWo^eO4UawJ zkrPL1j09=ZIJ>zeOYC=h;3tQ|XlZ}6D@WVN`spW$T>=qAxy|;WTvbIKTjx`u$Pu$y zJ+Xdac5jyG+ zBtF+ji)54;t^g@ioPo#nr|J}F=*ga~VoFeR5Pm*ByWV(S0=_GX$p#MRJrnp*ko&J_ zclp=Ct;>cC3=AKyP}~et4EnM*UXhxdOwJ^}Zb^R~^1$uJ2aP`8LxIy7KW}Quq`q_! zzi{`Od`6){n|;R&){ULJh83b?g&Qb)Aue`fMVB>ZHd5^=%7UMMGTCI*Hc zPq^sBJZCrJzD-tfWMrgjlAwrA^41u=kd%~<&{Kc^Hiat^5)y(77J8nZ`mNqPmiezz-025DTnUf=M?ut|ymTpWp8{t2{J3DQaUhM2ERiq8^s9MS`PiXeSYmT^B_kt4zQ6?Kj{JN<|J}QHn=`Fz zCUu9(JTx0Ktw_7WdIzk+)2DKNe!7I7L{&!VcmIHyra*N^zfi0B|SJ?t9T=s3vg|HqK1l9`nj4K_)|?ogQxrX#q#K_IgAxk zYTKwMFaJG;Q@QIii!r{0(d!l|yO)>OuV26RH>OpeKNl7k_i%Mx8!Iy&%#g9Pw4`E@ zRJ1X!wo<`sl5PC1;(Sc35ZAqQ>7$uHG0!fey1E)<&oENPd*?L(*p-ojOj*BPG#w-3 zWTUgC!l$@6owsj=+3!k7crNxPxP^#VcDZessJj-u;Awfc_3G8DKOe7BiHeFcirX*G z&CM+>E#dL75tBx{sVk7?=U1{ye(Y~JO-$;YE=~5V9rW-#)ZpL|Lb)Gp&zV_tp3qt^VMS&d$0wf2jcQ-tU|}g6t{Y*M@$Qhu z9BeJ9tE;a}m&1_$xf1fg5|(sybb8S#4{V1Ich?ZS6B84d#MF!oLCx8E2PQEYF0On# zXDN?$b6nWx&!(;3?sZN@CbMmRi&tg+kCPE?HiH?(+$f)YM{K=AiHs%)Dpwa58bXOD z6J(hS{1P=f*2g zr>iR~Mbx3cHl~qF8yf?@daYQ<6C1!xraPHS+bwFE_ynbR=py#lWh&3+=IuV_CcS)F zGFFa!%WE|6nL_-?mjG4ds0yz;4eg2JZm5`uWDt{m7gCHuCJC9DHaN0~ErNJ8C^yOW zWMN?`&`4)wW?ujEM}eD;p1$H`O^Dae4)?O~Av`xXR9mE7)7aV3Az;#2xkt6U*p~!Mo{i%APf!C2 zxGadHzJ&lj?6vDL!2mAc5v)vVm(jO6jXeI@u57l{V!;LN2sq2tqUkKgUV?)}^f9a{ zj?)xX&y+psjG*U;Sq4aPa$@ zENoa~O~Eoo8vH{gj!T3S@RvSdLtqu_!=07-`ugh_9zj9<(^yVUPK4GcY1~_PcQ~c+ zB+}FAerANi3bgGS91gFs?thhUXl#7->eUJuQvA`Q-;>-pWU+#R4kiXuuAuT?>z(9?tMVFGOl zLJV%O=1hp=*3?ecX{V>58^+p;D{Go*_D_ySWoFLitEc|OmY0`vYosktHHl5vB*w?z zjq6#?{M&G5tQ14dL3170V#T9T*6*;RqvMkzhmVhseTl5?HuIfg38_4i5eTS;2)Kh!m^o~Hah>Y(d+LQr{Qp0 z6(kHOVepZ#SAK%c-UsvHbh1AekDVj?c#%a<&xx?4AIF7t#_ivorv3Dv(n zn5!lg4s}v^^@^j_0S@nsEGK2>;^G290lo6%x=gIRygXWanGBTTJ2Ln0vj>^FYssJ$ z5KfIvO~iA5uu-S>Q<9NktKno~%^bzES0Iv<_YJdvz^i5bfeD-1A<5-{xP~*F=Qo55JUx1GqBUJ z|HZ7Od2XJvvN_n>qkWInDa5SNh44QvQc}niQ@fG;D24DJ3;y87oz2CcF`S+oQ(i;@ zoakt3I#Ue?`DTn;UzhgYSq8``VgLJ!mw*@@!=txZ2?+@bF9CFLK8wuCY_h~vq$~XH zAXkr!iaI$t0h92~Mnyz`n)}w(wPewruk{-G{kFfqzuuCWu$b8P?ylZ)lTH_?G)w)d z%F(lvRhFSG$T+VmN=iz`G%ayn-mn=~Nw>mS=j`H!3xS`3Tx>20OUMfjCZ1yKXd zmy)4nWYo2^eg8gFLv2HJgyy^5)!k0jhq7XMcX3ljo(q3EBlSh3u()T>RtHIxNEyI% z-YDeGw-h7o*Y@H^Q%$b#-@iw*@+&-tLVN#oMi`VC?Jh*i3BsD=Sx35jcS_yY#xNNp z6BCG;0W_NCdhm7pJ-vkZcwv5XC#Tu3N)c&k%#!e-EP1y;CDMs{2V*?m@ccP7G~t3! zFB}fn9zv1By&N7HNlrnbuBvJ&e)$Rs3C8)<6F0x}*EqMCr-rL|=G@`4mxo{>&qB(K z>T^BgGILw}w?n9Z%)dR{U(4Cc%~6wPtxzJ9B03o>1q&bAk#A!@JF>`56!W@wENty< z&IWAb`m>*g@CNS*1tcUsAjn-NncEZX4EY{zQM72FRb_gfo=3J zH!m-5z+iK3Ma9zMis?Rw>^&i&id)m;?==)sc*v`4fEq==A7QN3zaZ*0^whi2c-d4h+}!Z{p0J`T_$m|;DjJ*SFsT#xxt!R`%+UO)`(65!HsQy`n6z^DyfQ!I`Xz( zFl%65K{uP6VAjUT$FQ)laXXN-dfgnP>_TR3$GB9d5DF%7_f=gSRa0xLUo@-CGR%9m zXudsE>?k^4BOR2jDQ?Y-Pn~}Nbb>+;cu|^v!dD)gRq8`6Azi4N8c24%g>S`Rnl5J8 z-_1?7n>S-qQuyd1MuvwK6clJORCF<*YCcM{yV5y1F|musHrO@)fPO|3pOitOU~Eh^_4M?B$`7hH@b~6$o+{vsh&8^OnEyP#{6^LYy1)O5?@>Sgw^Zy|f4JAi zU<4~GtPMY&)Nk$6?3wY{a`G_B*u_=vc7X!v{M$#Ztv-SWE8Wj1m~JZY@$q>mult-H zlcay|8=1!B9V#8_iu&xWPl}ZS_6k;a=j9L+8|m*a%gqg<;T3R~3dDO^#d5Hbdqjw;439u?dfq;4^*!bYv6g+4V2 z2si`G9@NRLchV0X&m*1=NPHTl-Ocgw;2Uf4iHRFiEHNpG8l>~vMA?s5j;kMZ)HhxC z3qCwNyl~1%FPMxiwbfAoL}8-Vy*oJK*N$cq0x?mU{nXCO9AEQiZtfd-AdoWt2lJsI zFq#@0FO!gna9?5)Hixvz1f2TfuJgI*UAn=C=zT=bOtR#sMeQq3_)3Wys6=wP^aJ$r@{B+HgEu@arg>Fy~R59tX=Ao}`8 zZEg_`6s!=Gp*1m;vB?sCY27TWCnF=HpvZI5J@+6C^i2aF)!RRICn)-LwUA3V zbq=J-2m$ir`_M#8XXH%Kv=RRF_i~IsOPh& z)64a%1~Xxc;pd_YDLWv$N0iKyE$&xqV%i7lz2*)!K?cj7z>E7wM|Xg-HmtQ&ZDt&jMz@2+;Lmu}46ziP;QT;S4qb ziGHbDr}^U2a!N~!Cw~B$jOVGQh@}Y5!vT9)oq4t!NuPOUAobo*V4&acNRpcyl~oCp zC11?H-2*NPMnfZU$FbV5sw$BavGd;X=k)od0WNQ8dRQH~ZB z6U&qf5TuI$8qx91n>8FxAHQtR6{Z-Qcz#&}CeZA@Mun@O4^ptu){c#diHVDg%eNjk zskZ6?IJSrt7Z(S8sMAT zk==}A9R@vCpe;cD+Kf@nO4#K#l9QfHYkf)tL{P}Ix!Pj8eo4sm`ECBy+l-6?6hyCo z-<(7+4|P>JVt!9d{8EL2CTEcU3kYPD1vE+n`Ud=WHy>}aau<9$YeLr)6&0O*nxEal zPuGSe#Kf>ick7TA-@O@@)zZ?~c=z5t_kh3iQl1Nos?5V^v|52C1>=I8HeO!rU#ApE z>@4|@e}Nu3B5p5@jg3u~-GmfUT%h|VX9c$jJ(%KGgcYZ2lKG&5C-OD{Q0m#&QR#)XhGW>thOdd{gnwEFp_ zu#kti2bQx#gkOZn27t4IQ+ckq*1kbGkK@rX7G!U4Z?Sb2Pk8sj$`;W?FzH3SB1d^C zjh?QqZd3^=yS+HJul>YKEWhr}Q82mmhv>gRO@f|M!RA-c#KdeN6d^-`z&x0~pXlSq ze^H>rch63pQ$;I2`~CNhZ&mLXx)>Q4{0|^Q=dfi~`#Q!K3ACkhMfn`bWPADh>6^5QrHc*gEt^m?He5{7c&jK#PT{t6f~Tq0sPq+C^;{ZJ_B{ zldo=HTGo$NqoAMwaVLBO9DAH|>Hi$P9C(U@@&EI{#-hpwLczl))SN_ncF@mRrC5q@ z_O0UUpnX-Oz2@+d(U1n-A>m>>+?J!xt>dTTU~n~S25jI3x2I`GIg-(PbzY^4CY#px z+9QDh>ZI%%@6%<+wQPJdWYp3yg8GU2;#UHR4bG{p>V_G|0pSJB!;>`z{wrmpORH~? zf{@*t;5f-WUN4cjew!xFOC3vl;owuV)Xt|A->xqT{YoX_|2SN?-P zomQsXvtl>TD6hV_1a3}nK(+1;#&1uSlYA?9d)mW>b#lUZ+=r~Px!t%}n6F@B(J;z| z5->ZTSy6!eeumJ%@WQe!k^{1P&8QNh>&i2K=WB^~RR1f9p*LB0qfu@moEW;PY=@+S z?2>~8Flhl_N76r5)og?-s62?ave0~JWZz+spb7`96^_xHOaSOLUJC5<6@%p zG6B1@5~`>ZAtkM#FCOY9<>xk(NeDmcP=lphj#kFlAAHhI#12b*>vsTG~$X{Q~p z8n?HUk>*}+@5i@XdtvY!`dcX?>>+z@QqKON#hE8zeyg%%4YyB>MwW$2Q`^su?yfJ0 zX+))i_$Nixq*A+xt~3&&Od*iVupmfrwRoME=|44W-BA8l!q?^6J_7y^;gx)zxr^0M z-pVf;C5n^jg2u1!-Xg!l6W|+bl4N`&SG(B=t}N_>%=?55O)b}3X7@s_Dhix({zWg0 zv@*9EKlF%29pX-yqTOM+qAQD=739tg)e^EFs5#<#rb?xZQjN@@t>(5?h>RrJ(N52S zq6ze&08?cewKi|j6a#YM-Eh@Q8Jk-QU7Ku2Hoab&QbNYA$f~1@9JeSB>)AZs{rDx= zKfQ9vZ<0vLM?!>?Fw-r$Z@n?2H>2yUepo~C>9*g&cXrAM&cyK42d@=Kn~p>U*^MVG zjLq)8(P45hxR8}`egK=IA)f9g{SDf$eob090}-ne%NbAFB;e@bEY7YNox@#7L)xjJ z@H{5#%Rr(*iZW>re;;R13%`#tsn^lz{m|X^ih$X6gn{(B7dDdGA+8nma>PxcVWBFrZKIqsp_bPXr zn&0!5dw-J_Q3AdZKeaAKNIB4CcleRPeFyAI_u=mrtZ?BiNn-Zyt@9vzbzBK4O1wFUAsqkE3 zto5S&_Z0RwHoIx90}`dVU+-=#!XyV1cKDz9=J?8g4<04!^6n~NONah!Gtf1qk#w8h zpZ0$wEI3@EG_Gw?WXQKyyKU9wD@ocQ6IeX-0a!nV>E^vR}1EuYPS`GN4#(&$May*1ix*pp)%m< z(=9C5$Vo-%={V!%e0C26kWrVO!gDPzt#32Gu*xT_IOM6Vf4(B0myj=t-TEAJ3_2$O zj`EuABz624dS!Jrz+wDiR>pU=4G16B<4s>=&g>f`U8WRkDtByn{IQ(cdMUDL(>3&1 z)-XE9fW5p>JQ~r4XxY}Fz+J(Se7H#5%f6;8jc-X2vgMjxtZwq9MCls#qGz8}$g3!Y zdBgp@#EA|We`rhbMvzglWsb`)CTIrd{0C0zU=f3`yj<<502GD>7IQ`srA@^Gb0A>X z*F^0Dj!h5m#VF=dgQgBO?AKw#DcvvHK*n=}f3EHo&1CtQ{4_XA(8##{hKdq{*9sf* z4oEF%m2kn`B}@7p`0~aOryYUeX-0NE|HT();K=cXPE(=pcLXuj$-X0rv`v~uN&U>b zD_ov`x9M=Qoa{*}I9$!r*l76#^L)57+8+0=n5~VRna<`@-_T2$0JnzSArBa2RRnMn z#uvJ#ZbJO078=?`mAc`Zqsr|TRf{@$-@CtAxK0|?t-T)U(Vf-Bx?d>V|L`(oif{F#qsBUlPH5 literal 0 HcmV?d00001 diff --git a/app/gui/src/dashboard/components/MarkdownViewer/MarkdownViewer.tsx b/app/gui/src/dashboard/components/MarkdownViewer/MarkdownViewer.tsx index 709d617623b8..ebd199958086 100644 --- a/app/gui/src/dashboard/components/MarkdownViewer/MarkdownViewer.tsx +++ b/app/gui/src/dashboard/components/MarkdownViewer/MarkdownViewer.tsx @@ -1,5 +1,6 @@ /** @file A Markdown viewer component. */ +import { useLogger } from '#/providers/LoggerProvider' import { useText } from '#/providers/TextProvider' import { useSuspenseQuery } from '@tanstack/react-query' import type { RendererObject } from 'marked' @@ -23,6 +24,7 @@ export function MarkdownViewer(props: MarkdownViewerProps) { const { text, imgUrlResolver, renderer = defaultRenderer, testId } = props const { getText } = useText() + const logger = useLogger() const markedInstance = marked.use({ renderer: Object.assign({}, defaultRenderer, renderer) }) @@ -36,7 +38,15 @@ export function MarkdownViewer(props: MarkdownViewerProps) { const href = token.href token.raw = href - token.href = await args.imgUrlResolver(href).catch(() => null) + token.href = await args + .imgUrlResolver(href) + .then((url) => { + return url + }) + .catch((error) => { + logger.error(error) + return null + }) token.text = getText('arbitraryFetchImageError') } }, diff --git a/app/gui/src/dashboard/components/MarkdownViewer/defaultRenderer.ts b/app/gui/src/dashboard/components/MarkdownViewer/defaultRenderer.ts index 032da7bf4f5d..ac2a909e2987 100644 --- a/app/gui/src/dashboard/components/MarkdownViewer/defaultRenderer.ts +++ b/app/gui/src/dashboard/components/MarkdownViewer/defaultRenderer.ts @@ -26,13 +26,11 @@ export const defaultRenderer: RendererObject = { return `${this.parser.parseInline(tokens)}` }, /** The renderer for images. */ - image({ href, title, raw, text }) { + image({ href, title, raw }) { const alt = title ?? '' return ` - - ${text} - + ${alt} ` }, /** The renderer for code. */