127 lines
4.8 KiB
PHP
127 lines
4.8 KiB
PHP
|
|
<?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([]);
|
|||
|
|
});
|