corrige /ide, sert Mulish depuis le portail et complète les mentions légales
/ide avait une erreur de syntaxe depuis la maquette et un aperçu hors de son bac à sable. Mulish était encore importée depuis Google par la feuille de thème. Nouveaux tests : syntaxe des scripts de chaque page, feuilles de style sans import externe. public/vendor exclu du rsync. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
e4a6aaebb1
commit
1ac8d4773a
10 changed files with 77 additions and 15 deletions
|
|
@ -41,6 +41,9 @@
|
||||||
- "--exclude=/storage"
|
- "--exclude=/storage"
|
||||||
- "--exclude=/bootstrap/cache"
|
- "--exclude=/bootstrap/cache"
|
||||||
- "--exclude=/public/build"
|
- "--exclude=/public/build"
|
||||||
|
# Produit sur le serveur par `npm run build` (scripts/copier-vendor.mjs),
|
||||||
|
# donc appartenant à root : ne jamais le synchroniser depuis le Mac.
|
||||||
|
- "--exclude=/public/vendor"
|
||||||
- "--exclude=/public/hot"
|
- "--exclude=/public/hot"
|
||||||
- "--exclude=/.git"
|
- "--exclude=/.git"
|
||||||
register: portail_code_synchronise
|
register: portail_code_synchronise
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,14 @@ retirer la ligne (et le noter dans le journal) quand c'est fait.
|
||||||
| Vitrine : 52 scripts en ligne ; Pyodide et sql.js depuis jsDelivr (`/ide`) ; vidéo YouTube possible (`/cours/…/cm`) | CSP vitrine avec `'unsafe-inline'` ; tiers déclarés dans `/confidentialite`. Polices, xterm.js et CodeMirror sont auto-hébergés depuis l'étape 14. Héberger Pyodide/sql.js, passer les scripts en fichiers. | Avant ouverture publique | Étapes 12 et 14 |
|
| Vitrine : 52 scripts en ligne ; Pyodide et sql.js depuis jsDelivr (`/ide`) ; vidéo YouTube possible (`/cours/…/cm`) | CSP vitrine avec `'unsafe-inline'` ; tiers déclarés dans `/confidentialite`. Polices, xterm.js et CodeMirror sont auto-hébergés depuis l'étape 14. Héberger Pyodide/sql.js, passer les scripts en fichiers. | Avant ouverture publique | Étapes 12 et 14 |
|
||||||
| xterm.js 5.5.0 (6.0.0 disponible) | Version majeure à évaluer séparément. | Bloc B | Étape 12 |
|
| xterm.js 5.5.0 (6.0.0 disponible) | Version majeure à évaluer séparément. | Bloc B | Étape 12 |
|
||||||
| NodeSource installé par `curl … \| bash` | Script distant exécuté en root au premier déploiement. Déclarer le dépôt (deb822) avec une clé vérifiée. | Bloc B | Étape 8 |
|
| NodeSource installé par `curl … \| bash` | Script distant exécuté en root au premier déploiement. Déclarer le dépôt (deb822) avec une clé vérifiée. | Bloc B | Étape 8 |
|
||||||
| Aucune vérification dans un vrai navigateur | Extension Chrome non connectée (étapes 8, 12 et 14). La CSP n'est contrôlée que par ses rapports. À vérifier en priorité : `/ide` (Pyodide, WebAssembly) et les pages vitrine qui utilisent xterm.js/CodeMirror désormais servis par le portail. | Dès que possible | Étapes 8, 12, 14 |
|
| Aucune vérification dans un vrai navigateur | Extension Chrome non connectée (étapes 8, 12 et 14). La CSP n'est contrôlée que par ses rapports. Adam a testé `/ide` le 2026-10-09 (erreurs corrigées) ; à revérifier : exécution Python et SQL de `/ide`, pages vitrine utilisant xterm.js/CodeMirror servis par le portail. | Dès que possible | Étapes 8, 12, 14 |
|
||||||
| Pas de sonde hors du serveur | Si le serveur tombe, personne n'est prévenu. | À décider avec Adam | Étape 7 |
|
| Pas de sonde hors du serveur | Si le serveur tombe, personne n'est prévenu. | À décider avec Adam | Étape 7 |
|
||||||
| Kolla installé depuis une branche, pas un tag | Reproductibilité. | Montée de version | ADR 0001 |
|
| Kolla installé depuis une branche, pas un tag | Reproductibilité. | Montée de version | ADR 0001 |
|
||||||
| Correction séquentielle (~5 s pour le lab 08) | Acceptable ; à paralléliser si la charge l'exige. | Bloc B | Étape 11 |
|
| Correction séquentielle (~5 s pour le lab 08) | Acceptable ; à paralléliser si la charge l'exige. | Bloc B | Étape 11 |
|
||||||
| Jeton de console : ≈ 1,1 s par appel Nova `remote-consoles` | Plus gros poste de l'ouverture d'un lab (2,8 s au total, critère < 5 s tenu). Préparer les jetons à l'attribution si besoin. | Bloc B | Étape 13 |
|
| Jeton de console : ≈ 1,1 s par appel Nova `remote-consoles` | Plus gros poste de l'ouverture d'un lab (2,8 s au total, critère < 5 s tenu). Préparer les jetons à l'attribution si besoin. | Bloc B | Étape 13 |
|
||||||
| Tâches Ansible toujours « modifiées » (composer, npm, build, cache) | Brouille la lecture de l'idempotence. | Bloc B | Étape 8 |
|
| Tâches Ansible toujours « modifiées » (composer, npm, build, cache) | Brouille la lecture de l'idempotence. | Bloc B | Étape 8 |
|
||||||
| `Permissions-Policy` ferme caméra et micro | À ouvrir pour la visio. | Étape 15 | Étape 12 |
|
| `Permissions-Policy` ferme caméra et micro | À ouvrir pour la visio. | Étape 15 | Étape 12 |
|
||||||
| Mentions légales à compléter | Forme juridique, SIREN et adresse du siège d'Esquisse Formation ; délégué à la protection des données s'il en est désigné ; article « Disponibilité et responsabilité » des CGU. | Avant le pilote | Étape 14 |
|
| Mentions légales à compléter | Article « Disponibilité et responsabilité » des CGU ; délégué à la protection des données s'il en est désigné (éditeur, SIREN et adresse complétés le 2026-10-09). | Avant le pilote | Étape 14 |
|
||||||
| Slogans « du lycée au master » | Le catalogue va aujourd'hui du lycée au BUT. Positionnement laissé tel quel : décision éditoriale d'Adam. | À décider | Étape 14 |
|
| Slogans « du lycée au master » | Le catalogue va aujourd'hui du lycée au BUT. Positionnement laissé tel quel : décision éditoriale d'Adam. | À décider | Étape 14 |
|
||||||
| Ancien `storage/logs/laravel.log` | Fichier unique d'avant la rotation quotidienne (étape 14) : jamais purgé, à supprimer ou archiver (accord d'Adam). | Dès que possible | Étape 14 |
|
| Ancien `storage/logs/laravel.log` | Fichier unique d'avant la rotation quotidienne (étape 14) : jamais purgé, à supprimer ou archiver (accord d'Adam). | Dès que possible | Étape 14 |
|
||||||
| Pages d'aperçu publiques (32) | Signalées comme aperçus avec données fictives ; à retirer ou remplacer au fur et à mesure que les fonctions réelles arrivent. | Au fil du bloc B | Étape 14 |
|
| Pages d'aperçu publiques (32) | Signalées comme aperçus avec données fictives ; à retirer ou remplacer au fur et à mesure que les fonctions réelles arrivent. | Au fil du bloc B | Étape 14 |
|
||||||
|
|
|
||||||
|
|
@ -600,3 +600,15 @@ Une politique de confidentialité n'est pas un texte juridique à recopier : c'e
|
||||||
|
|
||||||
- Adam : compléter forme juridique, SIREN, adresse du siège (et DPO s'il y en a un) dans `/legal` et `/confidentialite` ; vérifier `/ide` dans un navigateur.
|
- Adam : compléter forme juridique, SIREN, adresse du siège (et DPO s'il y en a un) dans `/legal` et `/confidentialite` ; vérifier `/ide` dans un navigateur.
|
||||||
- Étape 15 : visio de classe — seulement après validation des étapes 1 à 11 (le prompt l'exige).
|
- Étape 15 : visio de classe — seulement après validation des étapes 1 à 11 (le prompt l'exige).
|
||||||
|
|
||||||
|
## 2026-10-09 — Étape 14 (suite) : retours du test d'Adam dans son navigateur
|
||||||
|
|
||||||
|
Adam a ouvert `/ide` et transmis la console du navigateur, plus sa fiche INPI.
|
||||||
|
|
||||||
|
- **`/ide` n'a jamais fonctionné en production** : « Uncaught SyntaxError: Unexpected token '.' ». Dans la maquette `design/` elle-même, des `!!x` (conversion en booléen) sont devenus `.x` (4 occurrences, seulement dans `/ide`, vérifié par une recherche sur tous les scripts). Corrigé dans le portail (pas dans `design/`). **Nouveau test Pest** : chaque script en ligne de chaque page publique est analysé par `node --check` ; vérifié qu'il échoue sur l'ancienne version.
|
||||||
|
- **Aperçu HTML de `/ide` hors de son bac à sable** : `sandbox="allow-scripts allow-same-origin"` faisait tourner le code de l'élève avec l'origine `syllabuslab.fr`. Passé à `allow-scripts` seul ; la vérification automatique s'exécute dans l'aperçu et renvoie ses résultats par `postMessage` (délai de 3 s si la page de l'élève ne répond pas).
|
||||||
|
- **Régression introduite à l'étape 14** : `public/css/syllabus-theme.css` importait Mulish (police principale de tout le site) depuis Google par `@import`. Mon test ne regardait que le HTML ; la CSP durcie bloquait l'appel, donc plus de fuite d'IP, mais le site s'affichait avec une police de remplacement. Mulish ajoutée aux polices servies par le portail, `@import` retiré, test étendu aux feuilles de style de `public/css`.
|
||||||
|
- **Déploiement en échec passé inaperçu** : `npm run build`, lancé en root sur le serveur, a créé `public/vendor/` au nom de root ; le `rsync` suivant (compte `adam`) n'a pas pu y écrire. Mon filtre de sortie n'affichait ni l'échec ni le récapitulatif : je l'ai pris pour un succès, et c'est la vérification en production qui a montré que rien n'avait changé. `public/vendor/` exclu du `rsync` (même piège qu'à l'étape 9). Règle pour la suite : toujours lire la ligne `PLAY RECAP` (et le code de sortie) avant de conclure.
|
||||||
|
- **Mentions légales complétées** d'après la fiche INPI : Adam Belkacemi, entrepreneur individuel (EI), nom commercial Esquisse Formation, SIREN 838 093 268, SIRET 838 093 268 00087, 30 rue Lorenzaccio, 38100 Grenoble. La date de naissance de la fiche n'est pas reprise. Restent à compléter : l'article « Disponibilité et responsabilité » des CGU, et un éventuel délégué à la protection des données.
|
||||||
|
|
||||||
|
Vérifié en production : `/ide` 1 script en ligne, 0 erreur de syntaxe, `sandbox="allow-scripts"` ; Mulish déclarée par le portail ; aucun `@import` vers Google ; `/vendor/xterm/xterm.js` et `/vendor/codemirror/codemirror.js` en 200. 35 tests Pest.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/* Syllabus — thème global. Surcharge les anciennes classes néo-brutalistes pour appliquer la DA "SaaS doux" de la bibliothèque v2. */
|
/* Syllabus — thème global. Surcharge les anciennes classes néo-brutalistes pour appliquer la DA "SaaS doux" de la bibliothèque v2. */
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Mulish:wght@400;500;600;700;800&display=swap');
|
/* Mulish : servie par le portail (Vite::fonts(), vite.config.js), plus par Google Fonts (étape 14). */
|
||||||
:root { --sy-bg:#f5f6f8; --sy-surface:#fff; --sy-line:#b8c1cf; --sy-ink:#111827; --sy-muted:#6b7280; --sy-blue:#1d5cf5; --sy-blue-dark:#174bd0; --sy-navy:#1e3a8a; }
|
:root { --sy-bg:#f5f6f8; --sy-surface:#fff; --sy-line:#b8c1cf; --sy-ink:#111827; --sy-muted:#6b7280; --sy-blue:#1d5cf5; --sy-blue-dark:#174bd0; --sy-navy:#1e3a8a; }
|
||||||
html, body { background: var(--sy-bg) !important; color: var(--sy-ink) !important; font-family: Mulish, system-ui, sans-serif !important; }
|
html, body { background: var(--sy-bg) !important; color: var(--sy-ink) !important; font-family: Mulish, system-ui, sans-serif !important; }
|
||||||
body * { font-family: inherit; }
|
body * { font-family: inherit; }
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
<section id="responsable" aria-labelledby="t-responsable">
|
<section id="responsable" aria-labelledby="t-responsable">
|
||||||
<h2 id="t-responsable">Qui est responsable</h2>
|
<h2 id="t-responsable">Qui est responsable</h2>
|
||||||
<div class="mt-6 grid gap-4 text-lg font-medium leading-relaxed">
|
<div class="mt-6 grid gap-4 text-lg font-medium leading-relaxed">
|
||||||
<p>Syllabus est édité par Esquisse Formation (voir les <a href="{{ route('legal') }}#mentions" class="font-bold underline underline-offset-4">mentions légales</a>).</p>
|
<p>Syllabus est édité par Adam Belkacemi, entrepreneur individuel (EI), sous le nom commercial Esquisse Formation (voir les <a href="{{ route('legal') }}#mentions" class="font-bold underline underline-offset-4">mentions légales</a>).</p>
|
||||||
<p>Quand un établissement utilise Syllabus avec ses élèves, <strong>l’établissement est responsable du traitement</strong> et Esquisse Formation agit comme <strong>sous-traitant</strong>, sur ses instructions. Pour les comptes enseignants, Esquisse Formation est responsable du traitement.</p>
|
<p>Quand un établissement utilise Syllabus avec ses élèves, <strong>l’établissement est responsable du traitement</strong> et Esquisse Formation agit comme <strong>sous-traitant</strong>, sur ses instructions. Pour les comptes enseignants, Esquisse Formation est responsable du traitement.</p>
|
||||||
<p>Délégué à la protection des données : <mark class="bg-[#fef7e0] px-1 font-bold">[à compléter si désigné]</mark>. Contact : adam@belkacemi.me</p>
|
<p>Délégué à la protection des données : <mark class="bg-[#fef7e0] px-1 font-bold">[à compléter si désigné]</mark>. Contact : adam@belkacemi.me</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,7 @@
|
||||||
<button type="button" role="tab" data-onglet="terminal" aria-selected="false" class="border-r border-[#e3e5e8] px-4 py-2 text-white aria-selected:bg-[#fef7e0] aria-selected:text-black">Terminal</button>
|
<button type="button" role="tab" data-onglet="terminal" aria-selected="false" class="border-r border-[#e3e5e8] px-4 py-2 text-white aria-selected:bg-[#fef7e0] aria-selected:text-black">Terminal</button>
|
||||||
</div>
|
</div>
|
||||||
<pre id="p-sortie" data-panneau="sortie" class="h-[220px] overflow-auto whitespace-pre-wrap p-4 font-mono text-sm text-[#E8EDF5]">Clique sur ▶ Exécuter pour lancer ton code.</pre>
|
<pre id="p-sortie" data-panneau="sortie" class="h-[220px] overflow-auto whitespace-pre-wrap p-4 font-mono text-sm text-[#E8EDF5]">Clique sur ▶ Exécuter pour lancer ton code.</pre>
|
||||||
<div data-panneau="apercu" hidden class="h-[220px] bg-white"><iframe id="p-apercu" title="Aperçu de la page" class="h-full w-full" sandbox="allow-scripts allow-same-origin"></iframe></div>
|
<div data-panneau="apercu" hidden class="h-[220px] bg-white"><iframe id="p-apercu" title="Aperçu de la page" class="h-full w-full" sandbox="allow-scripts"></iframe></div>
|
||||||
<div data-panneau="terminal" hidden class="h-[220px] p-2"><div id="p-terminal" class="h-full"></div></div>
|
<div data-panneau="terminal" hidden class="h-[220px] p-2"><div id="p-terminal" class="h-full"></div></div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -217,17 +217,23 @@
|
||||||
afficher([['Requête valide', true], ['Bonnes lignes, dans le bon ordre', obtenu === attendu]]);
|
afficher([['Requête valide', true], ['Bonnes lignes, dans le bon ordre', obtenu === attendu]]);
|
||||||
});
|
});
|
||||||
if (cur.lang === 'web') {
|
if (cur.lang === 'web') {
|
||||||
var f = $('p-apercu'); f.srcdoc = code; onglet('apercu');
|
// La vérification tourne DANS l'aperçu, isolé (sandbox sans
|
||||||
return new Promise(function (ok) { f.onload = function () {
|
// allow-same-origin : le code de l'élève ne peut pas agir sur
|
||||||
var d = f.contentDocument, b = d && d.getElementById('bouton'), m = d && d.getElementById('message');
|
// la page) ; elle renvoie ses résultats par postMessage.
|
||||||
var r = [['Le bouton #bouton existe',.b], ['Le paragraphe #message existe',.m]];
|
var f = $('p-apercu'); onglet('apercu');
|
||||||
if (b && m) { b.click(); r.push(['Le clic affiche « Bonjour ! »', /Bonjour/.test(m.textContent)]); }
|
var verif = '<script>addEventListener("load",function(){var b=document.getElementById("bouton"),m=document.getElementById("message"),r=[["Le bouton #bouton existe",!!b],["Le paragraphe #message existe",!!m]];if(b&&m){b.click();r.push(["Le clic affiche « Bonjour ! »",/Bonjour/.test(m.textContent)]);}parent.postMessage({syllabusVerif:r},"*");});<\/script>';
|
||||||
afficher(r); ok();
|
return new Promise(function (ok) {
|
||||||
}; });
|
var fini = false;
|
||||||
|
function terminer(r) { if (fini) return; fini = true; removeEventListener('message', recevoir); afficher(r); ok(); }
|
||||||
|
function recevoir(e) { if (e.source === f.contentWindow && e.data && e.data.syllabusVerif) terminer(e.data.syllabusVerif); }
|
||||||
|
addEventListener('message', recevoir);
|
||||||
|
setTimeout(function () { terminer([['La page s’affiche sans erreur', false]]); }, 3000);
|
||||||
|
f.srcdoc = code + verif;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
onglet('terminal');
|
onglet('terminal');
|
||||||
var h = shell ? shell.home : '/home/eleve', f2 = shell && shell.files.get(h + '/tp/script.sh');
|
var h = shell ? shell.home : '/home/eleve', f2 = shell && shell.files.get(h + '/tp/script.sh');
|
||||||
afficher([['Le dossier ~/tp existe',.(shell && shell.dirs.has(h + '/tp'))], ['Le fichier ~/tp/script.sh existe', f2 !== undefined && f2 !== null], ['Il contient echo Bonjour',.f2 && /echo\s+"?Bonjour/.test(f2)]]);
|
afficher([['Le dossier ~/tp existe', !!(shell && shell.dirs.has(h + '/tp'))], ['Le fichier ~/tp/script.sh existe', f2 !== undefined && f2 !== null], ['Il contient echo Bonjour', !!f2 && /echo\s+"?Bonjour/.test(f2)]]);
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
document.querySelectorAll('[data-ex]').forEach(function (b) { b.addEventListener('click', function () { charger(b.dataset.ex); }); });
|
document.querySelectorAll('[data-ex]').forEach(function (b) { b.addEventListener('click', function () { charger(b.dataset.ex); }); });
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
<section id="mentions" class="scroll-mt-6 border border-[#e3e5e8] rounded-2xl bg-white p-7" aria-labelledby="t-mentions">
|
<section id="mentions" class="scroll-mt-6 border border-[#e3e5e8] rounded-2xl bg-white p-7" aria-labelledby="t-mentions">
|
||||||
<h2 id="t-mentions" class="text-4xl font-extrabold tracking-tight">Mentions légales</h2>
|
<h2 id="t-mentions" class="text-4xl font-extrabold tracking-tight">Mentions légales</h2>
|
||||||
<div class="mt-6 grid gap-4 text-lg font-medium leading-relaxed">
|
<div class="mt-6 grid gap-4 text-lg font-medium leading-relaxed">
|
||||||
<p><strong>Éditeur :</strong> Esquisse Formation, <mark class="bg-[#fef7e0] px-1 font-bold">[forme juridique à compléter]</mark>, SIREN <mark class="bg-[#fef7e0] px-1 font-bold">[à compléter]</mark>, siège : <mark class="bg-[#fef7e0] px-1 font-bold">[adresse à compléter]</mark>.</p>
|
<p><strong>Éditeur :</strong> Adam Belkacemi, entrepreneur individuel (EI), sous le nom commercial Esquisse Formation. SIREN 838 093 268 (SIRET 838 093 268 00087), immatriculé au Registre national des entreprises. Adresse : 30 rue Lorenzaccio, 38100 Grenoble.</p>
|
||||||
<p><strong>Directeur de la publication :</strong> Adam Belkacemi.</p>
|
<p><strong>Directeur de la publication :</strong> Adam Belkacemi.</p>
|
||||||
<p><strong>Contact :</strong> adam@belkacemi.me</p>
|
<p><strong>Contact :</strong> adam@belkacemi.me</p>
|
||||||
<p><strong>Hébergeur :</strong> SCALEWAY, société par actions simplifiée au capital de 142 050 €, 8 rue de la Ville-l’Évêque, 75008 Paris, RCS Paris 433 115 904. Le service et ses sauvegardes sont hébergés en France, dans des datacenters de Scaleway.</p>
|
<p><strong>Hébergeur :</strong> SCALEWAY, société par actions simplifiée au capital de 142 050 €, 8 rue de la Ville-l’Évêque, 75008 Paris, RCS Paris 433 115 904. Le service et ses sauvegardes sont hébergés en France, dans des datacenters de Scaleway.</p>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
test('les pages légales sont en ligne et la page de statut redirige vers le site de statut', function () {
|
test('les pages légales sont en ligne et la page de statut redirige vers le site de statut', function () {
|
||||||
$this->get(route('legal'))->assertOk()->assertSee('Esquisse Formation')->assertSee('RCS Paris 433 115 904');
|
$this->get(route('legal'))->assertOk()->assertSee('Esquisse Formation')->assertSee('entrepreneur individuel (EI)')->assertSee('SIREN 838 093 268')->assertSee('RCS Paris 433 115 904');
|
||||||
$this->get(route('confidentialite'))->assertOk()
|
$this->get(route('confidentialite'))->assertOk()
|
||||||
->assertSee('90 jours après l’archivage', false)
|
->assertSee('90 jours après l’archivage', false)
|
||||||
->assertSee('aucune adresse e-mail', false)
|
->assertSee('aucune adresse e-mail', false)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Process;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
// Toutes les pages publiques sans paramètre doivent s'afficher : la page
|
// Toutes les pages publiques sans paramètre doivent s'afficher : la page
|
||||||
|
|
@ -35,3 +36,39 @@ test('aucune page publique ne charge de police depuis un service externe', funct
|
||||||
expect($html)->not->toMatch('#fonts\.(googleapis|gstatic|bunny)\.(com|net)#', "page /{$route->uri()}");
|
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('#<script(?![^>]*\bsrc=)[^>]*>(.*?)</script>#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([]);
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,10 @@ export default defineConfig({
|
||||||
bunny('IBM Plex Mono', {
|
bunny('IBM Plex Mono', {
|
||||||
weights: [400, 600],
|
weights: [400, 600],
|
||||||
}),
|
}),
|
||||||
|
// Police principale du thème (public/css/syllabus-theme.css).
|
||||||
|
bunny('Mulish', {
|
||||||
|
weights: [400, 500, 600, 700, 800],
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue