alert(1)'), 'e() échappe les balises HTML' ); assertEquals( '"guillemets" 'apostrophes'', e('"guillemets" \'apostrophes\''), 'e() échappe guillemets et apostrophes (ENT_QUOTES)' ); assertEquals('&', e('&'), 'e() échappe l\'esperluette'); assertEquals('', e(null), 'e() convertit null en chaîne vide'); assertEquals('42', e(42), 'e() convertit les nombres en chaîne'); // --- renderVideoCard : structure de base ------------------------------------- $video = [ 'id' => 'abc-123', 'title' => 'Ma vidéo', 'thumbnail' => 'https://videos.example/lazy/abc.jpg', 'duration' => 125, 'channel' => 'Ma chaîne', 'channelAvatar' => 'https://videos.example/avatars/a.png', 'views' => 42, 'date' => date('Y-m-d H:i:s'), ]; $html = renderVideoCard($video); assertContains('class="video-card"', $html, 'renderVideoCard génère une carte vidéo'); assertContains('data-video-id="abc-123"', $html, 'renderVideoCard expose l\'identifiant vidéo'); assertContains('src="https://videos.example/lazy/abc.jpg"', $html, 'renderVideoCard affiche la vignette'); assertContains('

Ma vidéo

', $html, 'renderVideoCard affiche le titre'); assertContains('Ma chaîne', $html, 'renderVideoCard affiche la chaîne'); assertContains('class="channel-avatar"', $html, 'renderVideoCard affiche l\'avatar personnalisé'); assertContains('2:05', $html, 'renderVideoCard affiche la durée formatée'); // --- renderVideoCard : avatar par défaut -------------------------------------- $videoDefaultAvatar = $video; $videoDefaultAvatar['channelAvatar'] = 'img/default-avatar.png'; $htmlDefault = renderVideoCard($videoDefaultAvatar); assertContains('channel-avatar-placeholder', $htmlDefault, 'renderVideoCard utilise un placeholder pour l\'avatar par défaut'); assertNotContains('class="channel-avatar"', $htmlDefault, 'renderVideoCard n\'affiche pas l\'image d\'avatar par défaut'); $videoEmptyAvatar = $video; $videoEmptyAvatar['channelAvatar'] = ''; assertContains( 'channel-avatar-placeholder', renderVideoCard($videoEmptyAvatar), 'renderVideoCard utilise un placeholder si l\'avatar est vide' ); // --- renderVideoCard : échappement XSS (SEC-1) --------------------------------- $maliciousTitle = '">'; $maliciousChannel = ''; $maliciousThumbnail = 'https://videos.example/x.jpg" onerror="alert(4)'; $maliciousAvatar = 'https://videos.example/a.png\' onerror=\'alert(5)'; $htmlXss = renderVideoCard([ 'id' => $maliciousTitle, 'title' => $maliciousTitle, 'thumbnail' => $maliciousThumbnail, 'duration' => 60, 'channel' => $maliciousChannel, 'channelAvatar' => $maliciousAvatar, 'views' => 1, 'date' => date('Y-m-d H:i:s'), ]); assertNotContains($maliciousTitle, $htmlXss, 'renderVideoCard ne ressort pas le titre brut'); assertNotContains($maliciousChannel, $htmlXss, 'renderVideoCard ne ressort pas la chaîne brute'); assertNotContains($maliciousThumbnail, $htmlXss, 'renderVideoCard ne ressort pas la vignette brute'); assertNotContains($maliciousAvatar, $htmlXss, 'renderVideoCard ne ressort pas l\'avatar brut'); assertNotContains('