filter(fn ($r) => in_array('GET', $r->methods(), true) && $r->getName() !== null && ! str_contains($r->uri(), '{') && ! collect($r->gatherMiddleware())->contains(fn ($m) => is_string($m) && str_starts_with($m, 'auth'))) ->reject(fn ($r) => str_starts_with($r->uri(), 'horizon') || str_starts_with($r->uri(), '_') || $r->uri() === 'up') // Redirections voulues (ex. /statut vers le site de statut). ->reject(fn ($r) => $r->getActionName() === '\\Illuminate\\Routing\\RedirectController'); expect($pages)->not->toBeEmpty(); $echecs = $pages->mapWithKeys(fn ($r) => [$r->uri() => $this->get('/'.ltrim($r->uri(), '/'))->getStatusCode()]) ->reject(fn ($code) => $code === 200); expect($echecs->all())->toBe([]); }); // Étape 14 : la politique de confidentialité affirme qu'aucune page ne fait // partir l'adresse IP du visiteur chez un fournisseur de polices. test('aucune page publique ne charge de police depuis un service externe', function () { $pages = collect(Route::getRoutes()) ->filter(fn ($r) => in_array('GET', $r->methods(), true) && $r->getName() !== null && ! str_contains($r->uri(), '{')) ->reject(fn ($r) => collect($r->gatherMiddleware())->contains(fn ($m) => is_string($m) && str_starts_with($m, 'auth'))); foreach ($pages as $route) { $html = $this->get('/'.ltrim($route->uri(), '/'))->getContent(); expect($html)->not->toMatch('#fonts\.(googleapis|gstatic|bunny)\.(com|net)#', "page /{$route->uri()}"); } }); test('aucune feuille de style publique n’importe de ressource externe', function () { foreach (glob(public_path('css/*.css')) as $fichier) { expect(file_get_contents($fichier))->not->toMatch('#@import\s+url\(\s*["\']?https?://#', basename($fichier)); } }); // /ide a eu une erreur de syntaxe JavaScript de l'étape 8 à l'étape 14 (des // « !! » devenus « . » dans la maquette) : chaque script en ligne de chaque // page publique est maintenant analysé par Node. test('les scripts en ligne des pages publiques sont du JavaScript valide', function () { if (Process::run('node --version')->failed()) { $this->markTestSkipped('Node absent'); } $pages = collect(Route::getRoutes()) ->filter(fn ($r) => in_array('GET', $r->methods(), true) && $r->getName() !== null && ! str_contains($r->uri(), '{')) ->reject(fn ($r) => collect($r->gatherMiddleware())->contains(fn ($m) => is_string($m) && str_starts_with($m, 'auth'))) ->reject(fn ($r) => $r->getActionName() === '\Illuminate\Routing\RedirectController'); $erreurs = []; foreach ($pages as $route) { preg_match_all('#]*\bsrc=)[^>]*>(.*?)#s', $this->get('/'.ltrim($route->uri(), '/'))->getContent(), $scripts); foreach ($scripts[1] as $i => $code) { $fichier = tempnam(sys_get_temp_dir(), 'js').'.js'; file_put_contents($fichier, $code); $resultat = Process::run(['node', '--check', $fichier]); unlink($fichier); if ($resultat->failed()) { $erreurs[] = "/{$route->uri()} script {$i} : ".strtok($resultat->errorOutput(), "\n"); } } } expect($erreurs)->toBe([]); });