Paillasse/portail/tests/Feature/DevoirTest.php
2026-10-13 12:44:00 +02:00

115 lines
5.8 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
use App\Models\Classe;
use App\Models\Devoir;
use App\Models\Eleve;
use App\Models\RenduDevoir;
use Illuminate\Support\Facades\Process;
test('un prof crée un devoir, un élève le rend, le prof le note', function () {
$classe = Classe::factory()->create();
$eleve = Eleve::factory()->create(['classe_id' => $classe->id]);
commeProf($this, $classe->prof)->post(route('mes-classes.devoirs.store', $classe), [
'titre' => 'Plan d’adressage',
'consigne' => 'Découpe 192.168.1.0/24 en 4 sous-réseaux.',
'echeance' => now()->addDays(3)->format('Y-m-d\TH:i'),
])->assertRedirect();
$devoir = Devoir::where('titre', 'Plan d’adressage')->first();
expect($devoir)->not->toBeNull()->and($devoir->classe_id)->toBe($classe->id);
eleve($this, $eleve)->get(route('devoirs.eleve.show', $devoir))->assertOk()->assertSee('Découpe 192.168.1.0/24');
eleve($this, $eleve)->post(route('devoirs.eleve.rendre', $devoir), ['reponse' => 'Voici mon découpage : /26 x4.'])
->assertRedirect();
$rendu = RenduDevoir::where('devoir_id', $devoir->id)->where('eleve_id', $eleve->id)->first();
expect($rendu)->not->toBeNull()->and($rendu->reponse)->toBe('Voici mon découpage : /26 x4.')->and($rendu->note)->toBeNull();
commeProf($this, $classe->prof)->get(route('mes-classes.devoirs.show', [$classe, $devoir]))
->assertSee($eleve->nomAffiche())
->assertSee('Voici mon découpage');
commeProf($this, $classe->prof)->post(route('mes-classes.devoirs.noter', [$classe, $devoir, $rendu]), ['note' => 15])
->assertRedirect();
expect($rendu->refresh()->note)->toBe(15);
});
test('un élève peut modifier son rendu avant l’échéance, un seul rendu par devoir en base', function () {
$classe = Classe::factory()->create();
$eleve = Eleve::factory()->create(['classe_id' => $classe->id]);
$devoir = Devoir::create(['classe_id' => $classe->id, 'titre' => 'Test', 'consigne' => 'Test', 'echeance' => now()->addDay()]);
eleve($this, $eleve)->post(route('devoirs.eleve.rendre', $devoir), ['reponse' => 'Premier essai'])->assertRedirect();
eleve($this, $eleve)->post(route('devoirs.eleve.rendre', $devoir), ['reponse' => 'Version corrigée'])->assertRedirect();
expect(RenduDevoir::where('devoir_id', $devoir->id)->count())->toBe(1);
expect(RenduDevoir::first()->reponse)->toBe('Version corrigée');
});
test('un élève ne peut plus rendre après l’échéance', function () {
$classe = Classe::factory()->create();
$eleve = Eleve::factory()->create(['classe_id' => $classe->id]);
$devoir = Devoir::create(['classe_id' => $classe->id, 'titre' => 'Test', 'consigne' => 'Test', 'echeance' => now()->subHour()]);
eleve($this, $eleve)->post(route('devoirs.eleve.rendre', $devoir), ['reponse' => 'Trop tard'])->assertStatus(409);
expect(RenduDevoir::count())->toBe(0);
});
test('un élève d’une autre classe ne peut pas voir ni rendre ce devoir', function () {
$classe = Classe::factory()->create();
$devoir = Devoir::create(['classe_id' => $classe->id, 'titre' => 'Test', 'consigne' => 'Test', 'echeance' => now()->addDay()]);
$autreEleve = Eleve::factory()->create();
eleve($this, $autreEleve)->get(route('devoirs.eleve.show', $devoir))->assertForbidden();
eleve($this, $autreEleve)->post(route('devoirs.eleve.rendre', $devoir), ['reponse' => 'Intrus'])->assertForbidden();
});
test('un prof d’une autre classe ne peut pas voir ni noter les rendus', function () {
$classe = Classe::factory()->create();
$eleve = Eleve::factory()->create(['classe_id' => $classe->id]);
$devoir = Devoir::create(['classe_id' => $classe->id, 'titre' => 'Test', 'consigne' => 'Test', 'echeance' => now()->addDay()]);
$rendu = RenduDevoir::create(['devoir_id' => $devoir->id, 'eleve_id' => $eleve->id, 'reponse' => 'x', 'rendu_a' => now()]);
$autreClasse = Classe::factory()->create();
commeProf($this, $autreClasse->prof)->get(route('mes-classes.devoirs.show', [$classe, $devoir]))->assertForbidden();
commeProf($this, $autreClasse->prof)->post(route('mes-classes.devoirs.noter', [$classe, $devoir, $rendu]), ['note' => 20])->assertForbidden();
});
test('archiver puis purger une classe supprime aussi ses devoirs et rendus', function () {
$classe = Classe::factory()->create();
$eleve = Eleve::factory()->create(['classe_id' => $classe->id]);
$devoir = Devoir::create(['classe_id' => $classe->id, 'titre' => 'Test', 'consigne' => 'Test', 'echeance' => now()->addDay()]);
RenduDevoir::create(['devoir_id' => $devoir->id, 'eleve_id' => $eleve->id, 'reponse' => 'x', 'rendu_a' => now()]);
$classe->forceFill(['archivee_a' => now()->subDays(91)])->save();
$this->artisan('classes:purger-archivees');
expect(Devoir::count())->toBe(0)->and(RenduDevoir::count())->toBe(0);
});
test('les scripts en ligne de la page de notation d’un devoir sont du JavaScript valide', function () {
if (Process::run('node --version')->failed()) {
$this->markTestSkipped('Node absent');
}
$classe = Classe::factory()->create();
$devoir = Devoir::create(['classe_id' => $classe->id, 'titre' => 'Test', 'consigne' => 'Test', 'echeance' => now()->addDay()]);
$html = commeProf($this, $classe->prof)->get(route('mes-classes.devoirs.show', [$classe, $devoir]))->getContent();
preg_match_all('#<script(?![^>]*\bsrc=)[^>]*>(.*?)</script>#s', $html, $scripts);
$erreurs = [];
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[] = "script {$i} : ".strtok($resultat->errorOutput(), "\n");
}
}
expect($erreurs)->toBe([]);
});