@@ -1125,6 +1125,58 @@ describe("faces.ajax.response", () => {
11251125 expect ( created ! . value ) . toBe ( "sharedState" ) ;
11261126 otherForm . remove ( ) ;
11271127 } ) ;
1128+
1129+ test ( "update jakarta.faces.Resource injects new <link> stylesheet into head" , ( ) => {
1130+ const beforeLinks = document . head . querySelectorAll ( "link" ) . length ;
1131+ ajax ( ) . request ( button , null , { render : "testForm" } ) ;
1132+ const href = "/test/jakarta.faces.resource/issue4345.css.xhtml?firstParam=1&secondParam=2" ;
1133+ const xml = successResponse (
1134+ `<update id="jakarta.faces.Resource"><![CDATA[<link type="text/css" rel="stylesheet" href="${ href } " />]]></update>`
1135+ ) ;
1136+ lastXHR ( ) . respond ( 200 , "" , xml ) ;
1137+
1138+ const links = document . head . querySelectorAll ( "link" ) ;
1139+ expect ( links . length ) . toBe ( beforeLinks + 1 ) ;
1140+ const injected = links [ links . length - 1 ] ;
1141+ expect ( injected . getAttribute ( "rel" ) ) . toBe ( "stylesheet" ) ;
1142+ // href is unescaped before being assigned to the DOM property
1143+ expect ( injected . href ) . toContain ( "issue4345.css.xhtml?firstParam=1&secondParam=2" ) ;
1144+ injected . remove ( ) ;
1145+ } ) ;
1146+
1147+ test ( "update jakarta.faces.Resource skips <link> already present in head" , ( ) => {
1148+ const href = "/test/jakarta.faces.resource/already-loaded.css.xhtml" ;
1149+ const existing = document . createElement ( "link" ) ;
1150+ existing . setAttribute ( "type" , "text/css" ) ;
1151+ existing . setAttribute ( "rel" , "stylesheet" ) ;
1152+ existing . setAttribute ( "href" , href ) ;
1153+ document . head . appendChild ( existing ) ;
1154+
1155+ const beforeLinks = document . head . querySelectorAll ( "link" ) . length ;
1156+ ajax ( ) . request ( button , null , { render : "testForm" } ) ;
1157+ const xml = successResponse (
1158+ `<update id="jakarta.faces.Resource"><![CDATA[<link type="text/css" rel="stylesheet" href="${ href } " />]]></update>`
1159+ ) ;
1160+ lastXHR ( ) . respond ( 200 , "" , xml ) ;
1161+
1162+ expect ( document . head . querySelectorAll ( "link" ) . length ) . toBe ( beforeLinks ) ;
1163+ existing . remove ( ) ;
1164+ } ) ;
1165+
1166+ test ( "update jakarta.faces.Resource handles multiple <link> tags without throwing" , ( ) => {
1167+ const beforeLinks = document . head . querySelectorAll ( "link" ) . length ;
1168+ ajax ( ) . request ( button , null , { render : "testForm" } ) ;
1169+ const xml = successResponse (
1170+ '<update id="jakarta.faces.Resource"><![CDATA[' +
1171+ '<link type="text/css" rel="stylesheet" href="/test/a.css" />' +
1172+ '<link type="text/css" rel="stylesheet" href="/test/b.css" />' +
1173+ ']]></update>'
1174+ ) ;
1175+ expect ( ( ) => lastXHR ( ) . respond ( 200 , "" , xml ) ) . not . toThrow ( ) ;
1176+
1177+ expect ( document . head . querySelectorAll ( "link" ) . length ) . toBe ( beforeLinks + 2 ) ;
1178+ document . head . querySelectorAll ( 'link[href^="/test/"]' ) . forEach ( el => el . remove ( ) ) ;
1179+ } ) ;
11281180} ) ;
11291181
11301182// ---- HTTP error codes ----
0 commit comments