Paillasse/portail/tests/Feature/SurveillanceExamenTest.php

127 lines
4.8 KiB
PHP
Raw Normal View History

<?php
use App\Models\Classe;
use App\Models\Eleve;
use App\Models\EvenementExamen;
use App\Models\Lab;
use App\Models\LabSession;
use App\Models\Seance;
use App\Services\Labs\StockChaud;
use Illuminate\Support\Facades\Process;
use Illuminate\Support\Facades\Queue;
function labSessionEnAttenteExamen(int $dureeExamenMin = 60): LabSession
{
$classe = Classe::factory()->create();
$lab = Lab::factory()->create(['identifiant' => '08-deux-lan-routeur']);
$seance = Seance::create([
'classe_id' => $classe->id,
'lab_id' => $lab->id,
'statut' => Seance::SALLE_ATTENTE,
'demarree_a' => now(),
'mode_examen' => true,
'duree_examen_min' => $dureeExamenMin,
]);
$eleve = Eleve::factory()->create(['classe_id' => $classe->id]);
return LabSession::create(['seance_id' => $seance->id, 'eleve_id' => $eleve->id, 'lab_id' => $lab->id, 'etat' => LabSession::EN_ATTENTE]);
}
test("admettre un élève en mode examen utilise la durée de l'examen, pas celle du lab", function () {
Queue::fake();
$this->mock(StockChaud::class, function ($stock) {
$stock->shouldReceive('prendre')->once()->andReturn(null);
});
$session = labSessionEnAttenteExamen(dureeExamenMin: 90);
admettre($this, $session)->assertRedirect();
$session->refresh();
expect(now()->diffInMinutes($session->expire_a))->toBeGreaterThan(85)
->and(now()->diffInMinutes($session->expire_a))->toBeLessThanOrEqual(90);
});
test("un élève signale un événement d'examen sur sa propre session", function () {
$session = labSessionEnAttenteExamen();
$session->update(['etat' => LabSession::ACTIF]);
eleve($this, $session->eleve)
->postJson(route('lab-sessions.evenements-examen', $session), ['type' => 'changement_onglet'])
->assertCreated();
expect($session->evenementsExamen()->count())->toBe(1)
->and($session->evenementsExamen()->first()->type)->toBe(EvenementExamen::CHANGEMENT_ONGLET);
});
test("un élève ne peut pas signaler un événement sur la session d'un autre élève", function () {
$session = labSessionEnAttenteExamen();
$intrus = Eleve::factory()->create();
eleve($this, $intrus)
->postJson(route('lab-sessions.evenements-examen', $session), ['type' => 'changement_onglet'])
->assertForbidden();
expect($session->evenementsExamen()->count())->toBe(0);
});
test("pas d'événement d'examen possible sur une séance normale (mode examen désactivé)", function () {
$session = sessionEnAttente();
$session->update(['etat' => LabSession::ACTIF]);
eleve($this, $session->eleve)
->postJson(route('lab-sessions.evenements-examen', $session), ['type' => 'changement_onglet'])
->assertForbidden();
});
test('type invalide refusé', function () {
$session = labSessionEnAttenteExamen();
$session->update(['etat' => LabSession::ACTIF]);
eleve($this, $session->eleve)
->postJson(route('lab-sessions.evenements-examen', $session), ['type' => 'webcam_allumee'])
->assertUnprocessable();
});
test('purger une classe archivée supprime aussi les événements d’examen', function () {
$session = labSessionEnAttenteExamen();
$session->update(['etat' => LabSession::TERMINE]);
$session->evenementsExamen()->create(['type' => EvenementExamen::SORTIE_PLEIN_ECRAN]);
$classe = $session->seance->classe;
$classe->forceFill(['archivee_a' => now()->subDays(91)])->save();
$this->artisan('classes:purger-archivees');
expect(EvenementExamen::count())->toBe(0);
});
test('les scripts en ligne du mode examen (barre prof et page élève) sont du JavaScript valide', function () {
if (Process::run('node --version')->failed()) {
$this->markTestSkipped('Node absent');
}
$session = labSessionEnAttenteExamen();
$session->update(['etat' => LabSession::ACTIF]);
$session->machines()->create(['nom' => 'pc-a', 'instance_id' => 'uuid-a']);
$pages = [
fn () => prof($this, $session->seance->classe->prof)->get(route('mes-classes.show', $session->seance->classe)),
fn () => eleve($this, $session->eleve)->get(route('seances.eleve.attente', $session->seance)),
];
$erreurs = [];
foreach ($pages as $i => $visite) {
preg_match_all('#<script(?![^>]*\bsrc=)[^>]*>(.*?)</script>#s', $visite()->getContent(), $scripts);
foreach ($scripts[1] as $j => $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[] = "page {$i} script {$j} : ".strtok($resultat->errorOutput(), "\n");
}
}
}
expect($erreurs)->toBe([]);
});