196 lines
7.5 KiB
PHP
196 lines
7.5 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Spatie\Permission\Models\Role;
|
|
use Spatie\Permission\PermissionRegistrar;
|
|
use Tests\TestCase;
|
|
|
|
/**
|
|
* La pagina "Backup e import" servita per intero: rotta, permessi, layout e i
|
|
* tre componenti Livewire annidati.
|
|
*
|
|
* Serve perché il grosso di questa pagina è markup, e il markup non fallisce
|
|
* come il codice: una vista che non compila non rompe niente finché qualcuno non
|
|
* la apre. È già successo — `@disabled(...)` dentro un tag <x-btn> non viene
|
|
* compilato, il tag resta letterale nel PHP generato e la pagina muore con un
|
|
* errore di sintassi. Qui quella classe di errori si vede subito.
|
|
*
|
|
* Quello che questi test NON possono coprire: Alpine e il comportamento della
|
|
* console (apertura automatica, scorrimento, evidenziazione delle zone) girano
|
|
* nel browser, e qui non c'è un browser. Vanno provati a mano.
|
|
*/
|
|
class PaginaBackupImportTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
// L'immagine di test non contiene public/build: gli asset sono
|
|
// compilati in uno stage a parte, che serve al runtime e non qui. Senza
|
|
// questo, ogni pagina morirebbe su "Vite manifest not found" — un
|
|
// fallimento che non parla del markup, cioe' l'unica cosa che questi
|
|
// test guardano. Che la build degli asset funzioni lo dice `npm run
|
|
// build`, non PHPUnit.
|
|
$this->withoutVite();
|
|
}
|
|
|
|
private function admin(): User
|
|
{
|
|
Role::findOr('amministratore', fn () => Role::create(['name' => 'amministratore', 'guard_name' => 'web']));
|
|
app(PermissionRegistrar::class)->forgetCachedPermissions();
|
|
|
|
$utente = User::create([
|
|
'name' => 'Amministratore',
|
|
'email' => 'admin@example.test',
|
|
'password' => 'password-di-prova',
|
|
]);
|
|
$utente->assignRole('amministratore');
|
|
|
|
return $utente;
|
|
}
|
|
|
|
public function test_la_pagina_si_apre_per_l_amministratore(): void
|
|
{
|
|
$this->actingAs($this->admin())
|
|
->get(route('xml.exchange'))
|
|
->assertOk();
|
|
}
|
|
|
|
public function test_chi_non_e_amministratore_non_ci_entra(): void
|
|
{
|
|
$utente = User::create([
|
|
'name' => 'Assistente',
|
|
'email' => 'assistente@example.test',
|
|
'password' => 'password-di-prova',
|
|
]);
|
|
|
|
$this->actingAs($utente)
|
|
->get(route('xml.exchange'))
|
|
->assertForbidden();
|
|
}
|
|
|
|
/**
|
|
* Le cinque zone esistono e sono raggiungibili per ancora: l'indice in cima
|
|
* alla pagina ci salta sopra, e un id sbagliato lo renderebbe un pulsante
|
|
* che non fa niente.
|
|
*/
|
|
public function test_ci_sono_tutte_e_cinque_le_zone_con_la_loro_ancora(): void
|
|
{
|
|
$risposta = $this->actingAs($this->admin())->get(route('xml.exchange'));
|
|
|
|
foreach (['zona-backup', 'zona-ripristino', 'zona-pdf', 'zona-xml', 'zona-azzeramento'] as $zona) {
|
|
$risposta->assertSee('id="' . $zona . '"', false);
|
|
$risposta->assertSee('tmFocusZona(\'' . $zona . '\'', false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* La zona pericolosa è l'ultima. È il motivo per cui l'azzeramento è stato
|
|
* estratto in un componente suo: reso da BackupManager finiva a metà pagina,
|
|
* fra il ripristino e l'import, che è l'ultimo posto in cui si vuole trovare
|
|
* un pulsante che cancella tutto.
|
|
*/
|
|
public function test_la_zona_pericolosa_e_l_ultima_della_pagina(): void
|
|
{
|
|
$html = $this->actingAs($this->admin())->get(route('xml.exchange'))->getContent();
|
|
|
|
$posizioni = [];
|
|
foreach (['zona-backup', 'zona-ripristino', 'zona-pdf', 'zona-xml', 'zona-azzeramento'] as $zona) {
|
|
$posizioni[$zona] = strpos($html, 'id="' . $zona . '"');
|
|
$this->assertNotFalse($posizioni[$zona], "Zona {$zona} assente dalla pagina.");
|
|
}
|
|
|
|
$this->assertSame(
|
|
['zona-backup', 'zona-ripristino', 'zona-pdf', 'zona-xml', 'zona-azzeramento'],
|
|
array_keys($posizioni),
|
|
'Ordine atteso delle chiavi.'
|
|
);
|
|
|
|
$precedente = -1;
|
|
foreach ($posizioni as $zona => $posizione) {
|
|
$this->assertGreaterThan($precedente, $posizione, "La zona {$zona} è fuori ordine.");
|
|
$precedente = $posizione;
|
|
}
|
|
}
|
|
|
|
public function test_la_console_unica_e_nella_pagina(): void
|
|
{
|
|
$risposta = $this->actingAs($this->admin())->get(route('xml.exchange'));
|
|
|
|
$risposta->assertSee('tmConsoleOperazioni', false);
|
|
$risposta->assertSee('class="tm-console"', false);
|
|
}
|
|
|
|
/**
|
|
* Ordine di caricamento, che è il motivo per cui quel JavaScript sta in uno
|
|
* <script> nella pagina e non in resources/js/app.js.
|
|
*
|
|
* Alpine arriva col pacchetto di Livewire, in fondo al body, e valuta
|
|
* x-data="tmConsoleOperazioni()" quando parte: se la funzione non fosse
|
|
* ancora definita, la console sarebbe un riquadro morto. Il bundle di app.js
|
|
* non andrebbe bene perché è un modulo ES, che il browser rimanda a dopo il
|
|
* parsing del documento — cioè dopo Alpine.
|
|
*
|
|
* Il rapporto fra i due è invisibile guardando i file: si vede solo qui.
|
|
*/
|
|
public function test_la_console_e_definita_prima_che_alpine_parta(): void
|
|
{
|
|
$html = $this->actingAs($this->admin())->get(route('xml.exchange'))->getContent();
|
|
|
|
$definizione = strpos($html, 'window.tmConsoleOperazioni =');
|
|
$livewire = strpos($html, 'livewire.js');
|
|
|
|
$this->assertNotFalse($definizione, 'La definizione della console non è nella pagina.');
|
|
$this->assertNotFalse($livewire, 'Il pacchetto di Livewire non è nella pagina.');
|
|
$this->assertLessThan(
|
|
$livewire,
|
|
$definizione,
|
|
'La console va definita prima del pacchetto che avvia Alpine, altrimenti x-data non la trova.'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* I cinque riquadri di log separati sono spariti: se ne torna uno, la pagina
|
|
* ricomincia a raccontare la stessa operazione in due punti diversi.
|
|
*/
|
|
public function test_non_sono_rimasti_riquadri_di_log_separati(): void
|
|
{
|
|
$risposta = $this->actingAs($this->admin())->get(route('xml.exchange'));
|
|
|
|
$risposta->assertDontSee('Console upload');
|
|
$risposta->assertDontSee('Log import PDF');
|
|
}
|
|
|
|
/**
|
|
* Il difetto che questo test presidia: se un tag di componente non viene
|
|
* compilato, resta nella pagina come testo. Non solleva, non rompe il
|
|
* layout in modo evidente — semplicemente il pulsante non c'è.
|
|
*/
|
|
public function test_nessun_tag_di_componente_e_rimasto_da_compilare(): void
|
|
{
|
|
$html = $this->actingAs($this->admin())->get(route('xml.exchange'))->getContent();
|
|
|
|
$this->assertStringNotContainsString('<x-btn', $html);
|
|
$this->assertStringNotContainsString('</x-btn>', $html);
|
|
}
|
|
|
|
/**
|
|
* I pulsanti passano tutti dal sistema unico. Un `style=` inline su un
|
|
* pulsante è il segnale che qualcuno è tornato a definirne uno per conto suo,
|
|
* senza stato di focus.
|
|
*/
|
|
public function test_i_pulsanti_usano_le_classi_del_sistema_unico(): void
|
|
{
|
|
$html = $this->actingAs($this->admin())->get(route('xml.exchange'))->getContent();
|
|
|
|
foreach (['tm-btn tm-btn-primary', 'tm-btn tm-btn-danger', 'tm-btn tm-btn-secondary'] as $classe) {
|
|
$this->assertStringContainsString($classe, $html, "Manca un pulsante {$classe}.");
|
|
}
|
|
}
|
|
}
|