diff --git a/.env.example b/.env.example index 11ee003..6dc4a02 100644 --- a/.env.example +++ b/.env.example @@ -36,19 +36,20 @@ SENHAUNICA_CALLBACK_ID=1 SENHAUNICA_ADMINS=1111 SENHAUNICA_DEV="http://auth.local:3141/wsusuario/oauth" SENHAUNICA_DEBUG=true +SENHAUNICA_USAR_REPLICADO=false ########## QUOTAS ########## CUPS_SERVER_IP=cups PRINTING_DRIVER=cups -CUPS_SERVER_USERNAME=admin -CUPS_SERVER_PASSWORD=admin +CUPS_SERVER_USERNAME=print +CUPS_SERVER_PASSWORD=print #Ativa o uso do replicado para obter os monitores (default: true) -#REPLICADO_MONITORES=true +REPLICADO_MONITORES=true #Lista os monitores caso REPLICADO_MONITORES=false (default: vazio) -#MONITORES= +MONITORES=111111 IMPRESSORAS_CODSLAMON=22 ########## WSFOTO ########## diff --git a/.github/workflows/dusk.yml b/.github/workflows/dusk.yml index bd2d91b..f93183d 100644 --- a/.github/workflows/dusk.yml +++ b/.github/workflows/dusk.yml @@ -26,6 +26,19 @@ jobs: - name: Build and start containers run: docker compose up -d --build + - name: Wait for CUPS printer + run: | + for i in {1..30}; do + if docker compose exec -T cups lpstat -v 2>/dev/null | grep -q ImpressoraTesteDusk001; then + echo "CUPS printer is ready" + exit 0 + fi + echo "Waiting for CUPS..." + sleep 2 + done + docker compose logs cups + exit 1 + - name: Wait for MariaDB run: | for i in {1..40}; do diff --git a/.gitignore b/.gitignore index 1e6ad87..22e38f6 100644 --- a/.gitignore +++ b/.gitignore @@ -16,5 +16,5 @@ yarn-error.log resources/ISOuncoated.icc resources/PDFX_def.ps /.ddev/ - +.env.dusk.local .phpunit.cache \ No newline at end of file diff --git a/app/Http/Requests/PrintingRequest.php b/app/Http/Requests/PrintingRequest.php index 57fe62b..60a2386 100644 --- a/app/Http/Requests/PrintingRequest.php +++ b/app/Http/Requests/PrintingRequest.php @@ -47,3 +47,4 @@ public function failedValidation(Validator $validator) ])); } */ +} \ No newline at end of file diff --git a/composer.lock b/composer.lock index cd270f3..78dbba1 100644 --- a/composer.lock +++ b/composer.lock @@ -8515,16 +8515,16 @@ }, { "name": "uspdev/replicado", - "version": "1.27.12", + "version": "1.28.4", "source": { "type": "git", "url": "https://github.com/uspdev/replicado.git", - "reference": "2feec97a58b92b108e74eba83f61d29fee69a472" + "reference": "b5f2fdc1ebb2d1c645dcb948bc2a99e2d3e4fa15" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/uspdev/replicado/zipball/2feec97a58b92b108e74eba83f61d29fee69a472", - "reference": "2feec97a58b92b108e74eba83f61d29fee69a472", + "url": "https://api.github.com/repos/uspdev/replicado/zipball/b5f2fdc1ebb2d1c645dcb948bc2a99e2d3e4fa15", + "reference": "b5f2fdc1ebb2d1c645dcb948bc2a99e2d3e4fa15", "shasum": "" }, "require": { @@ -8558,9 +8558,9 @@ "homepage": "https://github.com/uspdev/replicado", "support": { "issues": "https://github.com/uspdev/replicado/issues", - "source": "https://github.com/uspdev/replicado/tree/1.27.12" + "source": "https://github.com/uspdev/replicado/tree/1.28.4" }, - "time": "2026-05-13T14:04:10+00:00" + "time": "2026-09-03T15:10:07+00:00" }, { "name": "uspdev/senhaunica-socialite", diff --git a/docker-compose.yml b/docker-compose.yml index ba7fee4..27fff23 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -55,6 +55,9 @@ services: container_name: impressoras_cups ports: - '8631:631' + volumes: + - ./docker/cups/init-printers.sh:/init-printers.sh:ro + entrypoint: ["/bin/sh", "/init-printers.sh"] environment: - CUPS_ADMIN=print - CUPS_PASSWORD=print diff --git a/docker/cups/init-printers.sh b/docker/cups/init-printers.sh new file mode 100644 index 0000000..961a528 --- /dev/null +++ b/docker/cups/init-printers.sh @@ -0,0 +1,25 @@ +#!/bin/sh + +# sobe o cupsd em background +/usr/sbin/cupsd -f & +CUPSD_PID=$! +trap 'kill $CUPSD_PID' TERM INT + +# espera o CUPS responder (no máximo ~60s) +for i in $(seq 1 60); do + lpstat -r >/dev/null 2>&1 && break + sleep 1 +done + +# procura o PPD do cups-pdf (impressora virtual); se não achar, usa o genérico +PPD=$(lpinfo -m 2>/dev/null | grep -i 'cups-pdf' | head -n1 | awk '{print $1}') +echo "init-printers: PPD escolhido = ${PPD:-genérico}" + +# cria e habilita a impressora de teste (nome = machine_name no sistema) +if lpadmin -p ImpressoraTesteDusk001 -E -v cups-pdf:/ -m "${PPD:-drv:///sample.drv/generic.ppd}"; then + echo "init-printers: impressora ImpressoraTesteDusk001 criada" +else + echo "init-printers: ERRO ao criar a impressora (o CUPS continua no ar)" +fi + +wait $CUPSD_PID \ No newline at end of file diff --git a/phpunit.dusk.xml b/phpunit.dusk.xml index 24fbe45..5745a24 100644 --- a/phpunit.dusk.xml +++ b/phpunit.dusk.xml @@ -9,7 +9,11 @@ backupStaticProperties="false"> - ./tests/Browser + ./tests/Browser/IndexTest.php + ./tests/Browser/RegraCrudTest.php + ./tests/Browser/ImpressoraCrudTest.php + ./tests/Browser/AssistantCrudTest.php + ./tests/Browser/LocalUserCrudTest.php diff --git a/tests/Browser/AssistantCrudTest.php b/tests/Browser/AssistantCrudTest.php new file mode 100644 index 0000000..82e05a5 --- /dev/null +++ b/tests/Browser/AssistantCrudTest.php @@ -0,0 +1,103 @@ +visit('/') + ->clickLink('Entrar') + ->waitFor('#loginUsuario') + ->typeSlowly('loginUsuario', '1111') + ->press('Login') + ->waitForText('Impressoras', 10); + } + + private function criaRegra() + { + return Rule::create([ + 'name' => 'Regra Fila', + 'queue_control' => 1, + 'quota_period' => 'Mensal', + 'quota' => 100, + 'quota_type' => 'Páginas', + ]); + } + + private function criaImpressora(Rule $rule) + { + return Printer::create([ + 'name' => 'Impressora Fila', + 'machine_name' => 'ImpressoraTesteDusk001', + 'location' => 'Sala', + 'rule_id' => $rule->id, + 'active' => true, + ]); + } + + public function test_crud_assistant() + { + $rule = $this->criaRegra(); + $printer = $this->criaImpressora($rule); + + $this->browse(function (Browser $browser) use ($printer,$rule) { + // login como adm + $this->loginAdmin($browser); + + // CREATE + $browser->visit('/assistants') + ->waitForText('Monitores') + ->click('#btn-adicionar-codpes') + ->waitFor('#modal-adicionar-codpes') + ->waitFor('.select2-search__field') + ->type('.select2-search__field', '111111') + ->waitForText('Lucas Andrade Ferreira') + ->click('.select2-results__option--highlighted, .select2-results__option:first-child') + ->click('#modal-adicionar-codpes .btn-submit') + ->waitForText('111111', 10) + ->assertSee('111111'); + + // login como monitor + $browser->clickLink('Sair') + ->waitForLink('Entrar', 10) + ->visit('/') + ->clickLink('Entrar') + ->waitFor('#loginUsuario') + ->typeSlowly('loginUsuario', '111111') + ->press('Login') + ->waitForText('Impressoras', 10); + + // monitor acessa a fila de autorização + $browser->visit("/printers") + ->clickLink('Autorização') + ->waitForLocation("/printers/auth_queue/{$printer->id}") + ->waitForText('Fila de autorização') + ->clickLink('Sair'); + + $this->loginAdmin($browser); // loga como adm novamente + + // DELETE: monitor local tem botão excluir + $browser->visit('/assistants') + ->waitForText('Monitores', 10) + ->waitForText('111111', 10) + ->click('#actions button[type=submit]') + ->acceptDialog() + ->waitUntilMissingText('111111') + ->assertDontSee('111111'); + }); + } +} diff --git a/tests/Browser/ImpressoraCrudTest.php b/tests/Browser/ImpressoraCrudTest.php new file mode 100644 index 0000000..a291e1e --- /dev/null +++ b/tests/Browser/ImpressoraCrudTest.php @@ -0,0 +1,102 @@ + 'Regra Teste Dusk', + 'queue_control' => 0, + 'quota_period' => 'Mensal', + 'quota' => 100, + 'quota_type' => 'Páginas', + ]); + } + + public function test_crud_impressora() + { + $rule = $this->criaRegra(); + + $this->browse(function (Browser $browser) use ($rule) { + // Login como admin + $browser->visit('/') + ->clickLink('Entrar') + ->waitFor('#loginUsuario') + ->typeSlowly('loginUsuario', '1111') + ->press('Login') + ->waitForText('Impressoras', 5) + ->assertSee('Impressoras'); + + // CREATE + $browser->visit('/printers') + ->clickLink('Adicionar impressora') + ->waitFor('input[name=name]') + ->type('name', 'Impressora Teste Dusk') + ->type('machine_name', 'ImpressoraTesteDusk001') + ->type('location', 'Sala de Testes') + ->select('rule_id', (string) $rule->id) + ->press('Enviar') + ->waitForLocation('/printers') + ->assertSee('Impressora Teste Dusk'); + + // READ + $browser->visit('/printers') + ->waitForText('Impressora Teste Dusk') + ->assertSee('Regra Teste Dusk'); + + // UPDATE + $browser->click('a[href*="/printers/"][href$="/edit"]') + ->waitFor('input[name=name]') + ->clear('name') + ->type('name', 'Impressora Teste Dusk Editada') + ->press('Enviar') + ->waitForLocation('/printers') + ->assertSee('Impressora Teste Dusk Editada'); + + // DELETE + $browser->visit('/printers') + ->waitForText('Impressora Teste Dusk Editada') + ->click('#actions button[type=submit]') + ->acceptDialog() + ->waitUntilMissingText('Impressora Teste Dusk Editada') + ->assertDontSee('Impressora Teste Dusk Editada'); + }); + } + + // Usa fila criada no CUPS (init-printers.sh). + public function test_impressao_de_teste() + { + $rule = $this->criaRegra(); + $printer = Printer::create([ + 'name' => 'Impressora Teste Dusk', + 'machine_name' => 'ImpressoraTesteDusk001', + 'location' => 'Sala de Testes', + 'rule_id' => $rule->id, + 'active' => true, + ]); + + $this->browse(function (Browser $browser) use ($printer) { + $browser->visit('/') + ->clickLink('Entrar') + ->waitFor('#loginUsuario') + ->typeSlowly('loginUsuario', '1111') + ->press('Login') + ->waitForText('Impressoras', 5) + ->visit("/printers") + ->waitForText('Impressora Teste Dusk') + ->click("a[href='/printers/{$printer->id}/printtest']") + ->waitForText('Teste de impressão enviado com sucesso', 15) + ->assertSee('Teste de impressão enviado com sucesso'); + }); + } +} \ No newline at end of file diff --git a/tests/Browser/LocalUserCrudTest.php b/tests/Browser/LocalUserCrudTest.php new file mode 100644 index 0000000..335da8c --- /dev/null +++ b/tests/Browser/LocalUserCrudTest.php @@ -0,0 +1,107 @@ +visit('/') + ->clickLink('Entrar') + ->waitFor('#loginUsuario') + ->typeSlowly('loginUsuario', '1111') + ->press('Login') + ->waitForText('Impressoras', 10); + } + + public function test_crud_usuario_local() + { + $this->browse(function (Browser $browser) { + $this->loginAdmin($browser); + + // CREATE + $browser->visit('/local') + ->waitForText('Usuários locais') + ->clickLink('Adicionar usuário local') + ->waitFor('input[name=name]') + ->type('name', 'Usuário Local Teste') + ->type('codpes', 'usuario.local') + ->type('email', 'local@teste.com') + ->type('password', 'secret123') + ->press('Enviar') + ->waitForLocation('/local') + ->assertSee('Usuário Local Teste') + ->assertSee('usuario.local'); + + $user = User::where('codpes', 'usuario.local')->firstOrFail(); + + // validação: email inválido e senha curta devem recusar + $browser->visit('/local/create') + ->type('name', 'X') + ->type('codpes', 'x') + ->type('email', 'nao-e-email') + ->type('password', '123') + ->press('Enviar') + ->waitFor('.alert-danger') + ->assertPresent('.alert-danger'); + + // UPDATE (inclui troca de senha, pois o update exige password) + $browser->visit("/local/{$user->id}/edit") + ->waitFor('input[name=name]') + ->clear('name') + ->type('name', 'Usuário Local Editado') + ->clear('password') + ->type('password', 'novasenha123') + ->press('Enviar') + ->waitForLocation('/local') + ->assertSee('Usuário Local Editado'); + + // desloga do admin para testar o login local + $browser->clickLink('Sair') + ->waitForLink('Entrar', 10); + + // erro + $browser->visit('/login/local') + ->type('codpes', 'usuario.local') + ->type('password', 'errada') + ->press('Login') + ->waitForText('Usuário e senha incorretos') + ->assertSee('Usuário e senha incorretos'); + + // sucesso + $browser->waitFor('input[name=codpes]') + ->type('codpes', 'usuario.local') + ->type('password', 'novasenha123') + ->press('Login') + ->waitForText('Impressoras', 10); + + // Loga como adm para excluir uário local + $browser->clickLink('Sair') + ->waitForLink('Entrar', 10) + ->clickLink('Entrar') + ->waitFor('#loginUsuario') + ->typeSlowly('loginUsuario', '1111') + ->press('Login') + ->waitForText('Impressoras', 10); + + // DELETE + $browser->visit('/local') + ->waitForText('Usuário Local Editado') + ->click('#actions button[type=submit]') + ->acceptDialog() + ->waitUntilMissingText('Usuário Local Editado') + ->assertDontSee('Usuário Local Editado'); + }); + } +}