diff --git a/.env.example b/.env.example deleted file mode 100644 index e66a4d53..00000000 --- a/.env.example +++ /dev/null @@ -1 +0,0 @@ -VITE_API_URL=http://127.0.0.1:5001/api \ No newline at end of file diff --git a/README.md b/README.md index 9b8f6404..c5d46b9e 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,9 @@ +# 🚀 Guia de Integração do Frontend + +Este guia explica como configurar, estruturar e integrar o frontend do projeto React com o backend Flask. + +--- + # ESS Front-end React This is the Front-end base project in React for the Software and Systems Engineering discipline, offered by the Informatics Center (CIn) of the Federal University of Pernambuco (UFPE). @@ -46,6 +52,26 @@ npm run This project uses `.env` files to manage environment variables. You can create a `.env.development` file in the project directory and set the environment variables in the file (iou can create it from .`env.example`). The `env` script in the `package.json` file uses the `env-cmd` package to load the environment variables from the `.env.development` file. +Crie um arquivo .env na raiz do frontend e adicione a URL da API: +``` +VITE_API_URL=http://127.0.0.1:5000 +``` + +📂 src/ → Código-fonte do projeto │ +├── 📂 app/ → Páginas principais (leva a estilização das telas) │ ├── 📂 home/ │ │ ├── 📂 pages/ │ │ │ ├── Login.tsx → Tela de Login │ │ │ ├── Cadastro.tsx → Tela de Cadastro │ │ │ ├── Reservas.tsx → Tela após login │ +├── 📂 shared/ → Recursos compartilhados │ ├── 📂 services/ → Serviços de integração com a API │ │ ├── autorizacao.tsx → Requisições de login/cadastro │ +├── App.tsx → Arquivo principal do React onde você adiciona suas rotas criadas nas pages! ├── main.tsx → Ponto de entrada da aplicação │ ├── .env → Configuração da API ├── package.json → Lista de dependências ├── README.md → Você está aqui! 📌 + +Endereço para se colocar as estilizações próprias de cada tela. +``` +src\app\home\styles +``` + +Endereço onde se pode colocar arquivos css em comum ao sistema +``` +src\shared\components +``` + ### Running the App To start the app, run the following command: @@ -56,6 +82,73 @@ npm run dev This command will run the React app in development with Vite.js script + + +## Rodando o backend Flask + +No powerShell mude para o diretório backend +``` +cd backend +``` +Crie arquivos __init__.py em todos diretórios dentro de backend, inclusive nele. + +backend/ + __init__.py + main.py + testes/ + __init__.py + teste_exemplo.py + +MacOS +Dentro da pasta backend no terminal, execute os seguintes comandos: + +``` +export PYTHONPATH=$(pwd)/backend +export FLASK_APP=main +flask run +``` + +Dentro da pasta backend/testes, execute: + +``` +export PYTHONPATH=$(pwd)/backend +pytest +Windows (Powershell) +``` +Dentro da pasta backend no PowerShell, execute os seguintes comandos: + +``` +$env:PYTHONPATH = "$(pwd)/backend" +$env:FLASK_APP = "main" +flask run +``` + + +Crie a venv +``` +python -m venv venv +``` +Ative a venv +``` +venv\Scripts\activate +``` +e agora você deve instalar todas as dependencias do projeto + +``` +pip install -r requirements.txt +``` + +obs: caso sejam necessárias novas bibliotecas e deseja atualizar o requirements.txt por favor recirar arquivo. +``` +pip freeze > requirements.txt +``` + +Rodando servidor Flask localmente na rota 5000. +``` +python main.py +``` + + ## Running the tests There are two types of tests configured in the base project: unit tests using Vitest with React Testing Library and E2E acceptance tests using Cypress with Cucumber. It's interesting to create the **.env.testing** at the root of the project the same way it was created to run in development, changing the necessary values. diff --git a/backend/__init__.py b/backend/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/__pycache__/__init__.cpython-313.pyc b/backend/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 00000000..0ee68d70 Binary files /dev/null and b/backend/__pycache__/__init__.cpython-313.pyc differ diff --git a/backend/__pycache__/blueprints.cpython-313.pyc b/backend/__pycache__/blueprints.cpython-313.pyc new file mode 100644 index 00000000..6a4384cd Binary files /dev/null and b/backend/__pycache__/blueprints.cpython-313.pyc differ diff --git a/backend/__pycache__/config.cpython-313.pyc b/backend/__pycache__/config.cpython-313.pyc new file mode 100644 index 00000000..6e4f1e8f Binary files /dev/null and b/backend/__pycache__/config.cpython-313.pyc differ diff --git a/backend/__pycache__/main.cpython-313.pyc b/backend/__pycache__/main.cpython-313.pyc new file mode 100644 index 00000000..faa7a7fe Binary files /dev/null and b/backend/__pycache__/main.cpython-313.pyc differ diff --git a/backend/blueprints.py b/backend/blueprints.py new file mode 100644 index 00000000..60bd1df5 --- /dev/null +++ b/backend/blueprints.py @@ -0,0 +1,38 @@ +from backend.rotas.login import login_bp +from backend.rotas.cadastro import cadastro_bp +from backend.rotas.logout import logout_bp +from backend.rotas.criar_solicitacao_manutencao import criar_manutencao_bp +from backend.rotas.criar_solicitacao_recursos import criar_recursos_bp +from backend.rotas.excluir_solicitacao_manutencao import excluir_manutencao_bp +from backend.rotas.excluir_solicitacao_recursos import excluir_recursos_bp +from backend.rotas.editar_solicitacao_manutencao import editar_manutencao_bp +from backend.rotas.editar_solicitacao_recursos import editar_recursos_bp +from backend.rotas.reservas import reservas_bp +from backend.rotas.salas import salas_bp +from backend.rotas.usuario import usuarios_bp +from backend.rotas.criarReview import criar_review_bp +from backend.rotas.atualizarReview import atualizar_review_bp +from backend.rotas.deletarReview import deletar_review_bp +from backend.rotas.obterReview import obter_review_bp +from backend.rotas.listarReview import listar_reviews_bp +from flask_cors import CORS + +# Registra os Blueprints +def registrarBlueprints(app): + blueprints = [ + login_bp, cadastro_bp, logout_bp, criar_manutencao_bp, criar_recursos_bp, excluir_manutencao_bp, excluir_recursos_bp, editar_recursos_bp, editar_manutencao_bp, + salas_bp, usuarios_bp, reservas_bp, criar_review_bp, listar_reviews_bp, obter_review_bp, deletar_review_bp, atualizar_review_bp + ] + + for blueprint in blueprints: + CORS(blueprint) + app.register_blueprint(blueprint) + + CORS(app, supports_credentials=True) + + @app.after_request + def add_cors_headers(response): + response.headers["Access-Control-Allow-Origin"] = "*" + response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS" + response.headers["Access-Control-Allow-Headers"] = "Content-Type, Authorization" + return response \ No newline at end of file diff --git a/backend/config.py b/backend/config.py new file mode 100644 index 00000000..f7e562c4 --- /dev/null +++ b/backend/config.py @@ -0,0 +1,7 @@ +import os + +class Config: + SECRET_KEY = os.urandom(24) + SQLALCHEMY_DATABASE_URI = "sqlite:///users.db" + SQLALCHEMY_TRACK_MODIFICATIONS = False + diff --git a/backend/features/atualizarReview.feature b/backend/features/atualizarReview.feature new file mode 100644 index 00000000..f9d85f05 --- /dev/null +++ b/backend/features/atualizarReview.feature @@ -0,0 +1,13 @@ +Feature: Atualizar avaliação + + Scenario: Atualizar uma avaliação existente + Given o professor Suruagy deseja editar uma avaliação feita anteriormente + When ele envia uma requisição PUT "/api/reviews/1" especificando o id "1" da avaliação que deseja editar + And modifica a nota de "4" para "5" e o comentário de "Sala boa, mas com algumas falhas." para "Modificações feitas, a sala está impecável agora!" + Then o sistema retorna a mensagem "Avaliação atualizada com sucesso." e o status 200 OK + + Scenario: Tentar atualizar uma avaliação que não existe + Given o professor Suruagy deseja editar uma avaliação cuja qual não está presente no sistema + When ele envia uma requisição PUT "/api/reviews/1" especificando o id "1" da avaliação que deseja editar + And modifica a nota de "4" para "5" e o comentário de "Sala boa, mas com algumas falhas." para "Falhas corrigidas, a sala está impecável!" + Then o sistema retorna a mensagem "Avaliação não encontrada para o ID fornecido." e o status 404 NOT FOUND diff --git a/backend/features/cadastroServico.feature b/backend/features/cadastroServico.feature new file mode 100644 index 00000000..09f2c1ce --- /dev/null +++ b/backend/features/cadastroServico.feature @@ -0,0 +1,100 @@ +Feature: Serviço de Cadastro de Usuários + + Scenario: Sucesso no cadastro de usuário + Given o usuário deseja se cadastrar + When ele informa o nome "Demosténes" + And ele informa o CPF "126.455.789-00" + And ele informa o email "demostenessouza@example.com" + And ele informa se é professor "N" + And ele informa a senha "SecurePassword123" + And ele informa a confirmação da senha "SecurePassword123" + And ele envia uma requisição POST para "/cadastro" + Then a resposta deve conter a mensagem "Cadastro criado com sucesso!" + And o status code deve ser "201" + + Scenario: Sucesso no cadastro de professor + Given o usuário deseja se cadastrar + When ele informa o nome "Tatiana" + And ele informa o CPF "331.879.789-33" + And ele informa o email "vanessasilva@example.com" + And ele informa se é professor "S" + And ele informa o SIAPE "101110" + And ele informa a senha "12345678" + And ele informa a confirmação da senha "12345678" + And ele envia uma requisição POST para "/cadastro" + Then a resposta deve conter a mensagem "Cadastro criado com sucesso!" + And o status code deve ser "201" + + Scenario: Fracasso no cadastro por campos obrigatórios não preenchidos + Given o usuário deseja se cadastrar + When ele informa o nome "João" + And ele informa o CPF "987.654.321-00" + And ele informa o email "joao@iat.com" + And ele informa se é professor "N" + And ele informa a senha "Password123" + And ele deixa o campo "Confirmar Senha" com "" + And ele envia uma requisição POST para "/cadastro" + Then a resposta deve conter a mensagem "Confirmar Senha é obrigatório." + And o status code deve ser "400" + + Scenario: Fracasso no cadastro por duplicação de ID única + Given o usuário deseja se cadastrar + When ele informa o nome "Carlos Mendes" + And ele informa o CPF "126.456.789-00" + And ele informa o email "demostenes@example.com" + And ele informa se é professor "S" + And ele informa o SIAPE "010101" + And ele informa a senha "Password456" + And ele informa a confirmação da senha "Password456" + And ele envia uma requisição POST para "/cadastro" + Then a resposta deve conter a mensagem "Erro: email/cpf já está registrado." + And o status code deve ser "409" + + Scenario: Fracasso no cadastro por senhas que não coincidem + Given o usuário deseja se cadastrar + When ele informa o nome "Beatriz" + And ele informa o CPF "789.456.123-00" + And ele informa o email "Beatriz.oliveira@example.com" + And ele informa se é professor "N" + And ele informa a senha "MyPassword123" + And ele informa a confirmação da senha "DifferentPassword123" + And ele envia uma requisição POST para "/cadastro" + Then a resposta deve conter a mensagem "As senhas não coincidem." + And o status code deve ser "400" + + Scenario: Fracasso no cadastro por formato inválido de email + Given o usuário deseja se cadastrar + When ele informa o nome "Lucas" + And ele informa o CPF "987.654.321-00" + And ele informa o email "lucas.example.com" + And ele informa se é professor "N" + And ele informa a senha "SenhaForte123" + And ele informa a confirmação da senha "SenhaForte123" + And ele envia uma requisição POST para "/cadastro" + Then a resposta deve conter a mensagem "Formato de email inválido. Use um email válido, como exemplo@dominio.com." + And o status code deve ser "400" + + Scenario: Fracasso no cadastro por formato inválido de CPF + Given o usuário deseja se cadastrar + When ele informa o nome "Fabricio" + And ele informa o CPF "123" + And ele informa o email "fabricio@example.com" + And ele informa se é professor "N" + And ele informa a senha "SenhaSegura456" + And ele informa a confirmação da senha "SenhaSegura456" + And ele envia uma requisição POST para "/cadastro" + Then a resposta deve conter a mensagem "CPF inválido. Digite um CPF válido no formato XXX.XXX.XXX-XX." + And o status code deve ser "400" + + Scenario: Fracasso no cadastro por siape já registrado + Given o usuário deseja se cadastrar + When ele informa o nome "Max" + And ele informa o CPF "987.654.321-00" + And ele informa o email "max@gmail.com" + And ele informa se é professor "S" + And ele informa o SIAPE "101010" + And ele informa a senha "Senha123" + And ele informa a confirmação da senha "Senha123" + And ele envia uma requisição POST para "/cadastro" + Then a resposta deve conter a mensagem "Erro: siape já está registrado." + And o status code deve ser "409" \ No newline at end of file diff --git a/backend/features/criarReview.feature b/backend/features/criarReview.feature new file mode 100644 index 00000000..06b0c7ba --- /dev/null +++ b/backend/features/criarReview.feature @@ -0,0 +1,26 @@ +Feature: Criação de Review + + Scenario: Criação bem-sucedida de review + Given que o professor Suruagy deseja fazer uma avaliação pós reserva com reserva_id "1" para a sala_id "2" + When ele envia uma requisição POST para "/api/reviews" com os dados reserva_id "1", sala_id "2", usuario_id "3", nota "4" e comentário "Sala boa, mas com algumas falhas." + Then o sistema retorna "Avaliação criada com sucesso!" com o status 201 + + Scenario: Falha ao criar review sem nota + Given que o professor Suruagy deseja fazer uma avaliação pós reserva com reserva_id "1" para a sala_id "2" + When ele envia uma requisição POST para "/api/reviews" com os dados reserva_id "1", sala_id "2", usuario_id "3", nota "" e comentário "Sala boa, mas sem computador!" + Then o sistema retorna "A nota é obrigatória para avaliar a sala." com o status 400 + + Scenario: Falha ao criar review sem sala_id + Given que o professor Suruagy deseja fazer uma avaliação pós reserva com reserva_id "1" para a sala_id "" + When ele envia uma requisição POST para "/api/reviews" com os dados reserva_id "1", sala_id "", usuario_id "3", nota "4" e comentário "Sala boa, mas sem computador!" + Then o sistema retorna "O ID da Sala é obrigatório para avaliar a sala." com o status 400 + + Scenario: Falha ao criar review sem usuario_id + Given que o professor Suruagy deseja fazer uma avaliação pós reserva com reserva_id "1" para a sala_id "2" + When ele envia uma requisição POST para "/api/reviews" com os dados reserva_id "1", sala_id "2", usuario_id "", nota "4" e comentário "Sala boa, mas sem computador!" + Then o sistema retorna "O ID do Usuário é obrigatório para avaliar a sala." com o status 400 + + Scenario: Falha ao criar review sem reserva_id + Given que o professor Suruagy deseja fazer uma avaliação pós reserva com reserva_id "" + When ele envia uma requisição POST para "/api/reviews" com os dados reserva_id "", sala_id "2", usuario_id "3", nota "4" e comentário "Sala boa, mas sem computador!" + Then o sistema retorna "O ID da Reserva é obrigatório para avaliar a sala." com o status 400 diff --git a/backend/features/deletarReview.feature b/backend/features/deletarReview.feature new file mode 100644 index 00000000..67057a23 --- /dev/null +++ b/backend/features/deletarReview.feature @@ -0,0 +1,11 @@ +Feature: Deletar Avaliação + + Scenario: Deletar avaliação existente + Given o professor Suruagy deseja deletar uma avaliação feita anteriormente + When ele envia uma requisição DELETE para "/api/reviews/1" especificando o id "1" da avaliação que deseja deletar + Then o sistema retorna a mensagem "Avaliação deletada com sucesso!" e o status 200 OK + + Scenario: Deletar avaliação que não existe + Given o professor Suruagy deseja deletar uma avaliação que não existe + When ele envia uma requisição DELETE para "/api/reviews/1" especificando o id "1" da avaliação que deseja deletar + Then o sistema retorna a mensagem "Avaliação não encontrada para o ID fornecido." e o status 404 NOT FOUND diff --git a/backend/features/listarReview.feature b/backend/features/listarReview.feature new file mode 100644 index 00000000..f2d76e89 --- /dev/null +++ b/backend/features/listarReview.feature @@ -0,0 +1,12 @@ +Feature: Listar Avaliações + + Scenario: Listar avaliações quando há avaliações no sistema + Given o professor Suruagy deseja consultar as avaliações presentes no sistema e existem avaliações cadastradas + When ele envia uma requisição GET para "/api/reviews" + Then o sistema lista todas as avaliações que foram postadas anteriormente para todas as salas com o status 200 OK + + Scenario: Listar avaliações quando não há nenhuma avaliação + Given o professor Suruagy deseja consultar as avaliações presentes no sistema e não existem avaliações cadastradas + When ele envia uma requisição GET para "/api/reviews" + Then o sistema não encontra nenhuma avaliação no sistema + And exibe a mensagem de erro "Nenhuma avaliação encontrada." com o status 404 NOT FOUND diff --git a/backend/features/loginServico.feature b/backend/features/loginServico.feature new file mode 100644 index 00000000..07541119 --- /dev/null +++ b/backend/features/loginServico.feature @@ -0,0 +1,21 @@ +Feature: Autenticação de usuários + + Scenario: Sucesso no login + Given o usuário possui o email "demostenes@example.com" e a senha "SecurePassword123" válidos + When ele envia uma requisição POST para "/api/login" com os dados "demostenes@example.com" e "SecurePassword123" + Then a resposta deve conter o email "demostenes@example.com" e a mensagem "success" igual a True + And o status code deve ser "200" + + Scenario: Fracasso no login por senha incorreta + Given o usuário possui o email "demostenes@example.com" válido e a senha "SecureIncorreta123" inválida + When ele envia uma requisição POST para "/api/login" com os dados "demostenes@example.com" e "SecureIncorreta123" + Then a resposta deve conter a mensagem "Usuário ou senha inválidos." + And o status code deve ser "401" + + Scenario: Fracasso no login por falta de email ou senha + Given o usuário envia uma requisição sem email ou senha + When ele envia uma requisição POST para "/api/login" com os dados " " e " " + Then a resposta deve conter a mensagem "Usuário e senha são obrigatórios." + And o status code deve ser "400" + + \ No newline at end of file diff --git a/backend/features/manutencao.feature b/backend/features/manutencao.feature new file mode 100644 index 00000000..9121bc97 --- /dev/null +++ b/backend/features/manutencao.feature @@ -0,0 +1,28 @@ +Feature: Criar/remover solicitação de manutenção de sala + + Scenario: sucesso ao criar uma solicitação de manutenção para uma reserva concluída + Given o professor possui uma reserva de sala reserva_id "1" que já foi encerrada + When ele envia uma requisição POST /solicitacoes/manutencao com os dados: reserva_id: "1", descricao: "Mesa quebrada." + Then o sistema retorna "mensagem" "Parabéns, sua solicitação de manutenção foi criada!" e um status "201" + And a reserva reserva_id: "1" possui uma solicitação de manutenção com descricao: "Mesa quebrada." + + Scenario: fracasso ao criar uma solicitação de manutenção sem preencher o campo descricao + Given o professor possui uma reserva de sala reserva_id "1" que já foi encerrada + When ele envia uma requisição POST /solicitacoes/manutencao com os dados: reserva_id: "1", descricao: " " + Then o sistema retorna "erro" "O campo 'descricao' não pode estar vazio." e um status "400" + + Scenario: sucesso ao editar uma solicitação de manutenção existente + Given o professor já criou uma solicitação de manutenção associada a reserva_id "1" + When ele envia uma requisição PUT /solicitacoes/manutencao/"1" contendo o ID da solicitação de manutenção e a alteração descricao: "Mesa e cadeira quebradas." + Then o sistema retorna "mensagem" "Solicitação de manutenção atualizada com sucesso" e um status "200" + And o sistema atualiza os detalhes da solicitação com descricao: "Mesa e cadeira quebradas." + + Scenario: fracasso ao editar solicitação de manutenção sem preencher o campo descricao + Given o professor já criou uma solicitação de manutenção associada a reserva_id "1" + When ele envia uma requisição PUT /solicitacoes/manutencao/"1" contendo o ID da solicitação de manutenção e a alteração descricao: " " + Then o sistema retorna "erro" "A descrição da manutenção não pode ser vazia" e um status "400" + + Scenario: sucesso ao excluir uma solicitação de manutenção existente + Given o professor já criou uma solicitação de manutenção associada a reserva_id "1" + When ele envia uma requisição DELETE /solicitacoes/manutencao/"1", + Then o sistema remove a solicitação do banco de dados e retorna um status "204" \ No newline at end of file diff --git a/backend/features/obterReview.feature b/backend/features/obterReview.feature new file mode 100644 index 00000000..a0be4c7e --- /dev/null +++ b/backend/features/obterReview.feature @@ -0,0 +1,12 @@ +Feature: Consultar Avaliação + + Scenario: Consulta de avaliação existente + Given o professor Suruagy deseja consultar uma avaliação presente no sistema + When ele envia uma requisição GET para "/api/reviews/1" + Then o sistema retorna a avaliação com comentário "Sala excelente, as mudanças foram feitas e ficou ótima.", data_avaliacao "Sat, 15 Feb 2025 16:31:37 GMT", id "1", nota "5", reserva_id "1", sala_id "2", usuario_id "3" com o status 200 OK + + Scenario: Consulta de avaliação inexistente + Given o professor Suruagy deseja consultar uma avaliação presente no sistema + When ele envia uma requisição GET para "/api/reviews/1" + Then o sistema não encontra avaliação presente com o ID especificado + And retorna a mensagem de erro "Avaliação não encontrada para o ID fornecido." com o status 404 NOT FOUND diff --git a/backend/features/recursos.feature b/backend/features/recursos.feature new file mode 100644 index 00000000..3861f897 --- /dev/null +++ b/backend/features/recursos.feature @@ -0,0 +1,57 @@ +Feature: Criar/remover solicitação de recursos de sala + + Scenario: sucesso ao criar solicitação de recursos para uma reserva ativa com todos os campos preenchidos + Given o professor possui uma reserva ativa com reserva_id "1" + When ele envia uma requisição POST /solicitacoes/recursos com os dados: reserva_id: "1", recursos: "Projetor, Teclado", itens_nao_listados: "Extensão elétrica", observacoes: "Para aula prática" + Then o sistema retorna "mensagem" "Parabéns, sua solicitação de recursos foi criada!" e um status "201" + And a reserva_id "1" possui uma solicitação com recursos "Projetor, Teclado", itens_nao_listados "Extensão elétrica" e observacoes "Para aula prática" + + Scenario: fracasso ao criar solicitação de recursos com campos preenchidos com espaços ou não preenchidos + Given o professor possui uma reserva ativa com reserva_id "1" + When ele envia uma requisição POST /solicitacoes/recursos com os dados: reserva_id: "1", recursos: " ", itens_nao_listados: " ", observacoes: " " + Then o sistema retorna "erro" "Você deve selecionar um recurso ou especificar itens não listados." e um status "400" + + Scenario: fracasso ao criar solicitação de recursos com apenas o campo observacoes preenchido + Given o professor possui uma reserva ativa com reserva_id "1" + When ele envia uma requisição POST /solicitacoes/recursos com os dados: reserva_id: "1", recursos: " ", itens_nao_listados: " ", observacoes: "Para aula prática" + Then o sistema retorna "erro" "Você deve selecionar um recurso ou especificar itens não listados." e um status "400" + + Scenario: sucesso ao criar solicitação de recursos sem preencher o campo itens_nao_listados + Given o professor possui uma reserva ativa com reserva_id "1" + When ele envia uma requisição POST /solicitacoes/recursos com os dados: reserva_id: "1", recursos: "Projetor, Teclado", itens_nao_listados: " ", observacoes: "Para aula prática" + Then o sistema retorna "mensagem" "Parabéns, sua solicitação de recursos foi criada!" e um status "201" + And a reserva_id "1" possui uma solicitação com recursos "Projetor, Teclado", itens_nao_listados " " e observacoes "Para aula prática" + + Scenario: sucesso ao criar solicitação de recursos sem preencher o campo itens_nao_listados e o campo observacoes + Given o professor possui uma reserva ativa com reserva_id "1" + When ele envia uma requisição POST /solicitacoes/recursos com os dados: reserva_id: "1", recursos: "Projetor, Teclado", itens_nao_listados: " ", observacoes: " " + Then o sistema retorna "mensagem" "Parabéns, sua solicitação de recursos foi criada!" e um status "201" + And a reserva_id "1" possui uma solicitação com recursos "Projetor, Teclado", itens_nao_listados " " e observacoes " " + + Scenario: sucesso ao criar solicitação de recursos com o campo de recursos vazio e apenas especificando os itens não listados + Given o professor possui uma reserva ativa com reserva_id "1" + When ele envia uma requisição POST /solicitacoes/recursos com os dados: reserva_id: "1", recursos: " ", itens_nao_listados: "Extensão elétrica", observacoes: " " + Then o sistema retorna "mensagem" "Parabéns, sua solicitação de recursos foi criada!" e um status "201" + And a reserva_id "1" possui uma solicitação com recursos " ", itens_nao_listados "Extensão elétrica" e observacoes " " + + Scenario: sucesso ao criar solicitação de recursos com o campo de recursos vazio e especificando os itens não listados e as observacoes + Given o professor possui uma reserva ativa com reserva_id "1" + When ele envia uma requisição POST /solicitacoes/recursos com os dados: reserva_id: "1", recursos: " ", itens_nao_listados: "Extensão elétrica", observacoes: "Para aula prática" + Then o sistema retorna "mensagem" "Parabéns, sua solicitação de recursos foi criada!" e um status "201" + And a reserva_id "1" possui uma solicitação com recursos " ", itens_nao_listados "Extensão elétrica" e observacoes "Para aula prática" + + Scenario: sucesso ao editar uma solicitação de recursos existente + Given o professor possui uma solicitação de recursos associada a reserva_id "1" + When ele envia uma requisição PUT /solicitacoes/recursos/"1" contendo o ID da solicitação e os novos detalhes da solicitação recursos: "Projetor, Caixas de som", observacoes: "Para evento especial" + Then o sistema retorna "mensagem" "Solicitação de recursos atualizada com sucesso" e um status "200" + And o sistema atualiza os detalhes da solicitação: recursos: "Projetor, Caixas de som", observacoes: "Para evento especial" + + Scenario: fracasso ao editar uma solicitação de recursos com apenas o campo observacoes preenchido + Given o professor possui uma reserva ativa com reserva_id "1" + When ele envia uma requisição POST /solicitacoes/recursos com os dados: reserva_id: "1", recursos: " ", itens_nao_listados: " ", observacoes: "Para aula prática" + Then o sistema retorna "erro" "Você deve selecionar um recurso ou especificar itens não listados." e um status "400" + + Scenario: sucesso ao excluir uma solicitação de recursos existente + Given o professor possui uma solicitação de recursos associada a reserva_id "1" + When ele envia uma requisição DELETE /solicitacoes/recursos/"1" contendo o ID da solicitação + Then o sistema remove a solicitação do banco de dados e retorna um status "204" \ No newline at end of file diff --git a/backend/features/reservas.feature b/backend/features/reservas.feature new file mode 100644 index 00000000..00c45ded --- /dev/null +++ b/backend/features/reservas.feature @@ -0,0 +1,41 @@ +Feature: API de Reservas + + Scenario: Criar uma reserva com sucesso + Given a sala de id "5" está disponível no dia "2025-03-03" das "14:00" às "15:00" + And o professor de id "3" não tem uma reserva no dia "2025-03-03" das "14:00" às "15:00" + When uma requisição "POST" for enviada para "/api/reservas/3" com o corpo: "{"sala_id": 5,"data": "2025-03-03","start_time": "14:00","end_time": "15:00"}" + Then o status da resposta deve ser "201" + And o JSON da resposta deve conter "mensagem": "Reserva criada com sucesso!" + And o JSON da reserva deve conter "sala_id": "5" + And o JSON da reserva deve conter "professor_id": "3" + + + Scenario: Erro ao tentar reservar uma sala já ocupada + Given a sala de id "5" não está disponível no dia "2025-02-20" das "14:30" às "16:00" + When uma requisição "POST" for enviada para "/api/reservas/3" com o corpo: "{"sala_id": 5,"data": "2025-02-20","start_time": "14:30","end_time": "16:00"}" + Then o status da resposta deve ser "409" + And o JSON da resposta deve conter "erro": "Sala já reservada para esse horário" + + + Scenario: Erro ao tentar reservar com campos ausentes + Given a sala de id "5" está disponível no dia "2025-01-21" das "14:00" às "15:00" + And o professor de id "3" não tem uma reserva no dia "2025-01-21" das "14:00" às "15:00" + When uma requisição "POST" for enviada para "/api/reservas/3" com o corpo: "{"data": "2025-01-21","start_time": "14:00","end_time": "15:00"}" + Then o status da resposta deve ser "400" + And o JSON da resposta deve conter "erro": "Campos obrigatórios ausentes" + + + Scenario: Cancelar uma reserva com sucesso + Given o professor de id "3" tem uma reserva ativa de id "2" + When uma requisição "DELETE" for enviada para "/api/reservas/2" com o corpo: """" + Then o status da resposta deve ser "200" + And o JSON da resposta deve conter "mensagem": "Reserva cancelada!" + And o JSON da reserva deve conter "id": "2" + + + Scenario: Erro ao tentar cancelar uma reserva inexistente + When uma requisição "DELETE" for enviada para "/api/reservas/99" com o corpo: """" + Then o status da resposta deve ser "404" + And o JSON da resposta deve conter "erro": "Reserva não encontrada." + + diff --git a/backend/features/salas.feature b/backend/features/salas.feature new file mode 100644 index 00000000..1cbc2309 --- /dev/null +++ b/backend/features/salas.feature @@ -0,0 +1,43 @@ +Feature: API de Salas + + Scenario: Criar sala com sucesso + Given não existe uma sala com nome "E901" + When uma requisição "POST" for enviada para "/api/salas" com o corpo: "{"nome": "E901", "tipo": "Reunião", "lugares": 20, "andar": 3, "equipamentos": ["Projetor", "Ar-condicionado"]}" + Then o status da resposta deve ser "201" + And o JSON da resposta deve conter "mensagem": "Sala criada com sucesso!" + And o JSON da sala deve conter "andar": "3" + And o JSON da sala deve conter "nome": "E901" + And o JSON da sala deve conter "tipo": "Reunião" + And o JSON da sala deve conter "lugares": "20" + + + Scenario: Erro ao tentar criar sala com nome existente + Given existe uma sala com nome "E001" + When uma requisição "POST" for enviada para "/api/salas" com o corpo: "{"nome": "E001", "tipo": "Reunião", "lugares": 20, "andar": 3, "equipamentos": ["Projetor", "Ar-condicionado"]}" + Then o status da resposta deve ser "409" + And o JSON da resposta deve conter "erro": "Já existe uma sala com esse nome" + + + Scenario: Buscar todas as salas disponíveis + When uma requisição "GET" for enviada para "/api/salas" com o corpo: """" + Then o status da resposta deve ser "200" + And o JSON da resposta deve conter uma lista de salas com todos os dados + + + Scenario: Erro ao buscar salas com tempo não informado + When uma requisição "GET" for enviada para "/api/salas?data=2025-02-25&start_time=14:00" com o corpo: """" + Then o status da resposta deve ser "400" + And o JSON da resposta deve conter "erro": "tempo não informado" + + + Scenario: Erro ao buscar salas sem data preenchida + When uma requisição "GET" for enviada para "/api/salas?start_time=14:00&end_time=15:00" com o corpo: """" + Then o status da resposta deve ser "400" + And o JSON da resposta deve conter "erro": "data não informada" + + + Scenario: Erro ao tentar deletar sala com reserva ativa + Given a sala de id "1" tem uma reserva ativa + When uma requisição "DELETE" for enviada para "/api/salas/1" com o corpo: """" + Then o status da resposta deve ser "409" + And o JSON da resposta deve conter "erro": "Sala possui reservas ativas e não pode ser deletada" \ No newline at end of file diff --git a/backend/instance/users.db b/backend/instance/users.db new file mode 100644 index 00000000..6b943fea Binary files /dev/null and b/backend/instance/users.db differ diff --git a/backend/main.py b/backend/main.py new file mode 100644 index 00000000..0b986fd7 --- /dev/null +++ b/backend/main.py @@ -0,0 +1,29 @@ +from flask import Flask, jsonify +from flask_cors import CORS +from backend.blueprints import registrarBlueprints +from backend.modelo.extensao import db +from backend.config import Config +from sqlalchemy import inspect + + +app = Flask(__name__) +app.config["TESTING"] = True +app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///users.db" +app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False + +CORS(app, origins=["http://localhost:3000"]) + +db.init_app(app) +with app.app_context(): + db.create_all() + + +registrarBlueprints(app) + +@app.errorhandler(404) +def not_found_error(error): + return jsonify({"error": "Avaliação não encontrada para o ID fornecido."}), 404 + + +if __name__ == "__main__": + app.run(debug=True, port=5000) \ No newline at end of file diff --git a/backend/modelo/__init__.py b/backend/modelo/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/modelo/__pycache__/__init__.cpython-312.pyc b/backend/modelo/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..4d586061 Binary files /dev/null and b/backend/modelo/__pycache__/__init__.cpython-312.pyc differ diff --git a/backend/modelo/__pycache__/__init__.cpython-313.pyc b/backend/modelo/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 00000000..a23be153 Binary files /dev/null and b/backend/modelo/__pycache__/__init__.cpython-313.pyc differ diff --git a/backend/modelo/__pycache__/extensao.cpython-312.pyc b/backend/modelo/__pycache__/extensao.cpython-312.pyc new file mode 100644 index 00000000..41ff179f Binary files /dev/null and b/backend/modelo/__pycache__/extensao.cpython-312.pyc differ diff --git a/backend/modelo/__pycache__/extensao.cpython-313.pyc b/backend/modelo/__pycache__/extensao.cpython-313.pyc new file mode 100644 index 00000000..65a6327d Binary files /dev/null and b/backend/modelo/__pycache__/extensao.cpython-313.pyc differ diff --git a/backend/modelo/__pycache__/reserva.cpython-313.pyc b/backend/modelo/__pycache__/reserva.cpython-313.pyc new file mode 100644 index 00000000..c1bc466d Binary files /dev/null and b/backend/modelo/__pycache__/reserva.cpython-313.pyc differ diff --git a/backend/modelo/__pycache__/reservas.cpython-313.pyc b/backend/modelo/__pycache__/reservas.cpython-313.pyc new file mode 100644 index 00000000..70ac1830 Binary files /dev/null and b/backend/modelo/__pycache__/reservas.cpython-313.pyc differ diff --git a/backend/modelo/__pycache__/reviewSala.cpython-313.pyc b/backend/modelo/__pycache__/reviewSala.cpython-313.pyc new file mode 100644 index 00000000..1b67e433 Binary files /dev/null and b/backend/modelo/__pycache__/reviewSala.cpython-313.pyc differ diff --git a/backend/modelo/__pycache__/sala.cpython-313.pyc b/backend/modelo/__pycache__/sala.cpython-313.pyc new file mode 100644 index 00000000..e8efc4f4 Binary files /dev/null and b/backend/modelo/__pycache__/sala.cpython-313.pyc differ diff --git a/backend/modelo/__pycache__/salas.cpython-313.pyc b/backend/modelo/__pycache__/salas.cpython-313.pyc new file mode 100644 index 00000000..2048cb31 Binary files /dev/null and b/backend/modelo/__pycache__/salas.cpython-313.pyc differ diff --git a/backend/modelo/__pycache__/solicitacaomanutencao.cpython-312.pyc b/backend/modelo/__pycache__/solicitacaomanutencao.cpython-312.pyc new file mode 100644 index 00000000..f475de29 Binary files /dev/null and b/backend/modelo/__pycache__/solicitacaomanutencao.cpython-312.pyc differ diff --git a/backend/modelo/__pycache__/solicitacaomanutencao.cpython-313.pyc b/backend/modelo/__pycache__/solicitacaomanutencao.cpython-313.pyc new file mode 100644 index 00000000..67e5e247 Binary files /dev/null and b/backend/modelo/__pycache__/solicitacaomanutencao.cpython-313.pyc differ diff --git a/backend/modelo/__pycache__/solicitacaorecursos.cpython-312.pyc b/backend/modelo/__pycache__/solicitacaorecursos.cpython-312.pyc new file mode 100644 index 00000000..71bbff2a Binary files /dev/null and b/backend/modelo/__pycache__/solicitacaorecursos.cpython-312.pyc differ diff --git a/backend/modelo/__pycache__/solicitacaorecursos.cpython-313.pyc b/backend/modelo/__pycache__/solicitacaorecursos.cpython-313.pyc new file mode 100644 index 00000000..b94eeeb4 Binary files /dev/null and b/backend/modelo/__pycache__/solicitacaorecursos.cpython-313.pyc differ diff --git a/backend/modelo/__pycache__/usuario.cpython-312.pyc b/backend/modelo/__pycache__/usuario.cpython-312.pyc new file mode 100644 index 00000000..ca14eca7 Binary files /dev/null and b/backend/modelo/__pycache__/usuario.cpython-312.pyc differ diff --git a/backend/modelo/__pycache__/usuario.cpython-313.pyc b/backend/modelo/__pycache__/usuario.cpython-313.pyc new file mode 100644 index 00000000..4c2220c8 Binary files /dev/null and b/backend/modelo/__pycache__/usuario.cpython-313.pyc differ diff --git a/backend/modelo/extensao.py b/backend/modelo/extensao.py new file mode 100644 index 00000000..f0b13d6f --- /dev/null +++ b/backend/modelo/extensao.py @@ -0,0 +1,3 @@ +from flask_sqlalchemy import SQLAlchemy + +db = SQLAlchemy() diff --git a/backend/modelo/reserva.py b/backend/modelo/reserva.py new file mode 100644 index 00000000..109872b0 --- /dev/null +++ b/backend/modelo/reserva.py @@ -0,0 +1,21 @@ +from backend.modelo.extensao import db +from datetime import datetime + +# Definindo a tabela de Reservas +class Reserva(db.Model): + id = db.Column(db.Integer, primary_key=True) + usuario_id = db.Column(db.Integer, db.ForeignKey('Usuario.id'), nullable=False) + sala_id = db.Column(db.Integer, db.ForeignKey('sala.id'), nullable=False) + data_reserva = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) + data_inicio = db.Column(db.DateTime, nullable=False) + data_fim = db.Column(db.DateTime, nullable=False) + status = db.Column(db.String(50), nullable=False, default='pendente') + created_at = db.Column(db.DateTime, default=datetime.utcnow) + updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) + + # Relacionamentos + usuario = db.relationship('Usuario', backref=db.backref('reservas', lazy=True)) + sala = db.relationship('Sala', backref=db.backref('reservas', lazy=True)) + + def __repr__(self): + return f'' \ No newline at end of file diff --git a/backend/modelo/reservas.py b/backend/modelo/reservas.py new file mode 100644 index 00000000..3e48958e --- /dev/null +++ b/backend/modelo/reservas.py @@ -0,0 +1,50 @@ +# TODO: ver quais dados ainda precisa colocar aqui +import copy + +mock_reservas = [ + { + "id": 1, + "professor_id": 3, + "sala_id": 5, + "data": "2025-02-20", + "start_time": "14:00", + "end_time": "15:00", + "status": "ativa" + }, + + { + "id": 2, + "professor_id": 3, + "sala_id": 3, + "data": "2025-02-21", + "start_time": "09:00", + "end_time": "11:00", + "status": "ativa" + }, + + { + "id": 3, + "professor_id": 3, + "sala_id": 1, + "data": "2025-02-22", + "start_time": "08:00", + "end_time": "10:00", + "status": "ativa" + }, + + { + "id": 4, + "professor_id": 3, + "sala_id": 2, + "data": "2025-02-20", + "start_time": "16:00", + "end_time": "18:00", + "status": "ativa" + } +] + +MOCK_RESERVAS_COPY = copy.copy(mock_reservas) + +def reservas_reset(): + mock_reservas.clear() + mock_reservas.extend(copy.copy(MOCK_RESERVAS_COPY)) \ No newline at end of file diff --git a/backend/modelo/reviewSala.py b/backend/modelo/reviewSala.py new file mode 100644 index 00000000..0bab0557 --- /dev/null +++ b/backend/modelo/reviewSala.py @@ -0,0 +1,21 @@ +from backend.modelo.extensao import db +from backend.modelo.sala import Sala +from backend.modelo.reserva import Reserva + +# Definindo a tabela de Avaliações de Salas (ReviewSala) +class ReviewSala(db.Model): + id = db.Column(db.Integer, primary_key=True) + reserva_id = db.Column(db.Integer, db.ForeignKey('reserva.id'), nullable=False) + sala_id = db.Column(db.Integer, db.ForeignKey('sala.id'), nullable=False) + usuario_id = db.Column(db.Integer, db.ForeignKey('Usuario.id'), nullable=False) + nota = db.Column(db.Integer, nullable=False) + comentario = db.Column(db.String(500)) + data_avaliacao = db.Column(db.DateTime, default=db.func.now()) + + # Relacionamento + reserva = db.relationship('Reserva', backref=db.backref('reviews', lazy=True)) + sala = db.relationship('Sala', backref=db.backref('reviews', lazy=True)) + usuario = db.relationship('Usuario', backref=db.backref('reviews', lazy=True)) + + def __repr__(self): + return f'' diff --git a/backend/modelo/sala.py b/backend/modelo/sala.py new file mode 100644 index 00000000..89065cc0 --- /dev/null +++ b/backend/modelo/sala.py @@ -0,0 +1,10 @@ +from backend.modelo.extensao import db + +# Definindo a tabela de Salas +class Sala(db.Model): + id = db.Column(db.Integer, primary_key=True) + nome = db.Column(db.String(100), nullable=False) + capacidade = db.Column(db.Integer) + + def __repr__(self): + return f'' diff --git a/backend/modelo/salas.py b/backend/modelo/salas.py new file mode 100644 index 00000000..4bff9860 --- /dev/null +++ b/backend/modelo/salas.py @@ -0,0 +1,97 @@ +import copy + +EQUIPAMENTOS = [ + "Ar-condicionado", "Cabo P2", "Cabo HDMI", "Cabo VGA", "Microfone", + "Extensão", "Mesa de som", "Passador", "Televisor", "Projetor", + "Carregador", "Pen Drive", "Mouse", "Teclado", "Monitor", "USB-C", + "Cafeteira", "Gelágua" +] + +mock_salas = [ + { + "id": 1, + "nome": "E001", + "tipo": "Reunião", + "lugares": 14, + "andar": 15, + "equipamentos": ["Ar-condicionado", "Televisor"], + "average_rating": 4.0, + "review_count": 2 + }, + { + "id": 2, + "nome": "E002", + "tipo": "Auditório", + "lugares": 50, + "andar": 10, + "equipamentos": ["Projetor", "Microfone", "Mesa de som"], + "average_rating": 5.0, + "review_count": 5 + }, + { + "id": 3, + "nome": "E003", + "tipo": "Reunião", + "lugares": 8, + "andar": 7, + "equipamentos": ["Cabo HDMI", "Cabo VGA", "Extensão"], + "average_rating": 3.5, + "review_count": 3 + }, + { + "id": 4, + "nome": "E004", + "tipo": "Auditório", + "lugares": 80, + "andar": 5, + "equipamentos": ["Projetor", "Mesa de som", "Passador", "Teclado", "Mouse"], + "average_rating": 4.8, + "review_count": 10 + }, + { + "id": 5, + "nome": "E005", + "tipo": "Reunião", + "lugares": 12, + "andar": 9, + "equipamentos": ["Monitor", "USB-C", "Cabo P2"], + "average_rating": 4.2, + "review_count": 6 + }, + { + "id": 6, + "nome": "E006", + "tipo": "Reunião", + "lugares": 6, + "andar": 3, + "equipamentos": ["Ar-condicionado", "Gelágua", "Cafeteira"], + "average_rating": 3.9, + "review_count": 4 + }, + { + "id": 7, + "nome": "E007", + "tipo": "Auditório", + "lugares": 100, + "andar": 2, + "equipamentos": ["Projetor", "Microfone", "Mesa de som", "Extensão"], + "average_rating": 4.7, + "review_count": 8 + }, + { + "id": 8, + "nome": "E008", + "tipo": "Reunião", + "lugares": 10, + "andar": 4, + "equipamentos": ["Carregador", "Pen Drive", "USB-C"], + "average_rating": 4.5, + "review_count": 7 + } +] + +MOCK_SALAS_COPY = copy.copy(mock_salas) + +def salas_reset(): + mock_salas.clear() + mock_salas.extend(copy.copy(MOCK_SALAS_COPY)) diff --git a/backend/modelo/solicitacaomanutencao.py b/backend/modelo/solicitacaomanutencao.py new file mode 100644 index 00000000..ea49267e --- /dev/null +++ b/backend/modelo/solicitacaomanutencao.py @@ -0,0 +1,7 @@ +from backend.modelo.extensao import db +import datetime + +class SolicitacaoManutencao(db.Model): + id = db.Column(db.Integer, primary_key=True) + reserva_id = db.Column(db.Integer, nullable=False) + descricao = db.Column(db.Text, nullable=False) # Descrição da manutenção \ No newline at end of file diff --git a/backend/modelo/solicitacaorecursos.py b/backend/modelo/solicitacaorecursos.py new file mode 100644 index 00000000..7dc3b497 --- /dev/null +++ b/backend/modelo/solicitacaorecursos.py @@ -0,0 +1,9 @@ +from backend.modelo.extensao import db +import datetime + +class SolicitacaoRecursos(db.Model): + id = db.Column(db.Integer, primary_key=True) + reserva_id = db.Column(db.Integer, nullable=False) # Associa à reserva + recursos = db.Column(db.Text, nullable=False) # Itens solicitados + itens_nao_listados = db.Column(db.Text, nullable=True) + observacoes = db.Column(db.Text, nullable=True) \ No newline at end of file diff --git a/backend/modelo/usuario.py b/backend/modelo/usuario.py new file mode 100644 index 00000000..c72958cc --- /dev/null +++ b/backend/modelo/usuario.py @@ -0,0 +1,12 @@ +from backend.modelo.extensao import db + +# Modelo da Tabela de Usuários +class Usuario(db.Model): + __tablename__ = "Usuario" + id = db.Column(db.Integer, primary_key=True) + nome = db.Column(db.String(100), nullable=False) + cpf = db.Column(db.String(14), unique=True, nullable=False) + email = db.Column(db.String(100), unique=True, nullable=False) + professor = db.Column(db.String(1), nullable=False) # "S" ou "N" + siape = db.Column(db.String(6), unique=True) + senha = db.Column(db.String(200), nullable=False) \ No newline at end of file diff --git a/backend/requirements.txt b/backend/requirements.txt new file mode 100644 index 00000000..f86f6786 --- /dev/null +++ b/backend/requirements.txt @@ -0,0 +1,30 @@ +blinker==1.9.0 +certifi==2025.1.31 +charset-normalizer==3.4.1 +click==8.1.8 +colorama==0.4.6 +Flask==3.1.0 +Flask-Cors==5.0.0 +Flask-JWT-Extended==4.7.1 +Flask-SQLAlchemy==3.1.1 +gherkin-official==29.0.0 +greenlet==3.1.1 +idna==3.10 +iniconfig==2.0.0 +itsdangerous==2.2.0 +Jinja2==3.1.5 +Mako==1.3.9 +MarkupSafe==3.0.2 +packaging==24.2 +parse==1.20.2 +parse_type==0.6.4 +pluggy==1.5.0 +PyJWT==2.10.1 +pytest==8.3.4 +pytest-bdd==8.1.0 +requests==2.32.3 +six==1.17.0 +SQLAlchemy==2.0.38 +typing_extensions==4.12.2 +urllib3==2.3.0 +Werkzeug==3.1.3 diff --git a/backend/rotas/__init__.py b/backend/rotas/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/rotas/__pycache__/__init__.cpython-312.pyc b/backend/rotas/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..3f93da27 Binary files /dev/null and b/backend/rotas/__pycache__/__init__.cpython-312.pyc differ diff --git a/backend/rotas/__pycache__/__init__.cpython-313.pyc b/backend/rotas/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 00000000..8da29c37 Binary files /dev/null and b/backend/rotas/__pycache__/__init__.cpython-313.pyc differ diff --git a/backend/rotas/__pycache__/atualizarReview.cpython-313.pyc b/backend/rotas/__pycache__/atualizarReview.cpython-313.pyc new file mode 100644 index 00000000..fdd0d7b0 Binary files /dev/null and b/backend/rotas/__pycache__/atualizarReview.cpython-313.pyc differ diff --git a/backend/rotas/__pycache__/cadastro.cpython-312.pyc b/backend/rotas/__pycache__/cadastro.cpython-312.pyc new file mode 100644 index 00000000..d57b64f0 Binary files /dev/null and b/backend/rotas/__pycache__/cadastro.cpython-312.pyc differ diff --git a/backend/rotas/__pycache__/cadastro.cpython-313.pyc b/backend/rotas/__pycache__/cadastro.cpython-313.pyc new file mode 100644 index 00000000..20f8eea7 Binary files /dev/null and b/backend/rotas/__pycache__/cadastro.cpython-313.pyc differ diff --git a/backend/rotas/__pycache__/criarReview.cpython-313.pyc b/backend/rotas/__pycache__/criarReview.cpython-313.pyc new file mode 100644 index 00000000..860b34a4 Binary files /dev/null and b/backend/rotas/__pycache__/criarReview.cpython-313.pyc differ diff --git a/backend/rotas/__pycache__/criar_solicitacao_manutencao.cpython-312.pyc b/backend/rotas/__pycache__/criar_solicitacao_manutencao.cpython-312.pyc new file mode 100644 index 00000000..e25435ad Binary files /dev/null and b/backend/rotas/__pycache__/criar_solicitacao_manutencao.cpython-312.pyc differ diff --git a/backend/rotas/__pycache__/criar_solicitacao_manutencao.cpython-313.pyc b/backend/rotas/__pycache__/criar_solicitacao_manutencao.cpython-313.pyc new file mode 100644 index 00000000..3572d21b Binary files /dev/null and b/backend/rotas/__pycache__/criar_solicitacao_manutencao.cpython-313.pyc differ diff --git a/backend/rotas/__pycache__/criar_solicitacao_recursos.cpython-312.pyc b/backend/rotas/__pycache__/criar_solicitacao_recursos.cpython-312.pyc new file mode 100644 index 00000000..6e7a1754 Binary files /dev/null and b/backend/rotas/__pycache__/criar_solicitacao_recursos.cpython-312.pyc differ diff --git a/backend/rotas/__pycache__/criar_solicitacao_recursos.cpython-313.pyc b/backend/rotas/__pycache__/criar_solicitacao_recursos.cpython-313.pyc new file mode 100644 index 00000000..2bbd243b Binary files /dev/null and b/backend/rotas/__pycache__/criar_solicitacao_recursos.cpython-313.pyc differ diff --git a/backend/rotas/__pycache__/deletarReview.cpython-313.pyc b/backend/rotas/__pycache__/deletarReview.cpython-313.pyc new file mode 100644 index 00000000..c47dccbb Binary files /dev/null and b/backend/rotas/__pycache__/deletarReview.cpython-313.pyc differ diff --git a/backend/rotas/__pycache__/editar_solicitacao_manutencao.cpython-312.pyc b/backend/rotas/__pycache__/editar_solicitacao_manutencao.cpython-312.pyc new file mode 100644 index 00000000..e22af9e3 Binary files /dev/null and b/backend/rotas/__pycache__/editar_solicitacao_manutencao.cpython-312.pyc differ diff --git a/backend/rotas/__pycache__/editar_solicitacao_manutencao.cpython-313.pyc b/backend/rotas/__pycache__/editar_solicitacao_manutencao.cpython-313.pyc new file mode 100644 index 00000000..70546294 Binary files /dev/null and b/backend/rotas/__pycache__/editar_solicitacao_manutencao.cpython-313.pyc differ diff --git a/backend/rotas/__pycache__/editar_solicitacao_recursos.cpython-312.pyc b/backend/rotas/__pycache__/editar_solicitacao_recursos.cpython-312.pyc new file mode 100644 index 00000000..38649e7d Binary files /dev/null and b/backend/rotas/__pycache__/editar_solicitacao_recursos.cpython-312.pyc differ diff --git a/backend/rotas/__pycache__/editar_solicitacao_recursos.cpython-313.pyc b/backend/rotas/__pycache__/editar_solicitacao_recursos.cpython-313.pyc new file mode 100644 index 00000000..4c4f63cb Binary files /dev/null and b/backend/rotas/__pycache__/editar_solicitacao_recursos.cpython-313.pyc differ diff --git a/backend/rotas/__pycache__/excluir_solicitacao_manutencao.cpython-312.pyc b/backend/rotas/__pycache__/excluir_solicitacao_manutencao.cpython-312.pyc new file mode 100644 index 00000000..a3e25b9f Binary files /dev/null and b/backend/rotas/__pycache__/excluir_solicitacao_manutencao.cpython-312.pyc differ diff --git a/backend/rotas/__pycache__/excluir_solicitacao_manutencao.cpython-313.pyc b/backend/rotas/__pycache__/excluir_solicitacao_manutencao.cpython-313.pyc new file mode 100644 index 00000000..82516e15 Binary files /dev/null and b/backend/rotas/__pycache__/excluir_solicitacao_manutencao.cpython-313.pyc differ diff --git a/backend/rotas/__pycache__/excluir_solicitacao_recursos.cpython-312.pyc b/backend/rotas/__pycache__/excluir_solicitacao_recursos.cpython-312.pyc new file mode 100644 index 00000000..22bb541b Binary files /dev/null and b/backend/rotas/__pycache__/excluir_solicitacao_recursos.cpython-312.pyc differ diff --git a/backend/rotas/__pycache__/excluir_solicitacao_recursos.cpython-313.pyc b/backend/rotas/__pycache__/excluir_solicitacao_recursos.cpython-313.pyc new file mode 100644 index 00000000..c8a04a42 Binary files /dev/null and b/backend/rotas/__pycache__/excluir_solicitacao_recursos.cpython-313.pyc differ diff --git a/backend/rotas/__pycache__/listarReview.cpython-313.pyc b/backend/rotas/__pycache__/listarReview.cpython-313.pyc new file mode 100644 index 00000000..8de930d4 Binary files /dev/null and b/backend/rotas/__pycache__/listarReview.cpython-313.pyc differ diff --git a/backend/rotas/__pycache__/login.cpython-312.pyc b/backend/rotas/__pycache__/login.cpython-312.pyc new file mode 100644 index 00000000..05b489cf Binary files /dev/null and b/backend/rotas/__pycache__/login.cpython-312.pyc differ diff --git a/backend/rotas/__pycache__/login.cpython-313.pyc b/backend/rotas/__pycache__/login.cpython-313.pyc new file mode 100644 index 00000000..eb2a98d4 Binary files /dev/null and b/backend/rotas/__pycache__/login.cpython-313.pyc differ diff --git a/backend/rotas/__pycache__/logout.cpython-312.pyc b/backend/rotas/__pycache__/logout.cpython-312.pyc new file mode 100644 index 00000000..6f29e6e6 Binary files /dev/null and b/backend/rotas/__pycache__/logout.cpython-312.pyc differ diff --git a/backend/rotas/__pycache__/logout.cpython-313.pyc b/backend/rotas/__pycache__/logout.cpython-313.pyc new file mode 100644 index 00000000..686eadcf Binary files /dev/null and b/backend/rotas/__pycache__/logout.cpython-313.pyc differ diff --git a/backend/rotas/__pycache__/obterReview.cpython-313.pyc b/backend/rotas/__pycache__/obterReview.cpython-313.pyc new file mode 100644 index 00000000..d1217c8c Binary files /dev/null and b/backend/rotas/__pycache__/obterReview.cpython-313.pyc differ diff --git a/backend/rotas/__pycache__/reservas.cpython-313.pyc b/backend/rotas/__pycache__/reservas.cpython-313.pyc new file mode 100644 index 00000000..c048c172 Binary files /dev/null and b/backend/rotas/__pycache__/reservas.cpython-313.pyc differ diff --git a/backend/rotas/__pycache__/salas.cpython-313.pyc b/backend/rotas/__pycache__/salas.cpython-313.pyc new file mode 100644 index 00000000..88302b42 Binary files /dev/null and b/backend/rotas/__pycache__/salas.cpython-313.pyc differ diff --git a/backend/rotas/__pycache__/testecookies.cpython-312.pyc b/backend/rotas/__pycache__/testecookies.cpython-312.pyc new file mode 100644 index 00000000..693c4203 Binary files /dev/null and b/backend/rotas/__pycache__/testecookies.cpython-312.pyc differ diff --git a/backend/rotas/__pycache__/testecookies.cpython-313.pyc b/backend/rotas/__pycache__/testecookies.cpython-313.pyc new file mode 100644 index 00000000..356508dd Binary files /dev/null and b/backend/rotas/__pycache__/testecookies.cpython-313.pyc differ diff --git a/backend/rotas/__pycache__/usuario.cpython-313.pyc b/backend/rotas/__pycache__/usuario.cpython-313.pyc new file mode 100644 index 00000000..0c35f83b Binary files /dev/null and b/backend/rotas/__pycache__/usuario.cpython-313.pyc differ diff --git a/backend/rotas/atualizarReview.py b/backend/rotas/atualizarReview.py new file mode 100644 index 00000000..45424b30 --- /dev/null +++ b/backend/rotas/atualizarReview.py @@ -0,0 +1,20 @@ +from flask import Blueprint, request, jsonify +from backend.modelo.extensao import db +from backend.modelo.reviewSala import ReviewSala + +atualizar_review_bp = Blueprint("atualizar_review", __name__) + +@atualizar_review_bp.route("/api/reviews/", methods=["PUT"]) +def atualizar_review(id): + review = db.session.get(ReviewSala, id) + #Verifica o ID + if not review: + return jsonify({"error": "Avaliação não encontrada para o ID fornecido."}), 404 + + data = request.get_json() + # Atualiza a nota e o comentário se estiverem presentes no JSON + review.nota = data.get("nota", review.nota) + review.comentario = data.get("comentario", review.comentario) + + db.session.commit() + return jsonify({"mensagem": "Avaliação atualizada com sucesso!"}) diff --git a/backend/rotas/cadastro.py b/backend/rotas/cadastro.py new file mode 100644 index 00000000..f3b0721d --- /dev/null +++ b/backend/rotas/cadastro.py @@ -0,0 +1,79 @@ +from flask import Blueprint, request, jsonify +from werkzeug.security import generate_password_hash +from backend.modelo.usuario import Usuario +from backend.modelo.extensao import db +import re +from sqlalchemy import exists + +cadastro_bp = Blueprint("cadastro", __name__) + +def validarEmail(email): + """Valida o formato do email.""" + padraoEmail = r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$' + return bool(re.match(padraoEmail, email)) + +def validarCpf(cpf): + """Valida se o CPF está no formato correto XXX.XXX.XXX-XX""" + padraoCpf = r'^\d{3}\.\d{3}\.\d{3}-\d{2}$' + return bool(re.match(padraoCpf, cpf)) + +@cadastro_bp.route("/cadastro", methods=["POST"]) +def cadastro(): + data = request.get_json() + + nome = data.get("nome", "").strip() + cpf = data.get("cpf", "").strip() + email = data.get("email", "").lower().strip() + professor = data.get("professor", "").upper().strip() + siape = data.get("siape", "").strip() if professor == "S" else None + senha = data.get("senha", "").strip() + confirmar_senha = data.get("confirmarSenha", "").strip() + + camposObrigatorios = { + "nome": nome, + "cpf": cpf, + "email": email, + "senha": senha, + "Confirmar Senha": confirmar_senha + } + if professor == "S": + camposObrigatorios["siape"] = siape + + for campo, valor in camposObrigatorios.items(): + if not valor: + return jsonify({"error": f"{campo} é obrigatório."}), 400 + + if not validarEmail(email): + return jsonify({"error": "Formato de email inválido. Use um email válido, como exemplo@dominio.com."}), 400 + + if not validarCpf(cpf): + return jsonify({"error": "CPF inválido. Digite um CPF válido no formato XXX.XXX.XXX-XX."}), 400 + + usuarioExistente = db.session.query( + exists().where((Usuario.email == email) | (Usuario.cpf == cpf)) + ).scalar() + + if usuarioExistente: + return jsonify({"error": "Erro: email/cpf já está registrado."}), 409 + + if professor == "S": + siapeExistente = db.session.query( + exists().where(Usuario.siape == siape) + ).scalar() + + if siapeExistente: + return jsonify({"error": "Erro: siape já está registrado."}), 409 + + if senha != confirmar_senha: + return jsonify({"error": "As senhas não coincidem."}), 400 + + senhaHash = generate_password_hash(senha) + + novoUsuario = Usuario( + nome=nome, cpf=cpf, email=email, professor=professor, siape=siape, senha=senhaHash + ) + + db.session.add(novoUsuario) + db.session.commit() + + return jsonify({"message": "Cadastro criado com sucesso!"}), 201 diff --git a/backend/rotas/criarReview.py b/backend/rotas/criarReview.py new file mode 100644 index 00000000..6404d2b6 --- /dev/null +++ b/backend/rotas/criarReview.py @@ -0,0 +1,36 @@ +from flask import Blueprint, request, jsonify +from backend.modelo.extensao import db +from backend.modelo.reviewSala import ReviewSala + +criar_review_bp = Blueprint("criar_review", __name__) + +@criar_review_bp.route("/api/reviews", methods=["POST"]) +def criar_review(): + data = request.get_json() + + # Aqui tá verificando se os campos obrigatórios foram preenchidos + # se faltar o comentário, ainda vai passar (campo não obrigatório) + + if data.get("nota") is None: + return jsonify({"error": "A nota é obrigatória para avaliar a sala."}), 400 + + elif data.get("sala_id") is None: + return jsonify({"error": "O ID da Sala é obrigatório para avaliar a sala."}), 400 + + elif data.get("usuario_id") is None: + return jsonify({"error": "O ID do Usuário é obrigatório para avaliar a sala."}), 400 + + elif data.get("reserva_id") is None: + return jsonify({"error": "O ID da Reserva é obrigatório para avaliar a sala."}), 400 + + # Verifica tudo e prossegue pra avaliação! + nova_review = ReviewSala( + reserva_id=data.get("reserva_id"), + sala_id=data.get("sala_id"), + usuario_id=data.get("usuario_id"), + nota=data.get("nota"), + comentario=data.get("comentario") + ) + db.session.add(nova_review) + db.session.commit() + return jsonify({"mensagem": "Avaliação criada com sucesso!"}), 201 diff --git a/backend/rotas/criar_solicitacao_manutencao.py b/backend/rotas/criar_solicitacao_manutencao.py new file mode 100644 index 00000000..71ca786c --- /dev/null +++ b/backend/rotas/criar_solicitacao_manutencao.py @@ -0,0 +1,22 @@ +from flask import Blueprint, request, jsonify +from backend.modelo.extensao import db +from backend.modelo.solicitacaomanutencao import SolicitacaoManutencao +from backend.modelo.solicitacaorecursos import SolicitacaoRecursos + +criar_manutencao_bp = Blueprint("criarmanutencao", __name__) + +@criar_manutencao_bp.route("/solicitacoes/manutencao", methods=["POST"]) +def criar_solicitacao_manutencao(): + dados = request.json + + # Verifica se 'descricao' está vazio ou None + if not dados.get("descricao").strip(): + return jsonify({"erro": "O campo 'descricao' não pode estar vazio."}), 400 + + solicitacao = SolicitacaoManutencao( + reserva_id=dados.get("reserva_id"), + descricao=dados.get("descricao") + ) + db.session.add(solicitacao) + db.session.commit() + return jsonify({"id": solicitacao.id, "mensagem": "Parabéns, sua solicitação de manutenção foi criada!"}), 201 \ No newline at end of file diff --git a/backend/rotas/criar_solicitacao_recursos.py b/backend/rotas/criar_solicitacao_recursos.py new file mode 100644 index 00000000..6d4e74e3 --- /dev/null +++ b/backend/rotas/criar_solicitacao_recursos.py @@ -0,0 +1,38 @@ +from flask import Blueprint, request, jsonify +from backend.modelo.extensao import db +from backend.modelo.solicitacaomanutencao import SolicitacaoManutencao +from backend.modelo.solicitacaorecursos import SolicitacaoRecursos + +criar_recursos_bp = Blueprint("criarrecursos", __name__) + +@criar_recursos_bp.route("/solicitacoes/recursos", methods=["POST"]) +def criar_solicitacao_recursos(): + dados = request.json + + recursos = dados.get("recursos", "").strip() + itens_nao_listados = dados.get("itens_nao_listados", "").strip() + + # Validação: se recursos estiver vazio, itens_nao_listados deve estar preenchido + if not recursos and not itens_nao_listados: + return jsonify({"erro": "Você deve selecionar um recurso ou especificar itens não listados."}), 400 + + # Verifica se 'recursos' é None ou uma string vazia + #if not dados.get("recursos"): + #return jsonify({"error": "O campo 'recursos' não pode estar vazio."}), 400 + + #solicitacao = SolicitacaoRecursos( + #reserva_id=dados.get("reserva_id"), + #recursos=dados.get("recursos"), + #itens_nao_listados=dados.get("itens_nao_listados"), + #observacoes=dados.get("observacoes") + #) + + solicitacao = SolicitacaoRecursos( + reserva_id=dados.get("reserva_id"), + recursos=recursos, + itens_nao_listados=itens_nao_listados, + observacoes=dados.get("observacoes", "").strip() + ) + db.session.add(solicitacao) + db.session.commit() + return jsonify({"id": solicitacao.id, "mensagem": "Parabéns, sua solicitação de recursos foi criada!"}), 201 \ No newline at end of file diff --git a/backend/rotas/deletarReview.py b/backend/rotas/deletarReview.py new file mode 100644 index 00000000..505ca3af --- /dev/null +++ b/backend/rotas/deletarReview.py @@ -0,0 +1,16 @@ +from flask import Blueprint, jsonify, abort +from backend.modelo.extensao import db +from backend.modelo.reviewSala import ReviewSala + +deletar_review_bp = Blueprint("deletar_review", __name__) + +@deletar_review_bp.route("/api/reviews/", methods=["DELETE"]) +def deletar_review(id): + review = db.session.get(ReviewSala, id) + #Verifica o ID + if not review: + return jsonify({"error": "Avaliação não encontrada para o ID fornecido."}), 404 + + db.session.delete(review) + db.session.commit() + return jsonify({"mensagem": "Avaliação deletada com sucesso!"}) diff --git a/backend/rotas/editar_solicitacao_manutencao.py b/backend/rotas/editar_solicitacao_manutencao.py new file mode 100644 index 00000000..8412ab8f --- /dev/null +++ b/backend/rotas/editar_solicitacao_manutencao.py @@ -0,0 +1,27 @@ +from flask import Blueprint, request, jsonify +from backend.modelo.extensao import db +from backend.modelo.solicitacaomanutencao import SolicitacaoManutencao +from backend.modelo.solicitacaorecursos import SolicitacaoRecursos +import flask + +editar_manutencao_bp = Blueprint("editarmanutencao", __name__) + +@editar_manutencao_bp.route("/solicitacoes/manutencao/", methods=["PUT"]) +def editar_solicitacao_manutencao(id): + solicitacao = db.session.get(SolicitacaoManutencao, id) or flask.abort(404) + + dados = request.json + + # Verifica se a nova descrição é vazia ou apenas espaços em branco + nova_descricao = dados.get("descricao", solicitacao.descricao) + if not nova_descricao.strip(): + return jsonify({"erro": "A descrição da manutenção não pode ser vazia"}), 400 + + solicitacao.descricao = nova_descricao + db.session.commit() + return jsonify({ + "mensagem": "Solicitação de manutenção atualizada com sucesso", + "id": solicitacao.id, + #"reserva_id": solicitacao.reserva_id, + "descricao": solicitacao.descricao +}), 200 \ No newline at end of file diff --git a/backend/rotas/editar_solicitacao_recursos.py b/backend/rotas/editar_solicitacao_recursos.py new file mode 100644 index 00000000..16ad8223 --- /dev/null +++ b/backend/rotas/editar_solicitacao_recursos.py @@ -0,0 +1,27 @@ +from flask import Blueprint, request, jsonify +from backend.modelo.extensao import db +from backend.modelo.solicitacaomanutencao import SolicitacaoManutencao +from backend.modelo.solicitacaorecursos import SolicitacaoRecursos +import flask + +editar_recursos_bp = Blueprint("editarrecursos", __name__) + +@editar_recursos_bp.route("/solicitacoes/recursos/", methods=["PUT"]) +def editar_solicitacao_recursos(id): + solicitacao = db.session.get(SolicitacaoRecursos, id) or flask.abort(404) + dados = request.json + + # Verifica se ambos os campos estão vazios + recursos = dados.get("recursos", "").strip() + itens_nao_listados = dados.get("itens_nao_listados", "").strip() + + if not recursos and not itens_nao_listados: + return jsonify({"erro": "Você deve preencher pelo menos 'recursos' ou 'itens_nao_listados'."}), 400 + + # Atualiza os campos apenas se foram passados na requisição + solicitacao.recursos = recursos if "recursos" in dados else solicitacao.recursos + solicitacao.itens_nao_listados = itens_nao_listados if "itens_nao_listados" in dados else solicitacao.itens_nao_listados + solicitacao.observacoes = dados.get("observacoes", solicitacao.observacoes) + + db.session.commit() + return jsonify({"mensagem": "Solicitação de recursos atualizada com sucesso"}), 200 \ No newline at end of file diff --git a/backend/rotas/excluir_solicitacao_manutencao.py b/backend/rotas/excluir_solicitacao_manutencao.py new file mode 100644 index 00000000..39855f6b --- /dev/null +++ b/backend/rotas/excluir_solicitacao_manutencao.py @@ -0,0 +1,15 @@ +from flask import Blueprint, request, jsonify +import flask +from backend.modelo.extensao import db +from backend.modelo.solicitacaomanutencao import SolicitacaoManutencao +from backend.modelo.solicitacaorecursos import SolicitacaoRecursos + +excluir_manutencao_bp = Blueprint("excluirmanutencao", __name__) + +@excluir_manutencao_bp.route("/solicitacoes/manutencao/", methods=["DELETE"]) +def excluir_solicitacao_manutencao(id): + solicitacao = db.session.get(SolicitacaoManutencao, id) or flask.abort(404) + + db.session.delete(solicitacao) + db.session.commit() + return jsonify({"message": "Solicitação excluída"}), 204 diff --git a/backend/rotas/excluir_solicitacao_recursos.py b/backend/rotas/excluir_solicitacao_recursos.py new file mode 100644 index 00000000..bcb2664d --- /dev/null +++ b/backend/rotas/excluir_solicitacao_recursos.py @@ -0,0 +1,14 @@ +from flask import Blueprint, request, jsonify +from backend.modelo.extensao import db +from backend.modelo.solicitacaomanutencao import SolicitacaoManutencao +from backend.modelo.solicitacaorecursos import SolicitacaoRecursos +import flask + +excluir_recursos_bp = Blueprint("excluirrecursos", __name__) + +@excluir_recursos_bp.route("/solicitacoes/recursos/", methods=["DELETE"]) +def excluir_solicitacao_recursos(id): + solicitacao = db.session.get(SolicitacaoRecursos, id) or flask.abort(404) + db.session.delete(solicitacao) + db.session.commit() + return jsonify({"message": "Solicitação excluída"}), 204 diff --git a/backend/rotas/listarReview.py b/backend/rotas/listarReview.py new file mode 100644 index 00000000..352046f2 --- /dev/null +++ b/backend/rotas/listarReview.py @@ -0,0 +1,24 @@ +from flask import Blueprint, jsonify +from backend.modelo.reviewSala import ReviewSala + +listar_reviews_bp = Blueprint("listar_reviews", __name__) + +@listar_reviews_bp.route("/api/reviews", methods=["GET"]) +def listar_reviews(): + reviews = ReviewSala.query.all() + #Verifica as avaliações + if not reviews: + return jsonify({"error": "Nenhuma avaliação encontrada."}), 404 + + resultado = [] + for review in reviews: + resultado.append({ + "id": review.id, + "reserva_id": review.reserva_id, + "sala_id": review.sala_id, + "usuario_id": review.usuario_id, + "nota": review.nota, + "comentario": review.comentario, + "data_avaliacao": review.data_avaliacao + }) + return jsonify(resultado), 200 diff --git a/backend/rotas/login.py b/backend/rotas/login.py new file mode 100644 index 00000000..bf5dbaed --- /dev/null +++ b/backend/rotas/login.py @@ -0,0 +1,31 @@ +from flask import Blueprint, jsonify, request, make_response +from backend.modelo.usuario import Usuario +from werkzeug.security import check_password_hash + +login_bp = Blueprint("login", __name__) + +@login_bp.route("/", methods=["POST"]) +def login(): + data = request.get_json() + + print("Dados recebidos no login:", data) + + # Pegando os valores com `.get()` para evitar erro caso estejam ausentes + email = data.get("email", "").strip() + senha = data.get("senha", "").strip() + + if not email or not senha: + return jsonify({"success": False, "error": "Usuário e senha são obrigatórios."}), 400 + + # Buscar usuário no banco + usuario = Usuario.query.filter_by(email=email).first() + if not usuario or not check_password_hash(usuario.senha, senha): + return jsonify({"success": False, "error": "Usuário ou senha inválidos."}), 401 + + return jsonify({ + "success": True, + "usuario": { + "email": usuario.email + } + + }), 200 \ No newline at end of file diff --git a/backend/rotas/logout.py b/backend/rotas/logout.py new file mode 100644 index 00000000..f9d69aaa --- /dev/null +++ b/backend/rotas/logout.py @@ -0,0 +1,8 @@ +from flask import Blueprint, jsonify + +logout_bp = Blueprint("logout", __name__) + +@logout_bp.route("/api/logout", methods=["POST"]) +def logout(): + response = jsonify({"message": "Logout realizado com sucesso."}) + return response, 200 diff --git a/backend/rotas/obterReview.py b/backend/rotas/obterReview.py new file mode 100644 index 00000000..8780d83b --- /dev/null +++ b/backend/rotas/obterReview.py @@ -0,0 +1,22 @@ +from flask import Blueprint, jsonify +from backend.modelo.reviewSala import ReviewSala +from backend.modelo.extensao import db + +obter_review_bp = Blueprint("obter_review", __name__) + +@obter_review_bp.route("/api/reviews/", methods=["GET"]) +def obter_review(id): + review = db.session.get(ReviewSala, id) + #Verifica o ID + if not review: + return jsonify({"error": "Avaliação não encontrada para o ID fornecido."}), 404 + + return jsonify({ + "id": review.id, + "reserva_id": review.reserva_id, + "sala_id": review.sala_id, + "usuario_id": review.usuario_id, + "nota": review.nota, + "comentario": review.comentario, + "data_avaliacao": review.data_avaliacao + }) diff --git a/backend/rotas/reservas.py b/backend/rotas/reservas.py new file mode 100644 index 00000000..1328de2c --- /dev/null +++ b/backend/rotas/reservas.py @@ -0,0 +1,93 @@ +from flask import Blueprint, request, jsonify +from backend.modelo.reservas import mock_reservas +from datetime import datetime + +reservas_bp = Blueprint('reservas', __name__) + +def parse_time(time_str): + return datetime.strptime(time_str, "%H:%M").time() + +# Get reservas +@reservas_bp.route('/api/reservas', methods=['GET']) +def get_reservas(): + return jsonify(mock_reservas), 200 + +# Get reservas para um usuário +@reservas_bp.route('/api/reservas/', methods=['GET']) +def get_reservas_professor(professor_id): + user_reservas = [ + reserva for reserva in mock_reservas if reserva['professor_id'] == professor_id + ] + + if not user_reservas: + return jsonify({'mensagem': 'Nenhuma reserva encontrada'}), 404 + + return jsonify(user_reservas), 200 + +# Create reserva +@reservas_bp.route('/api/reservas/', methods=['POST']) +def create_reserva(professor_id): + """ + Exemplo de body + { + "sala_id": 3, + "data": "2025-02-25", + "start_time": "14:00", + "end_time": "15:00" + } + """ + + dados = request.get_json() + sala_id = dados.get('sala_id') + data = dados.get('data') + start_time = dados.get('start_time') + end_time = dados.get('end_time') + status = "ativa" + + # Checa se o body inclui o que precisa + if not sala_id or not data or not start_time or not end_time: + return jsonify({"erro": "Campos obrigatórios ausentes"}), 400 + + parsed_start_time = parse_time(start_time) + parsed_end_time = parse_time(end_time) + + for reserva in mock_reservas: + if reserva['sala_id'] == sala_id and reserva['data'] == data and reserva['status'] == 'ativa': + reserva_start = parse_time(reserva['start_time']) + reserva_end = parse_time(reserva['end_time']) + + # Sobreposição de horários + if not (reserva_end <= parsed_start_time or reserva_start >= parsed_end_time): + return jsonify({'erro': 'Sala já reservada para esse horário'}), 409 + + for reserva in mock_reservas: + if reserva['professor_id'] == professor_id and reserva['data'] == data and reserva['status'] == 'ativa': + professor_reserva_start = parse_time(reserva["start_time"]) + professor_reserva_end = parse_time(reserva["end_time"]) + + if not (professor_reserva_end <= parsed_start_time or professor_reserva_start >= parsed_end_time): + return jsonify({"erro": "Professor já possui uma reserva nesse horário"}), 409 + + new_reserva = { + "id": len(mock_reservas) + 1, + "sala_id": sala_id, + "professor_id": professor_id, + "data": data, + "start_time": start_time, + "end_time": end_time, + "status": status + } + mock_reservas.append(new_reserva) + + return jsonify({"mensagem": "Reserva criada com sucesso!", "reservation": new_reserva}), 201 + + +# Cancela reserva +@reservas_bp.route('/api/reservas/', methods=['DELETE']) +def cancel_reserva(reserva_id): + for reserva in mock_reservas: + if reserva['id'] == reserva_id: + reserva['status'] = 'cancelada' + return jsonify({"mensagem": "Reserva cancelada!", "reservation": reserva}), 200 + + return jsonify({"erro": "Reserva não encontrada."}), 404 \ No newline at end of file diff --git a/backend/rotas/salas.py b/backend/rotas/salas.py new file mode 100644 index 00000000..d3c32463 --- /dev/null +++ b/backend/rotas/salas.py @@ -0,0 +1,120 @@ +from flask import Blueprint, request, jsonify +from backend.modelo.reservas import mock_reservas +from backend.modelo.salas import mock_salas, EQUIPAMENTOS +from datetime import datetime + +salas_bp = Blueprint('salas', __name__) + +# Convert "HH:MM" string to datetime.time +def parse_time(time_str): + return datetime.strptime(time_str, "%H:%M").time() + +# Criar sala +@salas_bp.route('/api/salas', methods=['POST']) +def create_sala(): + """ + Exemplo de body: + { + "nome": "Sala E003", + "tipo": "Reunião", + "lugares": 20, + "andar": 3, + "equipamentos": ["Projetor", "Ar-condicionado"] + } + """ + + dados = request.get_json() + + nome = dados.get("nome") + tipo = dados.get("tipo") + lugares = dados.get("lugares") + andar = dados.get("andar") + equipamentos = dados.get("equipamentos", []) + + + if not nome or not tipo or not lugares or not andar: + return jsonify({"erro": "Campos obrigatórios ausentes"}), 400 + + if tipo not in ["Reunião", "Auditório"]: + return jsonify({"erro": "Tipo de sala inválido. Escolha entre 'Reunião' ou 'Auditório'"}), 400 + + equipamentos_invalidos = [eq for eq in equipamentos if eq not in EQUIPAMENTOS] + if equipamentos_invalidos: + return jsonify({"erro": f"Equipamentos inválidos: {equipamentos_invalidos}"}), 400 + + if any(sala["nome"] == nome for sala in mock_salas): + return jsonify({"erro": "Já existe uma sala com esse nome"}), 409 + + nova_sala = { + "id": len(mock_salas) + 1, + "nome": nome, + "tipo": tipo, + "lugares": lugares, + "andar": andar, + "equipamentos": equipamentos + } + mock_salas.append(nova_sala) + + return jsonify({"mensagem": "Sala criada com sucesso!", "sala": nova_sala}), 201 + + +# Get salas +@salas_bp.route('/api/salas', methods=['GET']) +def get_salas_disponiveis(): + equipamentos_filtro = request.args.getlist("equipamentos") # Get filter from query params + data = request.args.get("data") + start_time = request.args.get("start_time") + end_time = request.args.get("end_time") + + if not equipamentos_filtro and not data and not start_time and not end_time: + return jsonify(mock_salas), 200 + elif not data: + return jsonify({'erro': 'data não informada'}), 400 + elif not start_time or not end_time: + return jsonify({'erro': 'tempo não informado'}), 400 + + + salas_filtradas = [ + sala for sala in mock_salas + if not equipamentos_filtro or all(eq in sala["equipamentos"] for eq in equipamentos_filtro) + ] + + if data and start_time and end_time: + salas_disponiveis = [] + for sala in salas_filtradas: + reservas_existentes = [ + reserva for reserva in mock_reservas + if reserva["sala_id"] == sala["id"] and reserva["data"] == data and + not (parse_time(reserva["end_time"]) <= parse_time(start_time) or + parse_time(reserva["start_time"]) >= parse_time(end_time)) and + reserva["status"] == "ativa" + ] + + if not reservas_existentes: + salas_disponiveis.append(sala) + else: + salas_disponiveis = salas_filtradas + + if not salas_disponiveis: + return jsonify({'mensagem': 'nenhuma sala encontrada'}), 404 + + return jsonify(salas_disponiveis), 200 + +@salas_bp.route('/api/salas/', methods=['DELETE']) +def delete_sala(sala_id): + + sala = next((s for s in mock_salas if s["id"] == sala_id), None) + if not sala: + return jsonify({"erro": "Sala não encontrada"}), 404 + + reservas_ativas = [ + reserva for reserva in mock_reservas + if reserva["sala_id"] == sala_id and reserva["status"] == "ativa" + ] + + if reservas_ativas: + return jsonify({"erro": "Sala possui reservas ativas e não pode ser deletada"}), 409 + + mock_salas.remove(sala) + + return jsonify({"mensagem": "Sala deletada com sucesso!"}), 200 \ No newline at end of file diff --git a/backend/rotas/usuario.py b/backend/rotas/usuario.py new file mode 100644 index 00000000..5ea30501 --- /dev/null +++ b/backend/rotas/usuario.py @@ -0,0 +1,35 @@ +from flask import Blueprint, request, jsonify +from backend.modelo.extensao import db +from backend.modelo.usuario import Usuario + +usuarios_bp = Blueprint('usuarios', __name__) + +@usuarios_bp.route('/api/usuarios', methods=['GET']) +def get_usuarios(): + usuarios = Usuario.query.all() + + usuarios_json = [ + { + "id": usuario.id, + "nome": usuario.nome, + "cpf": usuario.cpf, + "email": usuario.email, + "professor": usuario.professor + } + for usuario in usuarios + ] + + return jsonify(usuarios_json), 200 + + +@usuarios_bp.route("/api/usuarios/", methods=["DELETE"]) +def deletar_usuario(usuario_id): + usuario = Usuario.query.get(usuario_id) + + if not usuario: + return jsonify({"erro": "Usuário não encontrado"}), 404 + + db.session.delete(usuario) + db.session.commit() + + return jsonify({"menssagem": "Usuário deletado com sucesso"}), 200 \ No newline at end of file diff --git a/backend/testes/__init__.py b/backend/testes/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/testes/__pycache__/__init__.cpython-313.pyc b/backend/testes/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 00000000..9085eede Binary files /dev/null and b/backend/testes/__pycache__/__init__.cpython-313.pyc differ diff --git a/backend/testes/__pycache__/common_step_definitions.cpython-313.pyc b/backend/testes/__pycache__/common_step_definitions.cpython-313.pyc new file mode 100644 index 00000000..f6cd8101 Binary files /dev/null and b/backend/testes/__pycache__/common_step_definitions.cpython-313.pyc differ diff --git a/backend/testes/__pycache__/conftest.cpython-313-pytest-8.3.4.pyc b/backend/testes/__pycache__/conftest.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 00000000..5c901c0d Binary files /dev/null and b/backend/testes/__pycache__/conftest.cpython-313-pytest-8.3.4.pyc differ diff --git a/backend/testes/__pycache__/test_atualizarReview.cpython-313-pytest-8.3.4.pyc b/backend/testes/__pycache__/test_atualizarReview.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 00000000..f1dc6684 Binary files /dev/null and b/backend/testes/__pycache__/test_atualizarReview.cpython-313-pytest-8.3.4.pyc differ diff --git a/backend/testes/__pycache__/test_cadastro_servicos.cpython-313-pytest-8.3.4.pyc b/backend/testes/__pycache__/test_cadastro_servicos.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 00000000..9006a689 Binary files /dev/null and b/backend/testes/__pycache__/test_cadastro_servicos.cpython-313-pytest-8.3.4.pyc differ diff --git a/backend/testes/__pycache__/test_criarReview.cpython-313-pytest-8.3.4.pyc b/backend/testes/__pycache__/test_criarReview.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 00000000..60ec2ea7 Binary files /dev/null and b/backend/testes/__pycache__/test_criarReview.cpython-313-pytest-8.3.4.pyc differ diff --git a/backend/testes/__pycache__/test_deletarReview.cpython-313-pytest-8.3.4.pyc b/backend/testes/__pycache__/test_deletarReview.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 00000000..20ff0de8 Binary files /dev/null and b/backend/testes/__pycache__/test_deletarReview.cpython-313-pytest-8.3.4.pyc differ diff --git a/backend/testes/__pycache__/test_listarReview.cpython-313-pytest-8.3.4.pyc b/backend/testes/__pycache__/test_listarReview.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 00000000..04a4bad1 Binary files /dev/null and b/backend/testes/__pycache__/test_listarReview.cpython-313-pytest-8.3.4.pyc differ diff --git a/backend/testes/__pycache__/test_login_servicos.cpython-313-pytest-8.3.4.pyc b/backend/testes/__pycache__/test_login_servicos.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 00000000..d200ab1a Binary files /dev/null and b/backend/testes/__pycache__/test_login_servicos.cpython-313-pytest-8.3.4.pyc differ diff --git a/backend/testes/__pycache__/test_manutencao.cpython-313-pytest-8.3.4.pyc b/backend/testes/__pycache__/test_manutencao.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 00000000..2b4fbd80 Binary files /dev/null and b/backend/testes/__pycache__/test_manutencao.cpython-313-pytest-8.3.4.pyc differ diff --git a/backend/testes/__pycache__/test_obterReview.cpython-313-pytest-8.3.4.pyc b/backend/testes/__pycache__/test_obterReview.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 00000000..87f97504 Binary files /dev/null and b/backend/testes/__pycache__/test_obterReview.cpython-313-pytest-8.3.4.pyc differ diff --git a/backend/testes/__pycache__/test_recursos.cpython-313-pytest-8.3.4.pyc b/backend/testes/__pycache__/test_recursos.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 00000000..28e54f0a Binary files /dev/null and b/backend/testes/__pycache__/test_recursos.cpython-313-pytest-8.3.4.pyc differ diff --git a/backend/testes/__pycache__/test_reservas.cpython-313-pytest-8.3.4.pyc b/backend/testes/__pycache__/test_reservas.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 00000000..f3fcb1d7 Binary files /dev/null and b/backend/testes/__pycache__/test_reservas.cpython-313-pytest-8.3.4.pyc differ diff --git a/backend/testes/__pycache__/test_salas.cpython-313-pytest-8.3.4.pyc b/backend/testes/__pycache__/test_salas.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 00000000..ff33adde Binary files /dev/null and b/backend/testes/__pycache__/test_salas.cpython-313-pytest-8.3.4.pyc differ diff --git a/backend/testes/common_step_definitions.py b/backend/testes/common_step_definitions.py new file mode 100644 index 00000000..a2ced8ee --- /dev/null +++ b/backend/testes/common_step_definitions.py @@ -0,0 +1,65 @@ +import json +import requests +from pytest_bdd import when, then, given, parsers + + +BASE_URL = 'http://127.0.0.1:5000' + +@when(parsers.parse('uma requisição "{metodo}" for enviada para "{url}" com o corpo: "{body}"')) +def mandar_requisicao(metodo, url, body): + url = f'{BASE_URL}/{url}' + + metodo = metodo.upper() + request_method = { + "POST": requests.post, + "GET": requests.get, + "DELETE": requests.delete, + "PUT": requests.put + }.get(metodo) + + assert request_method is not None, f"Método HTTP inválido: {metodo}" + + json_body = json.loads(body) if metodo in ["POST", "PUT"] else None + + response = request_method(url, json=json_body) + + mandar_requisicao.response = response + + +@then(parsers.parse('o status da resposta deve ser "{status}"')) +def checar_status(status): + assert mandar_requisicao.response.status_code == int(status), f'mensagem: "{mandar_requisicao.response.text}"' + +@then(parsers.parse('o JSON da resposta deve conter "{atributo}": "{string}"')) +def checar_mensagem(atributo, string): + response_json = mandar_requisicao.response.json() + + assert response_json.get(atributo) == string, ( + f"Erro: Esperado '{atributo}': '{string}', obtido '{response_json.get(atributo)}'" + ) + + +@given(parsers.parse('a sala de id "{sala_id:d}" está disponível no dia "{data}" das "{start_time}" às "{end_time}"')) +def sala_disponivel(sala_id, data, start_time, end_time): + url = f'{BASE_URL}/api/salas?data={data}&start_time={start_time}&end_time={end_time}' + response = requests.get(url) + + assert response.status_code == 200, f"Erro ao buscar salas disponíveis: {response.text}" + + sala_encontrada = any(sala['id'] == sala_id for sala in response.json()) + + assert sala_encontrada, (f'Sala {sala_id} não está disponível no horário solicitado ({data} das {start_time} ' + f'às {end_time})') + + +@given(parsers.parse('a sala de id "{sala_id:d}" não está disponível no dia "{data}" das "{start_time}" às "{end_time}"')) +def sala_nao_disponivel(sala_id, data, start_time, end_time): + url = f'{BASE_URL}/api/salas?data={data}&start_time={start_time}&end_time={end_time}' + response = requests.get(url) + + assert response.status_code == 200, f"Erro ao buscar salas disponíveis: {response.text}" + + sala_encontrada = any(sala['id'] == sala_id for sala in response.json()) + + assert not sala_encontrada, (f'Sala {sala_id} está disponível no horário solicitado ({data} das {start_time} ' + f'às {end_time})') \ No newline at end of file diff --git a/backend/testes/conftest.py b/backend/testes/conftest.py new file mode 100644 index 00000000..fe06c059 --- /dev/null +++ b/backend/testes/conftest.py @@ -0,0 +1,109 @@ +import pytest +from flask import Flask +from backend.rotas.criar_solicitacao_manutencao import criar_manutencao_bp +from backend.rotas.criar_solicitacao_recursos import criar_recursos_bp +from backend.rotas.editar_solicitacao_manutencao import editar_manutencao_bp +from backend.rotas.editar_solicitacao_recursos import editar_recursos_bp +from backend.rotas.excluir_solicitacao_manutencao import excluir_manutencao_bp +from backend.rotas.excluir_solicitacao_recursos import excluir_recursos_bp +from backend.rotas.criarReview import criar_review_bp +from backend.rotas.atualizarReview import atualizar_review_bp +from backend.rotas.deletarReview import deletar_review_bp +from backend.rotas.obterReview import obter_review_bp +from backend.rotas.listarReview import listar_reviews_bp +from backend.modelo.extensao import db +from backend.modelo.solicitacaomanutencao import SolicitacaoManutencao +from backend.modelo.solicitacaorecursos import SolicitacaoRecursos +from backend.modelo.reviewSala import ReviewSala +from backend.modelo.sala import Sala +from backend.modelo.reserva import Reserva +from ..rotas.cadastro import cadastro_bp +from ..rotas.login import login_bp +from ..modelo.extensao import db +from ..modelo.usuario import Usuario +from werkzeug.security import generate_password_hash + +@pytest.fixture(scope="module") +def app(): + aplicacao = Flask(__name__) + aplicacao.register_blueprint(criar_manutencao_bp) + aplicacao.register_blueprint(criar_recursos_bp) + aplicacao.register_blueprint(editar_manutencao_bp) + aplicacao.register_blueprint(editar_recursos_bp) + aplicacao.register_blueprint(excluir_manutencao_bp) + aplicacao.register_blueprint(excluir_recursos_bp) + aplicacao.register_blueprint(cadastro_bp) + aplicacao.register_blueprint(login_bp) + aplicacao.register_blueprint(criar_review_bp) + aplicacao.register_blueprint(atualizar_review_bp) + aplicacao.register_blueprint(deletar_review_bp) + aplicacao.register_blueprint(obter_review_bp) + aplicacao.register_blueprint(listar_reviews_bp) + aplicacao.config["TESTING"] = True + aplicacao.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///users.db" + aplicacao.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False + + with aplicacao.app_context(): + db.init_app(aplicacao) + db.create_all() + yield aplicacao + db.session.remove() + db.drop_all() + +@pytest.fixture(scope="module") +def client(app): + with app.test_client() as client: + yield client + +@pytest.fixture(scope="module", autouse=True) +def setup_database(app): + """Popula o banco de dados com usuários de teste antes dos testes rodarem.""" + with app.app_context(): + db.create_all() + + usuario1 = Usuario( + nome="Demosténes", + cpf="126.456.789-00", + email="demostenes@example.com", + professor="N", + siape=None, + senha=generate_password_hash("SecurePassword123"), + ) + + usuario2 = Usuario( + nome="Vanessa", + cpf="321.879.789-33", + email="vanessa@example.com", + professor="S", + siape="101010", + senha=generate_password_hash("12345678"), + ) + + manutencao1 = SolicitacaoManutencao( + reserva_id=1, + descricao="Mesa quebrada." + ) + + # Criando solicitações de recursos + recurso1 = SolicitacaoRecursos( + reserva_id=2, + recursos="Projetor, Teclado", + itens_nao_listados="Extensão elétrica", + observacoes="Para aula prática" + ) +# recurso2 = SolicitacaoRecursos( +# reserva_id=3, +# descricao="Necessidade de cadeiras extras.", +# quantidade=5 +# ) + + db.session.add_all([manutencao1, recurso1]) + db.session.add_all([usuario1, usuario2]) + db.session.commit() + yield + db.session.remove() + db.drop_all() + +@pytest.fixture +def contexto(): + return {} diff --git a/backend/testes/test_atualizarReview.py b/backend/testes/test_atualizarReview.py new file mode 100644 index 00000000..0bdc2682 --- /dev/null +++ b/backend/testes/test_atualizarReview.py @@ -0,0 +1,58 @@ +import json +import pytest +import sys +import os +from pathlib import Path +sys.path.append(str(Path(__file__).resolve().parent.parent)) +from backend.modelo.extensao import db +from backend.modelo.reviewSala import ReviewSala +from flask.testing import FlaskClient +from datetime import datetime + +@pytest.fixture(scope="module") +def criar_review(app): + with app.app_context(): + review = ReviewSala( + reserva_id=1, + sala_id=2, + usuario_id=3, + nota=4, + comentario="Sala boa, mas com algumas falhas.", + ) + db.session.add(review) + db.session.commit() + db.session.refresh(review) + + return review + + +def test_atualizar_review_existente(client, criar_review, app): + dados_atualizados = { + "nota": 5, + "comentario": "Modificações feitas, a sala está impecável agora!" + } + + response = client.put(f"/api/reviews/{criar_review.id}", json=dados_atualizados) + + assert response.status_code == 200 + + data = json.loads(response.data) + assert data['mensagem'] == "Avaliação atualizada com sucesso!" + + with app.app_context(): + review_atualizado = db.session.get(ReviewSala, criar_review.id) + assert review_atualizado.nota == 5 + assert review_atualizado.comentario == "Modificações feitas, a sala está impecável agora!" + +def test_atualizar_review_nao_existente(client): + dados_atualizados = { + "nota": 5, + "comentario": "Falhas corrigidas, a sala está impecável!" + } + + response = client.put("/api/reviews/9999", json=dados_atualizados) + + assert response.status_code == 404 + + data = json.loads(response.data) + assert data['error'] == "Avaliação não encontrada para o ID fornecido." \ No newline at end of file diff --git a/backend/testes/test_cadastro_servicos.py b/backend/testes/test_cadastro_servicos.py new file mode 100644 index 00000000..b3db7dd9 --- /dev/null +++ b/backend/testes/test_cadastro_servicos.py @@ -0,0 +1,89 @@ +from pytest_bdd import scenario, given, when, then, parsers +import pytest + +# Cenários de teste_ +@scenario("../features/CadastroServico.feature", "Sucesso no cadastro de usuário") +def teste_SucessoUsuario(): + pass + +@scenario("../features/CadastroServico.feature", "Sucesso no cadastro de professor") +def teste_SucessoProfessor(): + pass + +@scenario("../features/CadastroServico.feature", "Fracasso no cadastro por campos obrigatórios não preenchidos") +def teste_ErroObrigatorio(): + pass + +@scenario("../features/CadastroServico.feature", "Fracasso no cadastro por siape já registrado") +def teste_ErroSiapeRegistrado(): + pass + +@scenario("../features/CadastroServico.feature", "Fracasso no cadastro por duplicação de ID única") +def teste_ErroCadastroDuplo(): + pass + +@scenario("../features/CadastroServico.feature", "Fracasso no cadastro por senhas que não coincidem") +def teste_ErroCadastroSenha(): + pass + +@scenario("../features/CadastroServico.feature", "Fracasso no cadastro por formato inválido de email") +def teste_ErroEmailInvalido(): + pass + +@scenario("../features/CadastroServico.feature", "Fracasso no cadastro por formato inválido de CPF") +def teste_ErroCpfInvalido(): + pass + +# Given +@given("o usuário deseja se cadastrar") +def usuarioInicioCadastro(contexto): + contexto["dados_cadastro"] = {} + +# When +@when(parsers.parse('ele informa o nome "{nome}"')) +def informarNome(contexto, nome): + contexto["dados_cadastro"]["nome"] = nome + +@when(parsers.parse('ele informa o CPF "{cpf}"')) +def informarCpf(contexto, cpf): + contexto["dados_cadastro"]["cpf"] = cpf + +@when(parsers.parse('ele informa o email "{email}"')) +def informarEmail(contexto, email): + contexto["dados_cadastro"]["email"] = email + +@when(parsers.parse('ele informa se é professor "{professor}"')) +def informarProfessor(contexto, professor): + contexto["dados_cadastro"]["professor"] = professor + +@when(parsers.parse('ele informa o SIAPE "{siape}"')) +def informarSiape(contexto, siape): + contexto["dados_cadastro"]["siape"] = siape + +@when(parsers.parse('ele informa a senha "{senha}"')) +def informarSenha(contexto, senha): + contexto["dados_cadastro"]["senha"] = senha + +@when(parsers.parse('ele informa a confirmação da senha "{confirmar_senha}"')) +def confirmarSenha(contexto, confirmar_senha): + contexto["dados_cadastro"]["confirmarSenha"] = confirmar_senha + +@when(parsers.parse('ele envia uma requisição POST para "/cadastro"')) +def enviarCadastro(client, contexto): + resposta = client.post("/cadastro", json=contexto["dados_cadastro"]) + contexto["resposta"] = resposta + +@when(parsers.parse('ele deixa o campo "Confirmar Senha" com ""')) +def senhaVazio(contexto): + pass +# Then +@then(parsers.parse('a resposta deve conter a mensagem "{mensagem}"')) +def verificarMensagem(contexto, mensagem): + resposta_json = contexto["resposta"].get_json() + mensagem_real = resposta_json.get("message") or resposta_json.get("error") + assert mensagem_real == mensagem, f"Esperado: {mensagem}, Recebido: {mensagem_real}" + +@then(parsers.parse('o status code deve ser "{status_code}"')) +def verificarStatusCode(contexto, status_code): + status_code = int(status_code) + assert contexto["resposta"].status_code == status_code, f"Esperado: {status_code}, Recebido: {contexto['resposta'].status_code}" diff --git a/backend/testes/test_criarReview.py b/backend/testes/test_criarReview.py new file mode 100644 index 00000000..aaac2962 --- /dev/null +++ b/backend/testes/test_criarReview.py @@ -0,0 +1,97 @@ +import pytest +import requests +from pytest_bdd import scenarios, given, when, then + +scenarios('../features/criarReview.feature') + +BASE_URL = "http://127.0.0.1:5000/api/reviews" + +@pytest.fixture +def context(): + return {} + +@given('que o professor Suruagy deseja fazer uma avaliação pós reserva com reserva_id "1" para a sala_id "2"') +@given('que o professor Suruagy deseja fazer uma avaliação pós reserva com reserva_id ""') +@given('que o professor Suruagy deseja fazer uma avaliação pós reserva com reserva_id "1" para a sala_id ""') +def preparar_contexto(context): + context['url'] = BASE_URL + +@when('ele envia uma requisição POST para "/api/reviews" com os dados reserva_id "1", sala_id "2", usuario_id "3", nota "4" e comentário "Sala boa, mas com algumas falhas."') +def enviar_review_valida(context): + payload = { + "reserva_id": 1, + "sala_id": 2, + "usuario_id": 3, + "nota": 4, + "comentario": "Sala boa, mas com algumas falhas." + } + context['response'] = requests.post(context['url'], json=payload) + +@when('ele envia uma requisição POST para "/api/reviews" com os dados reserva_id "1", sala_id "2", usuario_id "3", nota "" e comentário "Sala boa, mas sem computador!"') +def enviar_review_sem_nota(context): + payload = { + "reserva_id": 1, + "sala_id": 2, + "usuario_id": 3, + "nota": None, + "comentario": "Sala boa, mas sem computador!" + } + context['response'] = requests.post(context['url'], json=payload) + +@when('ele envia uma requisição POST para "/api/reviews" com os dados reserva_id "1", sala_id "", usuario_id "3", nota "4" e comentário "Sala boa, mas sem computador!"') +def enviar_review_sem_sala_id(context): + payload = { + "reserva_id": 1, + "sala_id": None, + "usuario_id": 3, + "nota": 4, + "comentario": "Sala boa, mas sem computador!" + } + context['response'] = requests.post(context['url'], json=payload) + +@when('ele envia uma requisição POST para "/api/reviews" com os dados reserva_id "1", sala_id "2", usuario_id "", nota "4" e comentário "Sala boa, mas sem computador!"') +def enviar_review_sem_usuario_id(context): + payload = { + "reserva_id": 1, + "sala_id": 2, + "usuario_id": None, + "nota": 4, + "comentario": "Sala boa, mas sem computador!" + } + context['response'] = requests.post(context['url'], json=payload) + +@when('ele envia uma requisição POST para "/api/reviews" com os dados reserva_id "", sala_id "2", usuario_id "3", nota "4" e comentário "Sala boa, mas sem computador!"') +def enviar_review_sem_reserva_id(context): + payload = { + "reserva_id": None, + "sala_id": 2, + "usuario_id": 3, + "nota": 4, + "comentario": "Sala boa, mas sem computador!" + } + context['response'] = requests.post(context['url'], json=payload) + +@then('o sistema retorna "Avaliação criada com sucesso!" com o status 201') +def validar_resposta_sucesso(context): + assert context['response'].status_code == 201 + assert context['response'].json()["mensagem"] == "Avaliação criada com sucesso!" + +@then('o sistema retorna "A nota é obrigatória para avaliar a sala." com o status 400') +def validar_resposta_sem_nota(context): + assert context['response'].status_code == 400 + assert context['response'].json()["error"] == "A nota é obrigatória para avaliar a sala." + +@then('o sistema retorna "O ID da Sala é obrigatório para avaliar a sala." com o status 400') +def validar_resposta_sem_sala_id(context): + assert context['response'].status_code == 400 + assert context['response'].json()["error"] == "O ID da Sala é obrigatório para avaliar a sala." + +@then('o sistema retorna "O ID do Usuário é obrigatório para avaliar a sala." com o status 400') +def validar_resposta_sem_usuario_id(context): + assert context['response'].status_code == 400 + assert context['response'].json()["error"] == "O ID do Usuário é obrigatório para avaliar a sala." + +@then('o sistema retorna "O ID da Reserva é obrigatório para avaliar a sala." com o status 400') +def validar_resposta_sem_reserva_id(context): + assert context['response'].status_code == 400 + assert context['response'].json()["error"] == "O ID da Reserva é obrigatório para avaliar a sala." diff --git a/backend/testes/test_deletarReview.py b/backend/testes/test_deletarReview.py new file mode 100644 index 00000000..7429ef3d --- /dev/null +++ b/backend/testes/test_deletarReview.py @@ -0,0 +1,44 @@ +import sys +import os +from pathlib import Path + +sys.path.append(str(Path(__file__).resolve().parent.parent)) +import pytest +import json +from datetime import datetime +from backend.modelo.extensao import db +from backend.modelo.reviewSala import ReviewSala + + +@pytest.fixture +def criar_review(app): + with app.app_context(): + review = ReviewSala( + reserva_id=1, + sala_id=2, + usuario_id=3, + nota=4, + comentario="Sala boa, mas com algumas falhas.", + ) + db.session.add(review) + db.session.commit() + db.session.refresh(review) + return review + +def test_deletar_review_existente(client, criar_review, app): + response = client.delete(f"/api/reviews/{criar_review.id}") + assert response.status_code == 200 + + data = json.loads(response.data) + assert data['mensagem'] == "Avaliação deletada com sucesso!" + + with app.app_context(): + review_deletado = db.session.get(ReviewSala, criar_review.id) + assert review_deletado is None + +def test_deletar_review_nao_existente(client): + response = client.delete("/api/reviews/9999") + assert response.status_code == 404 + + data = json.loads(response.data) + assert data['error'] == "Avaliação não encontrada para o ID fornecido." \ No newline at end of file diff --git a/backend/testes/test_listarReview.py b/backend/testes/test_listarReview.py new file mode 100644 index 00000000..7cac8629 --- /dev/null +++ b/backend/testes/test_listarReview.py @@ -0,0 +1,62 @@ +import sys +import os +from pathlib import Path +sys.path.append(str(Path(__file__).resolve().parent.parent)) +import json +import pytest +from datetime import datetime +from pytest_bdd import scenarios, given, when, then + +scenarios('../features/listarReview.feature') + +from backend.modelo.extensao import db +from backend.modelo.reviewSala import ReviewSala + + +@pytest.fixture +def criar_avaliacao(app): + with app.app_context(): + review = ReviewSala( + reserva_id=1, + sala_id=2, + usuario_id=3, + nota=5, + comentario="Sala excelente, as mudanças foram feitas e ficou ótima.", + ) + db.session.add(review) + db.session.commit() + db.session.refresh(review) + return review + +@given('o professor Suruagy deseja consultar as avaliações presentes no sistema e existem avaliações cadastradas') +def existem_avaliacoes(criar_avaliacao): + pass + +@given('o professor Suruagy deseja consultar as avaliações presentes no sistema e não existem avaliações cadastradas') +def nao_existem_avaliacoes(app): + with app.app_context(): + db.session.query(ReviewSala).delete() + db.session.commit() + +@when('ele envia uma requisição GET para "/api/reviews"', target_fixture="enviar_requisicao_listar") +def enviar_requisicao_listar(client): + return client.get("/api/reviews") + +@then('o sistema lista todas as avaliações que foram postadas anteriormente para todas as salas com o status 200 OK') +def validar_listagem_sucesso(enviar_requisicao_listar): + response = enviar_requisicao_listar + assert response.status_code == 200 + data = json.loads(response.data) + assert isinstance(data, list) + assert len(data) > 0 + +@then('o sistema não encontra nenhuma avaliação no sistema') +def validar_listagem_vazia(enviar_requisicao_listar): + response = enviar_requisicao_listar + assert response.status_code == 404 + +@then('exibe a mensagem de erro "Nenhuma avaliação encontrada." com o status 404 NOT FOUND') +def validar_mensagem_erro(enviar_requisicao_listar): + response = enviar_requisicao_listar + data = json.loads(response.data) + assert data['error'] == "Nenhuma avaliação encontrada." \ No newline at end of file diff --git a/backend/testes/test_login_servicos.py b/backend/testes/test_login_servicos.py new file mode 100644 index 00000000..6fdb94dc --- /dev/null +++ b/backend/testes/test_login_servicos.py @@ -0,0 +1,57 @@ +from pytest_bdd import scenario, given, when, then, parsers +import pytest + +# Cenários de teste_ +@scenario("../features/loginServico.feature", "Sucesso no login") +def teste_SucessoLogin(): + pass + +@scenario("../features/loginServico.feature", "Fracasso no login por senha incorreta") +def teste_FracassoLogin(): + pass + +@scenario("../features/loginServico.feature", "Fracasso no login por falta de email ou senha") +def teste_FracassoLoginSemEmailOuSenha(): + pass + +# Given +@given(parsers.parse('o usuário possui o email "{email}" e a senha "{senha}" válidos')) +def usuarioCredenciais(contexto, email, senha): + contexto["email"] = email + contexto["senha"] = senha + +@given(parsers.parse('o usuário possui o email "{email}" válido e a senha "{senha}" inválida')) +def usuarioSenhaInvalida(contexto, email, senha): + contexto["email"] = email + contexto["senha"] = senha + +@given("o usuário envia uma requisição sem email ou senha") +def usuarioSemCredenciais(contexto): + contexto["email"] = "" + contexto["senha"] = "" + +# When +@when(parsers.parse('ele envia uma requisição POST para "/api/login" com os dados "{email}" e "{senha}"')) +def enviarRequisicaoLogin(client, contexto, email, senha): + resposta = client.post("/", json={"email": email, "senha": senha}) + contexto["resposta"] = resposta + + + +# Then +@then(parsers.parse('a resposta deve conter o email "{email}" e a mensagem "success" igual a True')) +def verificarRespostaSucesso(contexto, email): + respostaJson = contexto["resposta"].get_json() + assert respostaJson["usuario"]["email"] == email, f"Esperado: {email}, Recebido: {respostaJson}" + assert respostaJson["success"] == True, f"Esperado: True, Recebido: {respostaJson['success']}" + +@then(parsers.parse('a resposta deve conter a mensagem "{mensagem}"')) +def verificarRespostaFalha(contexto, mensagem): + respostaJson = contexto["resposta"].get_json() + assert respostaJson["error"] == mensagem, f"Esperado: {mensagem}, Recebido: {respostaJson}" + + +@then(parsers.parse('o status code deve ser "{status_code}"')) +def verificarStatusCode(contexto, status_code): + status_code = int(status_code) + assert contexto["resposta"].status_code == status_code, f"Esperado: {status_code}, Recebido: {contexto['resposta'].status_code}" diff --git a/backend/testes/test_manutencao.py b/backend/testes/test_manutencao.py new file mode 100644 index 00000000..b26ecb0c --- /dev/null +++ b/backend/testes/test_manutencao.py @@ -0,0 +1,77 @@ +from pytest_bdd import scenario, given, when, then, parsers +import pytest + +# Cenários de teste_ +@scenario("../features/manutencao.feature", "sucesso ao criar uma solicitação de manutenção para uma reserva concluída") +def teste_SucessoCriarManutencao(): + pass + +@scenario("../features/manutencao.feature", "fracasso ao criar uma solicitação de manutenção sem preencher o campo descricao") +def teste_FracassoCriarManutencao(): + pass + +@scenario("../features/manutencao.feature", "sucesso ao editar uma solicitação de manutenção existente") +def teste_SucessoEditarManutencao(): + pass + +@scenario("../features/manutencao.feature", "fracasso ao editar solicitação de manutenção sem preencher o campo descricao") +def teste_FracassoEditarManutencao(): + pass + +@scenario("../features/manutencao.feature", "sucesso ao excluir uma solicitação de manutenção existente") +def teste_SucessoExcluirManutencao(): + pass + +# Given +@given(parsers.parse('o professor possui uma reserva de sala reserva_id "{reserva_id}" que já foi encerrada')) +def reserva_encerrada(contexto, reserva_id): + contexto["reserva_id"] = int(reserva_id) + +@given(parsers.parse('o professor já criou uma solicitação de manutenção associada a reserva_id "{reserva_id}"')) +def criar_solicitacao_previa(contexto, reserva_id): + contexto["reserva_id"] = int(reserva_id) + contexto["descricao"] = "Mesa quebrada." + contexto["manutencao_id"] = 1 # Simulando um ID gerado pelo sistema + +# When +@when(parsers.parse('ele envia uma requisição POST /solicitacoes/manutencao com os dados: reserva_id: "{reserva_id}", descricao: "{descricao}"')) +def criar_solicitacao(client, contexto, reserva_id, descricao): + resposta = client.post("/solicitacoes/manutencao", json={"reserva_id": reserva_id, "descricao": descricao}) + contexto["resposta"] = resposta + contexto["descricao"] = descricao + +@when(parsers.parse('ele envia uma requisição PUT /solicitacoes/manutencao/{id} contendo o ID da solicitação de manutenção e a alteração descricao: "{descricao}"')) +def editar_solicitacao(client, contexto, descricao): + import json + manutencao_id = contexto["manutencao_id"] + resposta = client.put(f"/solicitacoes/manutencao/{manutencao_id}", json={"descricao": descricao}) + contexto["resposta"] = resposta + dados = json.loads(resposta.data) + contexto["descricao"] = dados.get("descricao") + +@when(parsers.parse('ele envia uma requisição DELETE /solicitacoes/manutencao/{id}')) +def deletar_solicitacao(client, contexto): + manutencao_id = contexto["manutencao_id"] + resposta = client.delete(f"/solicitacoes/manutencao/{manutencao_id}") + contexto["resposta"] = resposta + + +# Then +@then(parsers.parse('o sistema retorna "{mensagem_tipo}" "{mensagem}" e um status "{status}"')) +def verificar_resposta(contexto, mensagem_tipo, mensagem, status): + resposta = contexto["resposta"] + assert resposta.status_code == int(status), f"{resposta}" + assert resposta.get_json()[mensagem_tipo] == mensagem + +@then(parsers.parse('a reserva reserva_id: "{reserva_id}" possui uma solicitação de manutenção com descricao: "{descricao}"')) +def verificar_solicitacao_de_manutencao(contexto, reserva_id, descricao): + assert contexto["reserva_id"] == int(reserva_id) + assert contexto["descricao"] == descricao + +@then(parsers.parse('o sistema atualiza os detalhes da solicitação com descricao: "{descricao}"')) +def verificar_solicitacao_de_manutencao(contexto, descricao): + assert contexto["descricao"] == descricao + +@then(parsers.parse('o sistema remove a solicitação do banco de dados e retorna um status "{status}"')) +def verificar_exclusao_de_manutencao(contexto, status): + assert contexto["resposta"].status_code == int(status) \ No newline at end of file diff --git a/backend/testes/test_obterReview.py b/backend/testes/test_obterReview.py new file mode 100644 index 00000000..85fba458 --- /dev/null +++ b/backend/testes/test_obterReview.py @@ -0,0 +1,48 @@ +import sys +import os +from pathlib import Path +sys.path.append(str(Path(__file__).resolve().parent.parent)) + +import json +import pytest +from datetime import datetime + +from backend.modelo.extensao import db +from backend.modelo.reviewSala import ReviewSala + +@pytest.fixture(scope="module") +def criar_review(app): + with app.app_context(): + review = ReviewSala( + reserva_id=1, + sala_id=2, + usuario_id=3, + nota=5, + comentario="Sala excelente, as mudanças foram feitas e ficou ótima.", + ) + db.session.add(review) + db.session.commit() + db.session.refresh(review) + return review + +def test_obter_review_existente(client, criar_review): + response = client.get(f"/api/reviews/{criar_review.id}") + + assert response.status_code == 200 + + data = json.loads(response.data) + + assert data['id'] == criar_review.id + assert data['reserva_id'] == 1 + assert data['sala_id'] == 2 + assert data['usuario_id'] == 3 + assert data['nota'] == 5 + assert data['comentario'] == "Sala excelente, as mudanças foram feitas e ficou ótima." + +def test_obter_review_inexistente(client): + response = client.get("/api/reviews/9999") + + assert response.status_code == 404 + + data = json.loads(response.data) + assert data['error'] == "Avaliação não encontrada para o ID fornecido." \ No newline at end of file diff --git a/backend/testes/test_recursos.py b/backend/testes/test_recursos.py new file mode 100644 index 00000000..15fcb0c2 --- /dev/null +++ b/backend/testes/test_recursos.py @@ -0,0 +1,102 @@ +from pytest_bdd import scenario, given, when, then, parsers +import pytest + +# Cenários de teste +@scenario("../features/recursos.feature", "sucesso ao criar solicitação de recursos para uma reserva ativa com todos os campos preenchidos") +def teste_SucessoCriarSolicitacao(): + pass + +@scenario("../features/recursos.feature", "fracasso ao criar solicitação de recursos com campos preenchidos com espaços ou não preenchidos") +def teste_FracassoCriarSolicitacaoCamposVazios(): + pass + +@scenario("../features/recursos.feature", "fracasso ao criar solicitação de recursos com apenas o campo observacoes preenchido") +def teste_FracassoCriarSolicitacaoSomenteObservacoes(): + pass + +@scenario("../features/recursos.feature", "sucesso ao criar solicitação de recursos sem preencher o campo itens_nao_listados") +def teste_SucessoCriarSolicitacaoSemItensNaoListados(): + pass + +@scenario("../features/recursos.feature", "sucesso ao criar solicitação de recursos sem preencher o campo itens_nao_listados e o campo observacoes") +def teste_SucessoCriarSolicitacaoBasica(): + pass + +@scenario("../features/recursos.feature", "sucesso ao criar solicitação de recursos com o campo de recursos vazio e apenas especificando os itens não listados") +def teste_SucessoCriarSolicitacaoComItensNaoListados(): + pass + +@scenario("../features/recursos.feature", "sucesso ao criar solicitação de recursos com o campo de recursos vazio e especificando os itens não listados e as observacoes") +def teste_SucessoCriarSolicitacaoComItensNaoListadosObs(): + pass + +@scenario("../features/recursos.feature", "sucesso ao editar uma solicitação de recursos existente") +def teste_SucessoEditarSolicitacao(): + pass + +@scenario("../features/recursos.feature", "fracasso ao editar uma solicitação de recursos com apenas o campo observacoes preenchido") +def teste_FracassoEditarSolicitacao(): + pass + +@scenario("../features/recursos.feature", "sucesso ao excluir uma solicitação de recursos existente") +def teste_SucessoExcluirSolicitacao(): + pass + +# Given +@given(parsers.parse('o professor possui uma reserva ativa com reserva_id "{reserva_id}"')) +def reserva_ativa(contexto, reserva_id): + contexto["reserva_id"] = int(reserva_id) + +@given(parsers.parse('o professor possui uma solicitação de recursos associada a reserva_id "{reserva_id}"')) +def criar_solicitacao_previa(contexto, reserva_id): + contexto["reserva_id"] = int(reserva_id) + contexto["recursos"] = "Projetor, Teclado" + contexto["solicitacao_id"] = 1 + +# When +@when(parsers.parse('ele envia uma requisição POST /solicitacoes/recursos com os dados: reserva_id: "{reserva_id}", recursos: "{recursos}", itens_nao_listados: "{itens_nao_listados}", observacoes: "{observacoes}"')) +def criar_solicitacao(client, contexto, reserva_id, recursos, itens_nao_listados, observacoes): + resposta = client.post("/solicitacoes/recursos", json={"reserva_id": reserva_id, "recursos": recursos, "itens_nao_listados": itens_nao_listados, "observacoes": observacoes}) + contexto["resposta"] = resposta + contexto["recursos"] = recursos + contexto["itens_nao_listados"] = itens_nao_listados + contexto["observacoes"] = observacoes + +@when(parsers.parse('ele envia uma requisição PUT /solicitacoes/recursos/{id} contendo o ID da solicitação e os novos detalhes da solicitação recursos: "{recursos}", observacoes: "{observacoes}"')) +def editar_solicitacao(client, contexto, recursos, observacoes): + import json + solicitacao_id = contexto["solicitacao_id"] + resposta = client.put(f"/solicitacoes/recursos/{solicitacao_id}", json={"recursos": recursos, "observacoes": observacoes}) + contexto["resposta"] = resposta + dados = json.loads(resposta.data) + contexto["recursos"] = recursos + contexto["observacoes"] = observacoes + +@when(parsers.parse('ele envia uma requisição DELETE /solicitacoes/recursos/{id} contendo o ID da solicitação')) +def deletar_solicitacao(client, contexto): + solicitacao_id = contexto["solicitacao_id"] + resposta = client.delete(f"/solicitacoes/recursos/{solicitacao_id}") + contexto["resposta"] = resposta + +# Then +@then(parsers.parse('o sistema retorna "{mensagem_tipo}" "{mensagem}" e um status "{status}"')) +def verificar_resposta(contexto, mensagem_tipo, mensagem, status): + resposta = contexto["resposta"] + assert resposta.status_code == int(status) + assert resposta.get_json()[mensagem_tipo] == mensagem + +@then(parsers.parse('a reserva_id "{reserva_id}" possui uma solicitação com recursos "{recursos}", itens_nao_listados "{itens_nao_listados}" e observacoes "{observacoes}"')) +def verificar_solicitacao(contexto, reserva_id, recursos, itens_nao_listados, observacoes): + assert contexto["reserva_id"] == int(reserva_id) + assert contexto["recursos"] == recursos + assert contexto["itens_nao_listados"] == itens_nao_listados + assert contexto["observacoes"] == observacoes + +@then(parsers.parse('o sistema atualiza os detalhes da solicitação: recursos: "{recursos}", observacoes: "{observacoes}"')) +def verificar_solicitacao_de_manutencao(contexto, recursos, observacoes): + assert contexto["recursos"] == recursos + assert contexto["observacoes"] == observacoes + +@then(parsers.parse('o sistema remove a solicitação do banco de dados e retorna um status "{status}"')) +def verificar_exclusao(contexto, status): + assert contexto["resposta"].status_code == int(status) diff --git a/backend/testes/test_reservas.py b/backend/testes/test_reservas.py new file mode 100644 index 00000000..2e5cb556 --- /dev/null +++ b/backend/testes/test_reservas.py @@ -0,0 +1,78 @@ +import requests +from pytest_bdd import scenario, when, then, given, parsers +from backend.testes.common_step_definitions import * + +BASE_URL = 'http://127.0.0.1:5000' + + +@scenario('../features/reservas.feature', 'Criar uma reserva com sucesso') +def test_criar_uma_reserva_com_sucesso(): + pass + +@scenario('../features/reservas.feature', 'Erro ao tentar reservar uma sala já ocupada') +def test_erro_ao_tentar_reservar_sala_ocupada(): + pass + +@scenario('../features/reservas.feature', 'Erro ao tentar reservar com campos ausentes') +def test_erro_ao_tentar_reservar_campos_ausentes(): + pass + +@scenario('../features/reservas.feature', 'Cancelar uma reserva com sucesso') +def test_cancelar_uma_reserva_com_sucesso(): + pass + +@scenario('../features/reservas.feature', 'Erro ao tentar cancelar uma reserva inexistente') +def test_cancelar_uma_reserva_inexistente(): + pass + + +@given(parsers.parse('o professor de id "{professor_id:d}" não tem uma reserva no dia "{data}" ' + 'das "{start_time}" às "{end_time}"')) +def professor_disponivel(professor_id, data, start_time, end_time): + url = f'{BASE_URL}/api/reservas/{professor_id}' + response = requests.get(url) + reservas = response.json() + + for reserva in reservas: + if reserva['data'] == data and reserva['status'] == ['ativa']: + assert (start_time >= reserva['end_time'] or end_time <= reserva['start_time']), \ + f'Professor {professor_id} já tem uma reserva nesse horário' + + +@given(parsers.parse('o professor de id "{professor_id:d}" tem uma reserva ativa de id "{reserva_id}"')) +def professor_disponivel(professor_id, reserva_id): + url = f'{BASE_URL}/api/reservas/{professor_id}' + response = requests.get(url) + + assert response.status_code == 200, f'Erro ao buscar reservas do professor {professor_id}' + + reservas = response.json() + + reserva_encontrada = any( + reserva['id'] == int(reserva_id) and reserva["status"] for reserva in reservas + ) + + assert reserva_encontrada, f"O professor {professor_id} não tem uma reserva ativa de id {reserva_id}" + + +@then(parsers.parse('o JSON da reserva deve conter "{atributo}": "{valor}"')) +def checar_atributo(atributo, valor): + response_json = mandar_requisicao.response.json() + + assert "reservation" in response_json, ( + f"Erro: 'reservation' não encontrado na resposta:\n{response_json}" + ) + + assert atributo in response_json["reservation"], ( + f"Erro: Atributo '{atributo}' não encontrado dentro de 'reservation':\n{response_json}" + ) + + if valor.isdigit(): + valor = int(valor) + + valor_real = response_json["reservation"][atributo] + + assert valor_real == valor, ( + f"Erro: Para '{atributo}', esperado '{valor}' ({type(valor).__name__}), " + f"obtido '{valor_real}' ({type(valor_real).__name__})" + ) \ No newline at end of file diff --git a/backend/testes/test_salas.py b/backend/testes/test_salas.py new file mode 100644 index 00000000..d3be2dc6 --- /dev/null +++ b/backend/testes/test_salas.py @@ -0,0 +1,126 @@ +import json +import requests +from pytest_bdd import scenario, when, then, given, parsers + +from backend.testes.common_step_definitions import * + + +BASE_URL = 'http://127.0.0.1:5000' + +@scenario('../features/salas.feature', 'Criar sala com sucesso') +def test_criar_sala_com_sucesso(): + pass + +@scenario('../features/salas.feature', 'Buscar todas as salas disponíveis') +def test_buscar_salas(): + pass + +@scenario('../features/salas.feature', 'Erro ao buscar salas sem data preenchida') +def test_erro_ao_buscar_salas_data(): + pass + +@scenario('../features/salas.feature', 'Erro ao buscar salas com tempo não informado') +def test_erro_ao_buscar_salas_tempo(): + pass + +@scenario('../features/salas.feature', 'Erro ao tentar deletar sala com reserva ativa') +def test_erro_ao_tentar_deletar_sala_com_reserva(): + pass + +@given(parsers.parse('a sala de id "{sala_id}" tem uma reserva ativa')) +def sala_tem_reserva_ativa(sala_id): + sala_id = int(sala_id) + + response = requests.get(f"{BASE_URL}/api/reservas") + assert response.status_code == 200, f"Erro ao buscar reservas: {response.text}" + + reservas = response.json() + + reserva_existente = any( + reserva for reserva in reservas if reserva["sala_id"] == sala_id and reserva["status"] == "ativa" + ) + + if not reserva_existente: + reserva_body = { + "sala_id": sala_id, + "data": "2025-02-25", + "start_time": "14:00", + "end_time": "15:00" + } + + reserva_response = requests.post(f"{BASE_URL}/api/reservas/3", json=reserva_body) # Aqui eu crio só para o professor de id 3 + assert reserva_response.status_code == 201, f"Erro ao criar reserva ativa: {reserva_response.text}" + + +@given(parsers.parse('existe uma sala com nome "{nome}"')) +def existe_uma_sala_com_nome(nome): + response = requests.get(f"{BASE_URL}/api/salas") + + salas = response.json() + + + sala_existente = next((sala for sala in salas if sala["nome"] == nome), None) + + if not sala_existente: + sala_body = { + "nome": nome, + "tipo": "Reunião", + "lugares": 10, + "andar": 0, + "equipamentos": ["Projetor"] + } + + sala_response = requests.post(f"{BASE_URL}/api/salas", json=sala_body) + assert sala_response.status_code == 201, f"Erro ao criar sala: {response.text}" + +@given(parsers.parse('não existe uma sala com nome "{nome}"')) +def sala_nao_existe(nome): + + response = requests.get(f"{BASE_URL}/api/salas") + assert response.status_code == 200, "Erro ao buscar salas: " + response.text + + salas = response.json() + + for sala in salas: + if sala["nome"] == nome: + sala_id = sala["id"] + + # Deleta se necessário + delete_response = requests.delete(f"{BASE_URL}/api/salas/{sala_id}") + assert delete_response.status_code in [200, 204], "Erro ao deletar sala: " + delete_response.text + + +@then(parsers.parse('o JSON da sala deve conter "{atributo}": "{valor}"')) +def checar_atributo(atributo, valor): + response_json = mandar_requisicao.response.json() + + assert "sala" in response_json, f"Erro; 'sala' não encontrado na resposta: \n{response_json}" + + assert atributo in response_json["sala"], \ + f"Erro: Atributo '{atributo}' não encontrado dentro de 'sala': \n{response_json}" + + if valor.isdigit(): + valor = int(valor) + + valor_real = response_json["sala"][atributo] + + assert valor_real == valor, ( + f"Erro: Para '{atributo}', esperado '{valor}' ({type(valor).__name__}), " + f"obtido '{valor_real}' ({type(valor_real).__name__})" + ) + + +@then('o JSON da resposta deve conter uma lista de salas com todos os dados') +def checar_lista_de_salas(): + response_json = mandar_requisicao.response.json() + + assert isinstance(response_json, list), f'Uma lista de salas não for retornada' + assert len(response_json) > 0, f'A lista de salas retornada está vazia' + + for sala in response_json: + assert "id" in sala, f"'id' faltando: {sala}" + assert "tipo" in sala, f"'tipo' faltando: {sala}" + assert "lugares" in sala, f"'lugares' faltando: {sala}" + assert 'andar' in sala, f"'andar' faltando: {sala}" + assert 'equipamentos' in sala, f"'equipamentos' faltando: {sala}" + assert 'nome' in sala, f"'nome' faltando: {sala}" diff --git a/cypress/e2e/features/cadastro.feature b/cypress/e2e/features/cadastro.feature new file mode 100644 index 00000000..dcd24e2b --- /dev/null +++ b/cypress/e2e/features/cadastro.feature @@ -0,0 +1,42 @@ +Feature: Cadastro de usuário no frontend + + Scenario: Cadastro bem-sucedido + Given o usuário está na página de cadastro + When ele preenche o campo de nome com "Demóstenes Silva" + And ele preenche o campo de CPF com "123.456.789-00" + And ele preenche o campo de email com "demostenes@example.com" + And ele preenche o campo de senha com "SecurePassword123" + And ele preenche o campo de confirmar senha com "SecurePassword123" + And ele seleciona a opção "Não" para professor + And ele clica no botão "Criar" + Then ele deve ver uma mensagem de sucesso + And deve haver um botão para voltar à área de login + + Scenario: Cadastro como professor + Given o usuário está na página de cadastro + When ele preenche o campo de nome com "Prof. Demóstenes" + And ele preenche o campo de CPF com "987.654.321-00" + And ele preenche o campo de email com "prof.demostenes@example.com" + And ele preenche o campo de senha com "SecurePassword123" + And ele preenche o campo de confirmar senha com "SecurePassword123" + And ele seleciona a opção "Sim, sou professor" para professor + And ele preenche o campo SIAPE com "123456" + And ele clica no botão "Criar" + Then ele deve ver uma mensagem de sucesso + And deve haver um botão para voltar à área de login + + Scenario: Erro no cadastro com senhas diferentes + Given o usuário está na página de cadastro + When ele preenche o campo de nome com "Demóstenes Silva" + And ele preenche o campo de CPF com "123.456.789-00" + And ele preenche o campo de email com "demostenes@example.com" + And ele preenche o campo de senha com "SecurePassword123" + And ele preenche o campo de confirmar senha com "DifferentPassword456" + And ele seleciona a opção "Não" para professor + And ele clica no botão "Criar" + Then ele deve ver uma mensagem de erro "As senhas não coincidem." + + Scenario: Voltar para login a partir do cadastro + Given o usuário está na página de cadastro + When ele clica no link "Já possuo uma conta" + Then ele deve ser redirecionado para a página de login \ No newline at end of file diff --git a/cypress/e2e/features/common-step-definitions/cadastro.step.ts b/cypress/e2e/features/common-step-definitions/cadastro.step.ts new file mode 100644 index 00000000..196b6180 --- /dev/null +++ b/cypress/e2e/features/common-step-definitions/cadastro.step.ts @@ -0,0 +1,77 @@ +import { Given, When, Then } from "@badeball/cypress-cucumber-preprocessor"; + +// URL da página de cadastro +const cadastroUrl = "/cadastro"; +const loginUrl = "/"; + +// Elementos da página baseados no seu HTML +const nomeInput = 'input[type="text"][placeholder="Nome"]'; +const cpfInput = 'input[type="text"][placeholder="CPF"]'; +const emailInput = 'input[type="email"][placeholder="Email"]'; +const senhaInput = 'input[type="password"][placeholder="Senha"]'; +const confirmarSenhaInput = 'input[type="password"][placeholder="Confirmar senha"]'; +const professorSelect = 'select'; +const siapeInput = 'input[type="text"][placeholder="SIAPE"]'; +const criarButton = 'button:contains("Criar")'; +const errorMessageContainer = '.error-message'; +const successMessageContainer = 'div[style*="color: green"]'; +const voltarLoginButton = 'button:contains("Voltar à Área de Login")'; +const jaPossuoContaLink = 'span.link:contains("Já possuo uma conta")'; + +// Steps para cadastro +Given("o usuário está na página de cadastro", () => { + cy.visit(cadastroUrl); + cy.contains("Sistema de Agendamento e").should("be.visible"); +}); + +When("ele preenche o campo de nome com {string}", (nome: string) => { + cy.get(nomeInput).clear().type(nome); +}); + +When("ele preenche o campo de CPF com {string}", (cpf: string) => { + cy.get(cpfInput).clear().type(cpf); +}); + +When("ele preenche o campo de email com {string}", (email: string) => { + cy.get(emailInput).clear().type(email); +}); + +When("ele preenche o campo de senha com {string}", (senha: string) => { + cy.get(senhaInput).clear().type(senha); +}); + +When("ele preenche o campo de confirmar senha com {string}", (senha: string) => { + cy.get(confirmarSenhaInput).clear().type(senha); +}); + +When("ele seleciona a opção {string} para professor", (opcao: string) => { + cy.get(professorSelect).select(opcao); +}); + +When("ele preenche o campo SIAPE com {string}", (siape: string) => { + cy.get(siapeInput).clear().type(siape); +}); + +When("ele clica no botão {string}", (buttonText: string) => { + cy.contains("button", buttonText).click(); +}); + +When("ele clica no link {string}", (linkText: string) => { + cy.contains("span.link", linkText).click(); +}); + +Then("ele deve ver uma mensagem de sucesso", () => { + cy.get(successMessageContainer).should("be.visible"); +}); + +Then("deve haver um botão para voltar à área de login", () => { + cy.get(voltarLoginButton).should("be.visible"); +}); + +Then("ele deve ver uma mensagem de erro {string}", (message: string) => { + cy.get(errorMessageContainer).should("contain", message); +}); + +Then("ele deve ser redirecionado para a página de login", () => { + cy.url().should("eq", Cypress.config().baseUrl + loginUrl); +}); \ No newline at end of file diff --git a/cypress/e2e/features/common-step-definitions/login.step.ts b/cypress/e2e/features/common-step-definitions/login.step.ts new file mode 100644 index 00000000..8c0fc864 --- /dev/null +++ b/cypress/e2e/features/common-step-definitions/login.step.ts @@ -0,0 +1,62 @@ +import { Given, When, Then } from "@badeball/cypress-cucumber-preprocessor"; + +// URL da página de login e home +const loginUrl = "/"; +const homeUrl = "/home"; // Ajustar conforme o caminho real da sua aplicação +const cadastroUrl = "/cadastro"; + +// Elementos da página baseados no seu HTML +const emailInput = 'input[type="email"][placeholder="Email"]'; +const passwordInput = 'input[type="password"][placeholder="Senha"]'; +const loginButton = 'button[type="submit"]'; +const errorMessageContainer = '.error-message'; // Ajuste para o seletor real do seu componente ErrorMessage +const naoCadastradoLink = 'span.link:contains("Não tem conta ainda?")'; +const userInfoElement = '[data-testid="user-info"]'; // Ajuste para o seletor real onde o email do usuário é exibido + +// Steps para login +Given("o usuário está na página de login", () => { + cy.visit(loginUrl); + cy.contains("Sistema de Agendamento e").should("be.visible"); +}); + +When("ele preenche o campo de email com {string}", (email: string) => { + cy.get(emailInput).clear().type(email); +}); + +When("ele preenche o campo de senha com {string}", (password: string) => { + cy.get(passwordInput).clear().type(password); +}); + +When("ele clica no botão {string}", (buttonText: string) => { + cy.get(loginButton).contains(buttonText).click(); +}); + +When("ele clica no botão {string} sem preencher os campos", (buttonText: string) => { + cy.get(emailInput).clear(); + cy.get(passwordInput).clear(); + cy.get(loginButton).contains(buttonText).click(); +}); + +When("ele clica no link {string}", (linkText: string) => { + cy.contains("span.link", linkText).click(); +}); + +Then("ele deve ser redirecionado para a página principal", () => { + cy.url().should("include", homeUrl); +}); + +Then("ele deve ver seu email {string} na tela", (email: string) => { + cy.get(userInfoElement).should("contain", email); +}); + +Then("ele deve ver uma mensagem de erro {string}", (message: string) => { + cy.get(errorMessageContainer).should("contain", message); +}); + +Then("ele deve permanecer na página de login", () => { + cy.url().should("eq", Cypress.config().baseUrl + loginUrl); +}); + +Then("ele deve ser redirecionado para a página de cadastro", () => { + cy.url().should("include", cadastroUrl); +}); \ No newline at end of file diff --git a/cypress/e2e/features/login.feature b/cypress/e2e/features/login.feature new file mode 100644 index 00000000..fcf0c2bb --- /dev/null +++ b/cypress/e2e/features/login.feature @@ -0,0 +1,28 @@ +Feature: Login de usuário no frontend + + Scenario: Login bem-sucedido + Given o usuário está na página de login + When ele preenche o campo de email com "demostenes@example.com" + And ele preenche o campo de senha com "SecurePassword123" + And ele clica no botão "Entrar" + Then ele deve ser redirecionado para a página principal + And ele deve ver seu email "demostenes@example.com" na tela + + Scenario: Login falha com senha incorreta + Given o usuário está na página de login + When ele preenche o campo de email com "demostenes@example.com" + And ele preenche o campo de senha com "SecureIncorreta123" + And ele clica no botão "Entrar" + Then ele deve ver uma mensagem de erro "Usuário ou senha inválidos." + And ele deve permanecer na página de login + + Scenario: Login falha com campos vazios + Given o usuário está na página de login + When ele clica no botão "Entrar" sem preencher os campos + Then ele deve ver uma mensagem de erro "Usuário e senha são obrigatórios." + And ele deve permanecer na página de login + + Scenario: Navegar para a página de cadastro + Given o usuário está na página de login + When ele clica no link "Não tem conta ainda?" + Then ele deve ser redirecionado para a página de cadastro \ No newline at end of file diff --git a/cypress/e2e/features/tests/ReservaSalas.feature b/cypress/e2e/features/tests/ReservaSalas.feature new file mode 100644 index 00000000..eab391ce --- /dev/null +++ b/cypress/e2e/features/tests/ReservaSalas.feature @@ -0,0 +1,14 @@ +SCENARIO: Visualizar salas de reunião disponíveis em 15/01 das 14h às 15h com ar condicionado + GIVEN eu estou logado como "professor" na página “Reservar" + AND no campo “tipo de sala” está selecionado “reunião" + AND no campo “data” está inserido “15/01/2025” + AND no campo “Hora Início” está selecionada a opção “14:00" + AND no campo “Hora Fim” está selecionada a opção “15:00" + AND no campo “Equipamentos” está selecionada a opção “Ar-condicionado" + WHEN eu seleciono a opção “Procurar" + THEN é exibida a sala "E001" como sala disponível e "bem" avaliada + THEN o user "aroldo@mail.com" volta á página de "login". + + +SCENARIO: Sucesso ao reservar uma sala disponível + GIVEN \ No newline at end of file diff --git a/docs/cenarios.rb b/docs/cenarios.rb new file mode 100644 index 00000000..1ff2741b --- /dev/null +++ b/docs/cenarios.rb @@ -0,0 +1,86 @@ +FEATURE: Cadastrar e fazer a manutenção dos usuários + AS a usuário do SAGAA + I WANT TO cadastrar-me no novo sistema + SO THAT consiga acessar a plataforma + + +SCENARIO: Sucesso no cadastro de usuário. + GIVEN eu estou na página de "Cadastro"; + WHEN eu preencho o campo "Nome" com "Demosténes" + AND "CPF" com "123.456.789-00" + AND "Email" com "demostenes@example.com" + AND "Você é professor?" com "Não" + AND "Senha" com "SecurePassword123" + AND "Confirmar Senha" com "SecurePassword123"; + AND clico em "Cadastrar"; + THEN uma mensagem "Cadastro criado com sucesso!" deve ser exibida. + + +SCENARIO: Sucesso no cadastro de professor. + GIVEN eu estou na página de "Cadastro"; + WHEN eu preencho o campo "Nome" com "Paula" + AND "CPF" com "321.879.789-33" + AND "Email" com "vanessa@example.com" + AND "Você é professor?" com "Sim" + AND "SIAP" com "101010" + AND "Senha" com "12345678" + AND "Confirmar Senha" com "12345678"; + AND clico em "Cadastrar"; + THEN uma mensagem "Cadastro criado com sucesso!" deve ser exibida. + + + + +SCENARIO: Fracasso no cadastro por campos obrigatórios não preenchidos + GIVEN eu estou na página de "Cadastro de usuário"; + WHEN eu preencho o campo "Nome" + AND "CPF" + AND "Email" + AND "Você é professor?" com "Não" + AND "Senha" + AND deixo o campo "Confirmar Senha" vazio; + AND clico em "Cadastrar" + THEN uma sinalização informando que o campo "Confirmar Senha" é obrigatório deve ser exibida. + + +SCENARIO: Fracasso no cadastro por duplicação de ID única. + GIVEN eu estou na página de "Cadastro de usuário"; + WHEN eu preencho o campo "Nome" com "Carlos Mendes" + AND "CPF" com "456.123.789-10" + AND "Email" com "carlos.mendes@example.com" + AND "Você é professor?" com "Sim" + AND "SIAP" com "010101" + AND "Senha" com "Password456" + AND "Confirmar Senha" com "Password456"; + AND clico em "Cadastrar"; + THEN uma mensagem de erro "Erro: email/cpf/siap já está registrado." deve ser exibida. + + +SCENARIO: Fracasso no cadastro por senhas que não coincidem + GIVEN eu estou na página de "Cadastro de usuário"; + WHEN eu preencho o campo "Nome" com "Beatriz" + AND "CPF" com "789.456.123-00" + AND "Email" com "Beatriz.oliveira@example.com" + AND "Você é professor?" com "Não" + AND "Senha" com "MyPassword123" + AND "Confirmar Senha" com "DifferentPassword123"; + AND clico em "Cadastrar"; + THEN uma mensagem de erro "As senhas não coincidem." deve ser exibida. + + +# Login +SCENARIO: Sucesso no login +GIVEN eu estou na página de "Login"; +WHEN eu preencho o campo "Email" com "demostenes@example.com" +AND o campo "Senha" com "SecurePassword123"; +AND clico em "Entrar"; +THEN eu devo ser redirecionado para a página "reserva" + + +SCENARIO: Fracasso no login +GIVEN eu estou na página de "Login"; +WHEN eu preencho o campo "Email" com "demostenes@example.com" +AND o campo "Senha" com "SenhaIncorreta123"; +AND clico em "Entrar"; +THEN uma sinalização de erro "Usuário ou senha incorretos." deve ser exibida. + diff --git a/instance/users.db b/instance/users.db new file mode 100644 index 00000000..80b9c654 Binary files /dev/null and b/instance/users.db differ diff --git a/package-lock.json b/package-lock.json index 5af242dc..02b50f05 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,6 @@ }, "devDependencies": { "@badeball/cypress-cucumber-preprocessor": "^18.0.1", - "@bahmutov/cypress-code-coverage": "^2.6.1", "@bahmutov/cypress-esbuild-preprocessor": "^2.2.0", "@cucumber/cucumber": "^9.2.0", "@cypress/code-coverage": "^3.10.7", @@ -48,34 +47,6 @@ "vitest": "^0.31.1" } }, - "node_modules/@actions/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz", - "integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==", - "dev": true, - "dependencies": { - "@actions/http-client": "^2.0.1", - "uuid": "^8.3.2" - } - }, - "node_modules/@actions/core/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/@actions/http-client": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.0.tgz", - "integrity": "sha512-BonhODnXr3amchh4qkmjPMUO8mFi/zLaaCeCAJZqch8iQqyDnVIkySjB38VHAC8IJ+bnlgfOqlhpyCUZHlQsqw==", - "dev": true, - "dependencies": { - "tunnel": "^0.0.6" - } - }, "node_modules/@adobe/css-tools": { "version": "4.2.0", "dev": true, @@ -167,6 +138,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/types": "^7.22.5" }, @@ -178,6 +150,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/types": "^7.22.5" }, @@ -215,6 +188,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.5", @@ -237,6 +211,7 @@ "version": "6.3.0", "dev": true, "license": "ISC", + "peer": true, "bin": { "semver": "bin/semver.js" } @@ -245,6 +220,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "regexpu-core": "^5.3.1", @@ -261,6 +237,7 @@ "version": "6.3.0", "dev": true, "license": "ISC", + "peer": true, "bin": { "semver": "bin/semver.js" } @@ -269,6 +246,7 @@ "version": "0.4.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-compilation-targets": "^7.17.7", "@babel/helper-plugin-utils": "^7.16.7", @@ -285,6 +263,7 @@ "version": "6.3.0", "dev": true, "license": "ISC", + "peer": true, "bin": { "semver": "bin/semver.js" } @@ -324,6 +303,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/types": "^7.22.5" }, @@ -364,6 +344,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/types": "^7.22.5" }, @@ -383,6 +364,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.5", @@ -400,6 +382,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.5", "@babel/helper-member-expression-to-functions": "^7.22.5", @@ -427,6 +410,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/types": "^7.22.5" }, @@ -473,6 +457,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-function-name": "^7.22.5", "@babel/template": "^7.22.5", @@ -580,6 +565,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -594,6 +580,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", @@ -610,6 +597,8 @@ "version": "7.18.6", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -625,6 +614,8 @@ "version": "7.20.7", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/compat-data": "^7.20.5", "@babel/helper-compilation-targets": "^7.20.7", @@ -643,6 +634,7 @@ "version": "7.21.0-placeholder-for-preset-env.2", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6.9.0" }, @@ -654,6 +646,7 @@ "version": "7.18.6", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -669,6 +662,7 @@ "version": "7.8.4", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -680,6 +674,7 @@ "version": "7.12.13", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.12.13" }, @@ -691,6 +686,7 @@ "version": "7.14.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -705,6 +701,7 @@ "version": "7.8.3", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -716,6 +713,7 @@ "version": "7.8.3", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.3" }, @@ -727,6 +725,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -741,6 +740,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -755,6 +755,7 @@ "version": "7.10.4", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -766,6 +767,7 @@ "version": "7.8.3", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -777,6 +779,8 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -791,6 +795,7 @@ "version": "7.10.4", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -802,6 +807,7 @@ "version": "7.8.3", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -813,6 +819,7 @@ "version": "7.10.4", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -824,6 +831,7 @@ "version": "7.8.3", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -835,6 +843,7 @@ "version": "7.8.3", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -846,6 +855,7 @@ "version": "7.8.3", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -857,6 +867,7 @@ "version": "7.14.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -871,6 +882,7 @@ "version": "7.14.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -885,6 +897,7 @@ "version": "7.18.6", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -900,6 +913,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -914,6 +928,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5", @@ -931,6 +946,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-module-imports": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5", @@ -947,6 +963,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -961,6 +978,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -975,6 +993,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5" @@ -990,6 +1009,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5", @@ -1006,6 +1026,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-compilation-targets": "^7.22.5", @@ -1028,6 +1049,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/template": "^7.22.5" @@ -1043,6 +1065,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1057,6 +1080,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5" @@ -1072,6 +1096,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1086,6 +1111,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3" @@ -1101,6 +1127,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5" @@ -1116,6 +1143,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" @@ -1131,6 +1159,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1145,6 +1174,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-compilation-targets": "^7.22.5", "@babel/helper-function-name": "^7.22.5", @@ -1161,6 +1191,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-json-strings": "^7.8.3" @@ -1176,6 +1207,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1190,6 +1222,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" @@ -1205,6 +1238,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1219,6 +1253,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-module-transforms": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5" @@ -1234,6 +1269,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-module-transforms": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5", @@ -1250,6 +1286,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-module-transforms": "^7.22.5", @@ -1267,6 +1304,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-module-transforms": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5" @@ -1282,6 +1320,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5" @@ -1297,6 +1336,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1311,6 +1351,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" @@ -1326,6 +1367,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-numeric-separator": "^7.10.4" @@ -1341,6 +1383,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/compat-data": "^7.22.5", "@babel/helper-compilation-targets": "^7.22.5", @@ -1359,6 +1402,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-replace-supers": "^7.22.5" @@ -1374,6 +1418,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" @@ -1389,6 +1434,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", @@ -1405,6 +1451,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1419,6 +1466,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5" @@ -1434,6 +1482,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-create-class-features-plugin": "^7.22.5", @@ -1451,6 +1500,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1465,6 +1515,8 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1479,6 +1531,8 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-module-imports": "^7.22.5", @@ -1497,6 +1551,8 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/plugin-transform-react-jsx": "^7.22.5" }, @@ -1539,6 +1595,8 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5" @@ -1554,6 +1612,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "regenerator-transform": "^0.15.1" @@ -1569,6 +1628,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1583,6 +1643,8 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-module-imports": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5", @@ -1602,6 +1664,8 @@ "version": "6.3.0", "dev": true, "license": "ISC", + "optional": true, + "peer": true, "bin": { "semver": "bin/semver.js" } @@ -1610,6 +1674,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1624,6 +1689,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" @@ -1639,6 +1705,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1653,6 +1720,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1667,6 +1735,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1681,6 +1750,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1695,6 +1765,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5" @@ -1710,6 +1781,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5" @@ -1725,6 +1797,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5" @@ -1740,6 +1813,7 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/compat-data": "^7.22.5", "@babel/helper-compilation-targets": "^7.22.5", @@ -1833,6 +1907,7 @@ "version": "6.3.0", "dev": true, "license": "ISC", + "peer": true, "bin": { "semver": "bin/semver.js" } @@ -1841,6 +1916,7 @@ "version": "0.1.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", @@ -1856,6 +1932,8 @@ "version": "7.22.5", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-validator-option": "^7.22.5", @@ -1874,7 +1952,8 @@ "node_modules/@babel/regjsgen": { "version": "0.8.0", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@babel/runtime": { "version": "7.22.5", @@ -2202,161 +2281,6 @@ "node": "*" } }, - "node_modules/@bahmutov/cypress-code-coverage": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@bahmutov/cypress-code-coverage/-/cypress-code-coverage-2.6.1.tgz", - "integrity": "sha512-oK6RP+537hcIB90QdI4m/u95Pr7YYgbD4PwoDJr924t6viiN8hSc1f3BbD5D8Xuq9JagXLes68l7+lR0cBI9Hg==", - "dev": true, - "dependencies": { - "@actions/core": "^1.10.0", - "@cypress/browserify-preprocessor": "3.0.2", - "chalk": "4.1.2", - "console.table": "^0.10.0", - "dayjs": "1.10.7", - "debug": "4.3.3", - "execa": "4.1.0", - "globby": "11.1.0", - "istanbul-lib-coverage": "3.0.0", - "js-yaml": "3.14.1", - "nyc": "15.1.0", - "rimraf": "^4.4.1", - "sort-array": "^4.1.5" - }, - "bin": { - "cc-merge": "bin/cc-merge.js" - }, - "peerDependencies": { - "cypress": ">=10.0.0" - } - }, - "node_modules/@bahmutov/cypress-code-coverage/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@bahmutov/cypress-code-coverage/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@bahmutov/cypress-code-coverage/node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@bahmutov/cypress-code-coverage/node_modules/glob": { - "version": "9.3.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", - "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "minimatch": "^8.0.2", - "minipass": "^4.2.4", - "path-scurry": "^1.6.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@bahmutov/cypress-code-coverage/node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@bahmutov/cypress-code-coverage/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@bahmutov/cypress-code-coverage/node_modules/minimatch": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz", - "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@bahmutov/cypress-code-coverage/node_modules/minipass": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", - "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@bahmutov/cypress-code-coverage/node_modules/rimraf": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.1.tgz", - "integrity": "sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==", - "dev": true, - "dependencies": { - "glob": "^9.2.0" - }, - "bin": { - "rimraf": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@bahmutov/cypress-esbuild-preprocessor": { "version": "2.2.0", "dev": true, @@ -2590,6 +2514,8 @@ "version": "3.0.2", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@babel/core": "^7.16.0", "@babel/plugin-proposal-class-properties": "^7.16.0", @@ -4054,6 +3980,8 @@ "version": "1.8.2", "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "acorn": "^7.0.0", "acorn-walk": "^7.0.0", @@ -4064,6 +3992,8 @@ "version": "7.4.1", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4075,6 +4005,8 @@ "version": "7.2.0", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.4.0" } @@ -4279,15 +4211,6 @@ "dequal": "^2.0.3" } }, - "node_modules/array-back": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-5.0.0.tgz", - "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/array-buffer-byte-length": { "version": "1.0.0", "dev": true, @@ -4320,6 +4243,8 @@ "version": "5.4.1", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "bn.js": "^4.0.0", "inherits": "^2.0.1", @@ -4330,12 +4255,16 @@ "node_modules/asn1.js/node_modules/bn.js": { "version": "4.12.0", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/assert": { "version": "1.5.0", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "object-assign": "^4.1.1", "util": "0.10.3" @@ -4352,12 +4281,16 @@ "node_modules/assert/node_modules/inherits": { "version": "2.0.1", "dev": true, - "license": "ISC" - }, - "node_modules/assert/node_modules/util": { + "license": "ISC", + "optional": true, + "peer": true + }, + "node_modules/assert/node_modules/util": { "version": "0.10.3", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "inherits": "2.0.1" } @@ -4458,12 +4391,15 @@ "node_modules/babel-plugin-add-module-exports": { "version": "1.0.4", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/babel-plugin-polyfill-corejs2": { "version": "0.4.3", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/compat-data": "^7.17.7", "@babel/helper-define-polyfill-provider": "^0.4.0", @@ -4477,6 +4413,7 @@ "version": "6.3.0", "dev": true, "license": "ISC", + "peer": true, "bin": { "semver": "bin/semver.js" } @@ -4485,6 +4422,7 @@ "version": "0.8.1", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-define-polyfill-provider": "^0.4.0", "core-js-compat": "^3.30.1" @@ -4497,6 +4435,7 @@ "version": "0.5.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-define-polyfill-provider": "^0.4.0" }, @@ -4508,6 +4447,8 @@ "version": "10.0.0", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=6.9.0" }, @@ -4573,7 +4514,9 @@ "node_modules/bn.js": { "version": "5.2.1", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/brace-expansion": { "version": "1.1.11", @@ -4598,12 +4541,16 @@ "node_modules/brorand": { "version": "1.1.0", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/browser-pack": { "version": "6.1.0", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "combine-source-map": "~0.8.0", "defined": "^1.0.0", @@ -4620,6 +4567,8 @@ "version": "2.0.0", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "resolve": "^1.17.0" } @@ -4633,6 +4582,8 @@ "version": "16.5.2", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "assert": "^1.4.0", "browser-pack": "^6.0.1", @@ -4694,6 +4645,8 @@ "version": "1.2.0", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "buffer-xor": "^1.0.3", "cipher-base": "^1.0.0", @@ -4707,6 +4660,8 @@ "version": "1.0.1", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "browserify-aes": "^1.0.4", "browserify-des": "^1.0.0", @@ -4717,6 +4672,8 @@ "version": "1.0.2", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "cipher-base": "^1.0.1", "des.js": "^1.0.0", @@ -4728,6 +4685,8 @@ "version": "4.1.0", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "bn.js": "^5.0.0", "randombytes": "^2.0.1" @@ -4737,6 +4696,8 @@ "version": "4.2.1", "dev": true, "license": "ISC", + "optional": true, + "peer": true, "dependencies": { "bn.js": "^5.1.1", "browserify-rsa": "^4.0.1", @@ -4753,6 +4714,8 @@ "version": "3.6.2", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -4766,6 +4729,8 @@ "version": "0.2.0", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "pako": "~1.0.5" } @@ -4805,6 +4770,8 @@ "version": "5.2.1", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "base64-js": "^1.0.2", "ieee754": "^1.1.4" @@ -4826,12 +4793,16 @@ "node_modules/buffer-xor": { "version": "1.0.3", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/builtin-status-codes": { "version": "3.0.0", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/c8": { "version": "7.14.0", @@ -4877,7 +4848,9 @@ "node_modules/cached-path-relative": { "version": "1.1.0", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/cachedir": { "version": "2.3.0", @@ -5026,6 +4999,8 @@ "version": "3.5.2", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -5069,6 +5044,8 @@ "version": "1.0.4", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -5155,20 +5132,12 @@ "wrap-ansi": "^7.0.0" } }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.8" - } - }, "node_modules/coffeeify": { "version": "3.0.1", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "convert-source-map": "^1.3.0", "through2": "^2.0.0" @@ -5181,6 +5150,8 @@ "version": "1.12.7", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "bin": { "cake": "bin/cake", "coffee": "bin/coffee" @@ -5214,6 +5185,8 @@ "version": "0.8.0", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "convert-source-map": "~1.1.0", "inline-source-map": "~0.6.0", @@ -5224,7 +5197,9 @@ "node_modules/combine-source-map/node_modules/convert-source-map": { "version": "1.1.3", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/combined-stream": { "version": "1.0.8", @@ -5274,6 +5249,8 @@ "node >= 0.8" ], "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", @@ -5301,24 +5278,16 @@ }, "node_modules/console-browserify": { "version": "1.2.0", - "dev": true - }, - "node_modules/console.table": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/console.table/-/console.table-0.10.0.tgz", - "integrity": "sha512-dPyZofqggxuvSf7WXvNjuRfnsOk1YazkVP8FdxH4tcH2c37wc79/Yl6Bhr7Lsu00KMgy2ql/qCMuNu8xctZM8g==", "dev": true, - "dependencies": { - "easy-table": "1.1.0" - }, - "engines": { - "node": "> 0.10" - } + "optional": true, + "peer": true }, "node_modules/constants-browserify": { "version": "1.0.0", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/convert-source-map": { "version": "1.9.0", @@ -5329,6 +5298,7 @@ "version": "3.31.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "browserslist": "^4.21.5" }, @@ -5340,12 +5310,16 @@ "node_modules/core-util-is": { "version": "1.0.3", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/create-ecdh": { "version": "4.0.4", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "bn.js": "^4.1.0", "elliptic": "^6.5.3" @@ -5354,12 +5328,16 @@ "node_modules/create-ecdh/node_modules/bn.js": { "version": "4.12.0", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/create-hash": { "version": "1.2.0", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "cipher-base": "^1.0.1", "inherits": "^2.0.1", @@ -5372,6 +5350,8 @@ "version": "1.1.7", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "cipher-base": "^1.0.3", "create-hash": "^1.1.0", @@ -5398,6 +5378,8 @@ "version": "3.12.0", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "browserify-cipher": "^1.0.0", "browserify-sign": "^4.0.0", @@ -5536,7 +5518,9 @@ "node_modules/dash-ast": { "version": "1.0.0", "dev": true, - "license": "Apache-2.0" + "license": "Apache-2.0", + "optional": true, + "peer": true }, "node_modules/dashdash": { "version": "1.14.1", @@ -5670,19 +5654,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, - "optional": true, - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/define-properties": { "version": "1.2.0", "dev": true, @@ -5702,6 +5673,8 @@ "version": "1.0.1", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -5717,6 +5690,8 @@ "version": "2.0.1", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "JSONStream": "^1.0.3", "shasum-object": "^1.0.0", @@ -5739,6 +5714,8 @@ "version": "1.1.0", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "inherits": "^2.0.1", "minimalistic-assert": "^1.0.0" @@ -5748,6 +5725,8 @@ "version": "5.2.1", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "acorn-node": "^1.8.2", "defined": "^1.0.0", @@ -5780,6 +5759,8 @@ "version": "5.0.3", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "bn.js": "^4.1.0", "miller-rabin": "^4.0.0", @@ -5789,7 +5770,9 @@ "node_modules/diffie-hellman/node_modules/bn.js": { "version": "4.12.0", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/dir-glob": { "version": "3.0.1", @@ -5822,6 +5805,8 @@ "version": "1.2.0", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.4", "npm": ">=1.2" @@ -5847,6 +5832,8 @@ "version": "0.1.4", "dev": true, "license": "BSD-3-Clause", + "optional": true, + "peer": true, "dependencies": { "readable-stream": "^2.0.2" } @@ -5856,15 +5843,6 @@ "dev": true, "license": "MIT" }, - "node_modules/easy-table": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.1.0.tgz", - "integrity": "sha512-oq33hWOSSnl2Hoh00tZWaIPi1ievrD9aFG82/IgjlycAnW9hHx5PkJiXpxPsgEE+H7BsbVQXFVFST8TEXS6/pA==", - "dev": true, - "optionalDependencies": { - "wcwidth": ">=1.0.1" - } - }, "node_modules/ecc-jsbn": { "version": "0.1.2", "dev": true, @@ -5883,6 +5861,8 @@ "version": "6.5.4", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "bn.js": "^4.11.9", "brorand": "^1.1.0", @@ -5896,7 +5876,9 @@ "node_modules/elliptic/node_modules/bn.js": { "version": "4.12.0", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/emoji-regex": { "version": "8.0.0", @@ -6336,6 +6318,8 @@ "version": "2.1.0", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.4.x" } @@ -6344,6 +6328,8 @@ "version": "1.0.3", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "md5.js": "^1.3.4", "safe-buffer": "^5.1.1" @@ -6467,7 +6453,9 @@ "node_modules/fast-safe-stringify": { "version": "2.1.1", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/fastq": { "version": "1.15.0", @@ -6714,7 +6702,9 @@ "node_modules/get-assigned-identifiers": { "version": "1.2.0", "dev": true, - "license": "Apache-2.0" + "license": "Apache-2.0", + "optional": true, + "peer": true }, "node_modules/get-caller-file": { "version": "2.0.5", @@ -6976,6 +6966,8 @@ "version": "3.1.0", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "inherits": "^2.0.4", "readable-stream": "^3.6.0", @@ -6989,6 +6981,8 @@ "version": "3.6.2", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -7002,6 +6996,8 @@ "version": "1.1.7", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "inherits": "^2.0.3", "minimalistic-assert": "^1.0.1" @@ -7042,6 +7038,8 @@ "version": "1.0.1", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "hash.js": "^1.0.3", "minimalistic-assert": "^1.0.0", @@ -7068,6 +7066,8 @@ "version": "1.1.1", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.10" } @@ -7101,7 +7101,9 @@ "node_modules/https-browserify": { "version": "1.0.0", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/https-proxy-agent": { "version": "5.0.1", @@ -7232,6 +7234,8 @@ "version": "0.6.2", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "source-map": "~0.5.3" } @@ -7240,6 +7244,8 @@ "version": "7.2.1", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "acorn-node": "^1.5.2", "combine-source-map": "^0.8.0", @@ -7342,7 +7348,9 @@ "node_modules/is-buffer": { "version": "1.1.6", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/is-callable": { "version": "1.2.7", @@ -7370,6 +7378,7 @@ "version": "2.12.1", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "has": "^1.0.3" }, @@ -7411,6 +7420,8 @@ "version": "1.0.10", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -7636,7 +7647,9 @@ "node_modules/isarray": { "version": "1.0.0", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/isexe": { "version": "2.0.0", @@ -8119,6 +8132,8 @@ "version": "0.0.1", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "jsonify": "~0.0.0" } @@ -8164,6 +8179,8 @@ "version": "0.0.1", "dev": true, "license": "Public Domain", + "optional": true, + "peer": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -8174,12 +8191,16 @@ "engines": [ "node >= 0.2.0" ], - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/JSONStream": { "version": "1.3.5", "dev": true, "license": "(MIT OR Apache-2.0)", + "optional": true, + "peer": true, "dependencies": { "jsonparse": "^1.2.0", "through": ">=2.2.7 <3" @@ -8235,6 +8256,8 @@ "version": "2.0.2", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "inherits": "^2.0.1", "stream-splicer": "^2.0.0" @@ -8338,12 +8361,15 @@ "node_modules/lodash.clonedeep": { "version": "4.5.0", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/lodash.debounce": { "version": "4.0.8", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/lodash.flattendeep": { "version": "4.4.0", @@ -8353,7 +8379,9 @@ "node_modules/lodash.memoize": { "version": "3.0.4", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/lodash.merge": { "version": "4.6.2", @@ -8533,6 +8561,8 @@ "version": "1.3.5", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1", @@ -8568,6 +8598,8 @@ "version": "4.0.1", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "bn.js": "^4.0.0", "brorand": "^1.0.1" @@ -8579,7 +8611,9 @@ "node_modules/miller-rabin/node_modules/bn.js": { "version": "4.12.0", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/mime-db": { "version": "1.52.0", @@ -8617,12 +8651,16 @@ "node_modules/minimalistic-assert": { "version": "1.0.1", "dev": true, - "license": "ISC" + "license": "ISC", + "optional": true, + "peer": true }, "node_modules/minimalistic-crypto-utils": { "version": "1.0.1", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/minimatch": { "version": "3.1.2", @@ -8668,7 +8706,9 @@ "node_modules/mkdirp-classic": { "version": "0.5.3", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/mlly": { "version": "1.4.0", @@ -8858,6 +8898,8 @@ "version": "6.2.3", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "browser-resolve": "^2.0.0", "cached-path-relative": "^1.0.2", @@ -9254,7 +9296,9 @@ "node_modules/os-browserify": { "version": "0.3.0", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/ospath": { "version": "1.2.2", @@ -9265,6 +9309,8 @@ "version": "1.1.1", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "shell-quote": "^1.4.2" } @@ -9347,7 +9393,9 @@ "node_modules/pako": { "version": "1.0.11", "dev": true, - "license": "(MIT AND Zlib)" + "license": "(MIT AND Zlib)", + "optional": true, + "peer": true }, "node_modules/parent-module": { "version": "1.0.1", @@ -9364,6 +9412,8 @@ "version": "1.0.1", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "path-platform": "~0.11.15" } @@ -9372,6 +9422,8 @@ "version": "5.1.6", "dev": true, "license": "ISC", + "optional": true, + "peer": true, "dependencies": { "asn1.js": "^5.2.0", "browserify-aes": "^1.0.0", @@ -9394,7 +9446,9 @@ "node_modules/path-browserify": { "version": "0.0.1", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/path-exists": { "version": "4.0.0", @@ -9423,12 +9477,15 @@ "node_modules/path-parse": { "version": "1.0.7", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/path-platform": { "version": "0.11.15", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 0.8.0" } @@ -9492,6 +9549,8 @@ "version": "3.1.2", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "create-hash": "^1.1.2", "create-hmac": "^1.1.4", @@ -9688,6 +9747,8 @@ "version": "0.11.10", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">= 0.6.0" } @@ -9695,7 +9756,9 @@ "node_modules/process-nextick-args": { "version": "2.0.1", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/process-on-spawn": { "version": "1.0.0", @@ -9748,6 +9811,8 @@ "version": "4.0.3", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "bn.js": "^4.1.0", "browserify-rsa": "^4.0.0", @@ -9760,7 +9825,9 @@ "node_modules/public-encrypt/node_modules/bn.js": { "version": "4.12.0", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/pump": { "version": "3.0.0", @@ -9774,7 +9841,9 @@ "node_modules/punycode": { "version": "1.4.1", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/qs": { "version": "6.10.4", @@ -9793,6 +9862,8 @@ "node_modules/querystring-es3": { "version": "0.2.1", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=0.4.x" } @@ -9833,6 +9904,8 @@ "version": "1.0.4", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "randombytes": "^2.0.5", "safe-buffer": "^5.1.0" @@ -9918,6 +9991,8 @@ "version": "2.0.0", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "readable-stream": "^2.0.2" } @@ -9926,6 +10001,8 @@ "version": "2.3.8", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -9939,12 +10016,16 @@ "node_modules/readable-stream/node_modules/safe-buffer": { "version": "5.1.2", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/readable-stream/node_modules/string_decoder": { "version": "1.1.1", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "safe-buffer": "~5.1.0" } @@ -9980,12 +10061,14 @@ "node_modules/regenerate": { "version": "1.4.2", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/regenerate-unicode-properties": { "version": "10.1.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "regenerate": "^1.4.2" }, @@ -10002,6 +10085,7 @@ "version": "0.15.1", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.8.4" } @@ -10042,6 +10126,7 @@ "version": "5.3.2", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/regjsgen": "^0.8.0", "regenerate": "^1.4.2", @@ -10058,6 +10143,7 @@ "version": "0.9.1", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "jsesc": "~0.5.0" }, @@ -10068,6 +10154,7 @@ "node_modules/regjsparser/node_modules/jsesc": { "version": "0.5.0", "dev": true, + "peer": true, "bin": { "jsesc": "bin/jsesc" } @@ -10130,6 +10217,7 @@ "version": "1.22.2", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "is-core-module": "^2.11.0", "path-parse": "^1.0.7", @@ -10213,6 +10301,8 @@ "version": "2.0.2", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1" @@ -10421,6 +10511,8 @@ "version": "2.4.11", "dev": true, "license": "(MIT AND BSD-3-Clause)", + "optional": true, + "peer": true, "dependencies": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -10433,6 +10525,8 @@ "version": "1.0.2", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "json-stable-stringify": "~0.0.0", "sha.js": "~2.4.4" @@ -10442,6 +10536,8 @@ "version": "1.0.0", "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "fast-safe-stringify": "^2.0.7" } @@ -10469,6 +10565,8 @@ "version": "1.8.1", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -10513,7 +10611,9 @@ "url": "https://feross.org/support" } ], - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/slash": { "version": "3.0.0", @@ -10536,23 +10636,12 @@ "node": ">=8" } }, - "node_modules/sort-array": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/sort-array/-/sort-array-4.1.5.tgz", - "integrity": "sha512-Ya4peoS1fgFN42RN1REk2FgdNOeLIEMKFGJvs7VTP3OklF8+kl2SkpVliZ4tk/PurWsrWRsdNdU+tgyOBkB9sA==", - "dev": true, - "dependencies": { - "array-back": "^5.0.0", - "typical": "^6.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/source-map": { "version": "0.5.7", "dev": true, "license": "BSD-3-Clause", + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -10751,6 +10840,8 @@ "version": "2.0.2", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "inherits": "~2.0.1", "readable-stream": "^2.0.2" @@ -10768,6 +10859,8 @@ "version": "1.1.1", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "duplexer2": "~0.1.0", "readable-stream": "^2.0.2" @@ -10777,6 +10870,8 @@ "version": "3.2.0", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "builtin-status-codes": "^3.0.0", "inherits": "^2.0.4", @@ -10788,6 +10883,8 @@ "version": "3.6.2", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -10801,6 +10898,8 @@ "version": "2.0.1", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "inherits": "^2.0.1", "readable-stream": "^2.0.2" @@ -10810,6 +10909,8 @@ "version": "1.3.0", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -10941,6 +11042,8 @@ "version": "1.0.0", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "minimist": "^1.1.0" } @@ -10963,6 +11066,7 @@ "version": "1.0.0", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4" }, @@ -10979,6 +11083,8 @@ "version": "1.4.0", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "acorn-node": "^1.2.0" } @@ -11119,6 +11225,8 @@ "version": "2.0.5", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" @@ -11135,6 +11243,8 @@ "node_modules/timers-browserify": { "version": "1.4.2", "dev": true, + "optional": true, + "peer": true, "dependencies": { "process": "~0.11.0" }, @@ -11272,16 +11382,9 @@ "node_modules/tty-browserify": { "version": "0.0.1", "dev": true, - "license": "MIT" - }, - "node_modules/tunnel": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", - "dev": true, - "engines": { - "node": ">=0.6.11 <=0.7.0 || >=0.7.3" - } + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/tunnel-agent": { "version": "0.6.0", @@ -11332,7 +11435,9 @@ "node_modules/typedarray": { "version": "0.0.6", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/typedarray-to-buffer": { "version": "3.1.5", @@ -11354,15 +11459,6 @@ "node": ">=14.17" } }, - "node_modules/typical": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/typical/-/typical-6.0.1.tgz", - "integrity": "sha512-+g3NEp7fJLe9DPa1TArHm9QAA7YciZmWnfAqEaFrBihQ7epOv9i99rjtgb6Iz0wh3WuQDjsCTDfgRoGnmHN81A==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/ufo": { "version": "1.1.2", "dev": true, @@ -11372,6 +11468,8 @@ "version": "3.0.3", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "bin": { "umd": "bin/cli.js" } @@ -11380,6 +11478,8 @@ "version": "1.1.3", "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "acorn-node": "^1.3.0", "dash-ast": "^1.0.0", @@ -11395,6 +11495,7 @@ "version": "2.0.0", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=4" } @@ -11403,6 +11504,7 @@ "version": "2.0.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "unicode-canonical-property-names-ecmascript": "^2.0.0", "unicode-property-aliases-ecmascript": "^2.0.0" @@ -11415,6 +11517,7 @@ "version": "2.1.0", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=4" } @@ -11423,6 +11526,7 @@ "version": "2.1.0", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=4" } @@ -11500,6 +11604,8 @@ "version": "0.11.1", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "punycode": "^1.4.1", "qs": "^6.11.0" @@ -11518,6 +11624,8 @@ "version": "6.11.2", "dev": true, "license": "BSD-3-Clause", + "optional": true, + "peer": true, "dependencies": { "side-channel": "^1.0.4" }, @@ -11532,6 +11640,8 @@ "version": "0.10.4", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "inherits": "2.0.3" } @@ -11544,12 +11654,16 @@ "node_modules/util-deprecate": { "version": "1.0.2", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/util/node_modules/inherits": { "version": "2.0.3", "dev": true, - "license": "ISC" + "license": "ISC", + "optional": true, + "peer": true }, "node_modules/uuid": { "version": "9.0.0", @@ -11843,7 +11957,9 @@ "node_modules/vm-browserify": { "version": "1.1.2", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/w3c-xmlserializer": { "version": "4.0.0", @@ -11887,6 +12003,8 @@ "version": "4.0.0", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "anymatch": "^3.1.0", "browserify": "^17.0.0", @@ -11907,6 +12025,8 @@ "version": "17.0.0", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "assert": "^1.4.0", "browser-pack": "^6.0.1", @@ -11968,6 +12088,8 @@ "version": "2.0.5", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" @@ -11977,6 +12099,8 @@ "version": "3.3.0", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.8.x" } @@ -11984,12 +12108,16 @@ "node_modules/watchify/node_modules/path-browserify": { "version": "1.0.1", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/watchify/node_modules/stream-browserify": { "version": "3.0.0", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "inherits": "~2.0.4", "readable-stream": "^3.5.0" @@ -11999,6 +12127,8 @@ "version": "3.6.2", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -12012,6 +12142,8 @@ "version": "4.0.2", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "readable-stream": "3" } @@ -12020,6 +12152,8 @@ "version": "3.6.2", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -12033,6 +12167,8 @@ "version": "0.12.5", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", @@ -12054,16 +12190,6 @@ "node": ">=10.13.0" } }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, - "optional": true, - "dependencies": { - "defaults": "^1.0.3" - } - }, "node_modules/webidl-conversions": { "version": "7.0.0", "dev": true, @@ -12383,6 +12509,8 @@ "version": "4.0.2", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.4" } @@ -12515,33 +12643,6 @@ } }, "dependencies": { - "@actions/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz", - "integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==", - "dev": true, - "requires": { - "@actions/http-client": "^2.0.1", - "uuid": "^8.3.2" - }, - "dependencies": { - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true - } - } - }, - "@actions/http-client": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.0.tgz", - "integrity": "sha512-BonhODnXr3amchh4qkmjPMUO8mFi/zLaaCeCAJZqch8iQqyDnVIkySjB38VHAC8IJ+bnlgfOqlhpyCUZHlQsqw==", - "dev": true, - "requires": { - "tunnel": "^0.0.6" - } - }, "@adobe/css-tools": { "version": "4.2.0", "dev": true @@ -12605,6 +12706,7 @@ "@babel/helper-annotate-as-pure": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/types": "^7.22.5" } @@ -12612,6 +12714,7 @@ "@babel/helper-builder-binary-assignment-operator-visitor": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/types": "^7.22.5" } @@ -12636,6 +12739,7 @@ "@babel/helper-create-class-features-plugin": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.5", @@ -12650,13 +12754,15 @@ "dependencies": { "semver": { "version": "6.3.0", - "dev": true + "dev": true, + "peer": true } } }, "@babel/helper-create-regexp-features-plugin": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-annotate-as-pure": "^7.22.5", "regexpu-core": "^5.3.1", @@ -12665,13 +12771,15 @@ "dependencies": { "semver": { "version": "6.3.0", - "dev": true + "dev": true, + "peer": true } } }, "@babel/helper-define-polyfill-provider": { "version": "0.4.0", "dev": true, + "peer": true, "requires": { "@babel/helper-compilation-targets": "^7.17.7", "@babel/helper-plugin-utils": "^7.16.7", @@ -12683,7 +12791,8 @@ "dependencies": { "semver": { "version": "6.3.0", - "dev": true + "dev": true, + "peer": true } } }, @@ -12709,6 +12818,7 @@ "@babel/helper-member-expression-to-functions": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/types": "^7.22.5" } @@ -12737,6 +12847,7 @@ "@babel/helper-optimise-call-expression": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/types": "^7.22.5" } @@ -12748,6 +12859,7 @@ "@babel/helper-remap-async-to-generator": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.5", @@ -12758,6 +12870,7 @@ "@babel/helper-replace-supers": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-environment-visitor": "^7.22.5", "@babel/helper-member-expression-to-functions": "^7.22.5", @@ -12777,6 +12890,7 @@ "@babel/helper-skip-transparent-expression-wrappers": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/types": "^7.22.5" } @@ -12803,6 +12917,7 @@ "@babel/helper-wrap-function": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-function-name": "^7.22.5", "@babel/template": "^7.22.5", @@ -12875,6 +12990,7 @@ "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" } @@ -12882,6 +12998,7 @@ "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", @@ -12891,6 +13008,8 @@ "@babel/plugin-proposal-class-properties": { "version": "7.18.6", "dev": true, + "optional": true, + "peer": true, "requires": { "@babel/helper-create-class-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -12899,6 +13018,8 @@ "@babel/plugin-proposal-object-rest-spread": { "version": "7.20.7", "dev": true, + "optional": true, + "peer": true, "requires": { "@babel/compat-data": "^7.20.5", "@babel/helper-compilation-targets": "^7.20.7", @@ -12910,11 +13031,13 @@ "@babel/plugin-proposal-private-property-in-object": { "version": "7.21.0-placeholder-for-preset-env.2", "dev": true, + "peer": true, "requires": {} }, "@babel/plugin-proposal-unicode-property-regex": { "version": "7.18.6", "dev": true, + "peer": true, "requires": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -12923,6 +13046,7 @@ "@babel/plugin-syntax-async-generators": { "version": "7.8.4", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" } @@ -12930,6 +13054,7 @@ "@babel/plugin-syntax-class-properties": { "version": "7.12.13", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.12.13" } @@ -12937,6 +13062,7 @@ "@babel/plugin-syntax-class-static-block": { "version": "7.14.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.14.5" } @@ -12944,6 +13070,7 @@ "@babel/plugin-syntax-dynamic-import": { "version": "7.8.3", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" } @@ -12951,6 +13078,7 @@ "@babel/plugin-syntax-export-namespace-from": { "version": "7.8.3", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.8.3" } @@ -12958,6 +13086,7 @@ "@babel/plugin-syntax-import-assertions": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" } @@ -12965,6 +13094,7 @@ "@babel/plugin-syntax-import-attributes": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" } @@ -12972,6 +13102,7 @@ "@babel/plugin-syntax-import-meta": { "version": "7.10.4", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } @@ -12979,6 +13110,7 @@ "@babel/plugin-syntax-json-strings": { "version": "7.8.3", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" } @@ -12986,6 +13118,8 @@ "@babel/plugin-syntax-jsx": { "version": "7.22.5", "dev": true, + "optional": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" } @@ -12993,6 +13127,7 @@ "@babel/plugin-syntax-logical-assignment-operators": { "version": "7.10.4", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } @@ -13000,6 +13135,7 @@ "@babel/plugin-syntax-nullish-coalescing-operator": { "version": "7.8.3", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" } @@ -13007,6 +13143,7 @@ "@babel/plugin-syntax-numeric-separator": { "version": "7.10.4", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } @@ -13014,6 +13151,7 @@ "@babel/plugin-syntax-object-rest-spread": { "version": "7.8.3", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" } @@ -13021,6 +13159,7 @@ "@babel/plugin-syntax-optional-catch-binding": { "version": "7.8.3", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" } @@ -13028,6 +13167,7 @@ "@babel/plugin-syntax-optional-chaining": { "version": "7.8.3", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" } @@ -13035,6 +13175,7 @@ "@babel/plugin-syntax-private-property-in-object": { "version": "7.14.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.14.5" } @@ -13042,6 +13183,7 @@ "@babel/plugin-syntax-top-level-await": { "version": "7.14.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.14.5" } @@ -13049,6 +13191,7 @@ "@babel/plugin-syntax-unicode-sets-regex": { "version": "7.18.6", "dev": true, + "peer": true, "requires": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -13057,6 +13200,7 @@ "@babel/plugin-transform-arrow-functions": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" } @@ -13064,6 +13208,7 @@ "@babel/plugin-transform-async-generator-functions": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-environment-visitor": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5", @@ -13074,6 +13219,7 @@ "@babel/plugin-transform-async-to-generator": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-module-imports": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5", @@ -13083,6 +13229,7 @@ "@babel/plugin-transform-block-scoped-functions": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" } @@ -13090,6 +13237,7 @@ "@babel/plugin-transform-block-scoping": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" } @@ -13097,6 +13245,7 @@ "@babel/plugin-transform-class-properties": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-create-class-features-plugin": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5" @@ -13105,6 +13254,7 @@ "@babel/plugin-transform-class-static-block": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-create-class-features-plugin": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5", @@ -13114,6 +13264,7 @@ "@babel/plugin-transform-classes": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-compilation-targets": "^7.22.5", @@ -13129,6 +13280,7 @@ "@babel/plugin-transform-computed-properties": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/template": "^7.22.5" @@ -13137,6 +13289,7 @@ "@babel/plugin-transform-destructuring": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" } @@ -13144,6 +13297,7 @@ "@babel/plugin-transform-dotall-regex": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-create-regexp-features-plugin": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5" @@ -13152,6 +13306,7 @@ "@babel/plugin-transform-duplicate-keys": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" } @@ -13159,6 +13314,7 @@ "@babel/plugin-transform-dynamic-import": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3" @@ -13167,6 +13323,7 @@ "@babel/plugin-transform-exponentiation-operator": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5" @@ -13175,6 +13332,7 @@ "@babel/plugin-transform-export-namespace-from": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" @@ -13183,6 +13341,7 @@ "@babel/plugin-transform-for-of": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" } @@ -13190,6 +13349,7 @@ "@babel/plugin-transform-function-name": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-compilation-targets": "^7.22.5", "@babel/helper-function-name": "^7.22.5", @@ -13199,6 +13359,7 @@ "@babel/plugin-transform-json-strings": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-json-strings": "^7.8.3" @@ -13207,6 +13368,7 @@ "@babel/plugin-transform-literals": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" } @@ -13214,6 +13376,7 @@ "@babel/plugin-transform-logical-assignment-operators": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" @@ -13222,6 +13385,7 @@ "@babel/plugin-transform-member-expression-literals": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" } @@ -13229,6 +13393,7 @@ "@babel/plugin-transform-modules-amd": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-module-transforms": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5" @@ -13237,6 +13402,7 @@ "@babel/plugin-transform-modules-commonjs": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-module-transforms": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5", @@ -13246,6 +13412,7 @@ "@babel/plugin-transform-modules-systemjs": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-module-transforms": "^7.22.5", @@ -13256,6 +13423,7 @@ "@babel/plugin-transform-modules-umd": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-module-transforms": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5" @@ -13264,6 +13432,7 @@ "@babel/plugin-transform-named-capturing-groups-regex": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-create-regexp-features-plugin": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5" @@ -13272,6 +13441,7 @@ "@babel/plugin-transform-new-target": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" } @@ -13279,6 +13449,7 @@ "@babel/plugin-transform-nullish-coalescing-operator": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" @@ -13287,6 +13458,7 @@ "@babel/plugin-transform-numeric-separator": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-numeric-separator": "^7.10.4" @@ -13295,6 +13467,7 @@ "@babel/plugin-transform-object-rest-spread": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/compat-data": "^7.22.5", "@babel/helper-compilation-targets": "^7.22.5", @@ -13306,6 +13479,7 @@ "@babel/plugin-transform-object-super": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-replace-supers": "^7.22.5" @@ -13314,6 +13488,7 @@ "@babel/plugin-transform-optional-catch-binding": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" @@ -13322,6 +13497,7 @@ "@babel/plugin-transform-optional-chaining": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", @@ -13331,6 +13507,7 @@ "@babel/plugin-transform-parameters": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" } @@ -13338,6 +13515,7 @@ "@babel/plugin-transform-private-methods": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-create-class-features-plugin": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5" @@ -13346,6 +13524,7 @@ "@babel/plugin-transform-private-property-in-object": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-create-class-features-plugin": "^7.22.5", @@ -13356,6 +13535,7 @@ "@babel/plugin-transform-property-literals": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" } @@ -13363,6 +13543,8 @@ "@babel/plugin-transform-react-display-name": { "version": "7.22.5", "dev": true, + "optional": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" } @@ -13370,6 +13552,8 @@ "@babel/plugin-transform-react-jsx": { "version": "7.22.5", "dev": true, + "optional": true, + "peer": true, "requires": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-module-imports": "^7.22.5", @@ -13381,6 +13565,8 @@ "@babel/plugin-transform-react-jsx-development": { "version": "7.22.5", "dev": true, + "optional": true, + "peer": true, "requires": { "@babel/plugin-transform-react-jsx": "^7.22.5" } @@ -13402,6 +13588,8 @@ "@babel/plugin-transform-react-pure-annotations": { "version": "7.22.5", "dev": true, + "optional": true, + "peer": true, "requires": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5" @@ -13410,6 +13598,7 @@ "@babel/plugin-transform-regenerator": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5", "regenerator-transform": "^0.15.1" @@ -13418,6 +13607,7 @@ "@babel/plugin-transform-reserved-words": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" } @@ -13425,6 +13615,8 @@ "@babel/plugin-transform-runtime": { "version": "7.22.5", "dev": true, + "optional": true, + "peer": true, "requires": { "@babel/helper-module-imports": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5", @@ -13436,13 +13628,16 @@ "dependencies": { "semver": { "version": "6.3.0", - "dev": true + "dev": true, + "optional": true, + "peer": true } } }, "@babel/plugin-transform-shorthand-properties": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" } @@ -13450,6 +13645,7 @@ "@babel/plugin-transform-spread": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" @@ -13458,6 +13654,7 @@ "@babel/plugin-transform-sticky-regex": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" } @@ -13465,6 +13662,7 @@ "@babel/plugin-transform-template-literals": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" } @@ -13472,6 +13670,7 @@ "@babel/plugin-transform-typeof-symbol": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" } @@ -13479,6 +13678,7 @@ "@babel/plugin-transform-unicode-escapes": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" } @@ -13486,6 +13686,7 @@ "@babel/plugin-transform-unicode-property-regex": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-create-regexp-features-plugin": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5" @@ -13494,6 +13695,7 @@ "@babel/plugin-transform-unicode-regex": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-create-regexp-features-plugin": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5" @@ -13502,6 +13704,7 @@ "@babel/plugin-transform-unicode-sets-regex": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/helper-create-regexp-features-plugin": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5" @@ -13510,6 +13713,7 @@ "@babel/preset-env": { "version": "7.22.5", "dev": true, + "peer": true, "requires": { "@babel/compat-data": "^7.22.5", "@babel/helper-compilation-targets": "^7.22.5", @@ -13595,13 +13799,15 @@ "dependencies": { "semver": { "version": "6.3.0", - "dev": true + "dev": true, + "peer": true } } }, "@babel/preset-modules": { "version": "0.1.5", "dev": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", @@ -13613,6 +13819,8 @@ "@babel/preset-react": { "version": "7.22.5", "dev": true, + "optional": true, + "peer": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-validator-option": "^7.22.5", @@ -13624,7 +13832,8 @@ }, "@babel/regjsgen": { "version": "0.8.0", - "dev": true + "dev": true, + "peer": true }, "@babel/runtime": { "version": "7.22.5", @@ -13841,116 +14050,6 @@ } } }, - "@bahmutov/cypress-code-coverage": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@bahmutov/cypress-code-coverage/-/cypress-code-coverage-2.6.1.tgz", - "integrity": "sha512-oK6RP+537hcIB90QdI4m/u95Pr7YYgbD4PwoDJr924t6viiN8hSc1f3BbD5D8Xuq9JagXLes68l7+lR0cBI9Hg==", - "dev": true, - "requires": { - "@actions/core": "^1.10.0", - "@cypress/browserify-preprocessor": "3.0.2", - "chalk": "4.1.2", - "console.table": "^0.10.0", - "dayjs": "1.10.7", - "debug": "4.3.3", - "execa": "4.1.0", - "globby": "11.1.0", - "istanbul-lib-coverage": "3.0.0", - "js-yaml": "3.14.1", - "nyc": "15.1.0", - "rimraf": "^4.4.1", - "sort-array": "^4.1.5" - }, - "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "glob": { - "version": "9.3.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", - "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "minimatch": "^8.0.2", - "minipass": "^4.2.4", - "path-scurry": "^1.6.1" - } - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "minimatch": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz", - "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - }, - "minipass": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", - "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", - "dev": true - }, - "rimraf": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.1.tgz", - "integrity": "sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==", - "dev": true, - "requires": { - "glob": "^9.2.0" - } - } - } - }, "@bahmutov/cypress-esbuild-preprocessor": { "version": "2.2.0", "dev": true, @@ -14126,6 +14225,8 @@ "@cypress/browserify-preprocessor": { "version": "3.0.2", "dev": true, + "optional": true, + "peer": true, "requires": { "@babel/core": "^7.16.0", "@babel/plugin-proposal-class-properties": "^7.16.0", @@ -15130,6 +15231,8 @@ "acorn-node": { "version": "1.8.2", "dev": true, + "optional": true, + "peer": true, "requires": { "acorn": "^7.0.0", "acorn-walk": "^7.0.0", @@ -15138,13 +15241,17 @@ "dependencies": { "acorn": { "version": "7.4.1", - "dev": true + "dev": true, + "optional": true, + "peer": true } } }, "acorn-walk": { "version": "7.2.0", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "agent-base": { "version": "6.0.2", @@ -15267,12 +15374,6 @@ "dequal": "^2.0.3" } }, - "array-back": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-5.0.0.tgz", - "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", - "dev": true - }, "array-buffer-byte-length": { "version": "1.0.0", "dev": true, @@ -15295,6 +15396,8 @@ "asn1.js": { "version": "5.4.1", "dev": true, + "optional": true, + "peer": true, "requires": { "bn.js": "^4.0.0", "inherits": "^2.0.1", @@ -15304,13 +15407,17 @@ "dependencies": { "bn.js": { "version": "4.12.0", - "dev": true + "dev": true, + "optional": true, + "peer": true } } }, "assert": { "version": "1.5.0", "dev": true, + "optional": true, + "peer": true, "requires": { "object-assign": "^4.1.1", "util": "0.10.3" @@ -15318,11 +15425,15 @@ "dependencies": { "inherits": { "version": "2.0.1", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "util": { "version": "0.10.3", "dev": true, + "optional": true, + "peer": true, "requires": { "inherits": "2.0.1" } @@ -15392,11 +15503,14 @@ }, "babel-plugin-add-module-exports": { "version": "1.0.4", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "babel-plugin-polyfill-corejs2": { "version": "0.4.3", "dev": true, + "peer": true, "requires": { "@babel/compat-data": "^7.17.7", "@babel/helper-define-polyfill-provider": "^0.4.0", @@ -15405,13 +15519,15 @@ "dependencies": { "semver": { "version": "6.3.0", - "dev": true + "dev": true, + "peer": true } } }, "babel-plugin-polyfill-corejs3": { "version": "0.8.1", "dev": true, + "peer": true, "requires": { "@babel/helper-define-polyfill-provider": "^0.4.0", "core-js-compat": "^3.30.1" @@ -15420,6 +15536,7 @@ "babel-plugin-polyfill-regenerator": { "version": "0.5.0", "dev": true, + "peer": true, "requires": { "@babel/helper-define-polyfill-provider": "^0.4.0" } @@ -15427,6 +15544,8 @@ "babelify": { "version": "10.0.0", "dev": true, + "optional": true, + "peer": true, "requires": {} }, "balanced-match": { @@ -15462,7 +15581,9 @@ }, "bn.js": { "version": "5.2.1", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "brace-expansion": { "version": "1.1.11", @@ -15481,11 +15602,15 @@ }, "brorand": { "version": "1.1.0", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "browser-pack": { "version": "6.1.0", "dev": true, + "optional": true, + "peer": true, "requires": { "combine-source-map": "~0.8.0", "defined": "^1.0.0", @@ -15498,6 +15623,8 @@ "browser-resolve": { "version": "2.0.0", "dev": true, + "optional": true, + "peer": true, "requires": { "resolve": "^1.17.0" } @@ -15509,6 +15636,8 @@ "browserify": { "version": "16.5.2", "dev": true, + "optional": true, + "peer": true, "requires": { "assert": "^1.4.0", "browser-pack": "^6.0.1", @@ -15563,6 +15692,8 @@ "browserify-aes": { "version": "1.2.0", "dev": true, + "optional": true, + "peer": true, "requires": { "buffer-xor": "^1.0.3", "cipher-base": "^1.0.0", @@ -15575,6 +15706,8 @@ "browserify-cipher": { "version": "1.0.1", "dev": true, + "optional": true, + "peer": true, "requires": { "browserify-aes": "^1.0.4", "browserify-des": "^1.0.0", @@ -15584,6 +15717,8 @@ "browserify-des": { "version": "1.0.2", "dev": true, + "optional": true, + "peer": true, "requires": { "cipher-base": "^1.0.1", "des.js": "^1.0.0", @@ -15594,6 +15729,8 @@ "browserify-rsa": { "version": "4.1.0", "dev": true, + "optional": true, + "peer": true, "requires": { "bn.js": "^5.0.0", "randombytes": "^2.0.1" @@ -15602,6 +15739,8 @@ "browserify-sign": { "version": "4.2.1", "dev": true, + "optional": true, + "peer": true, "requires": { "bn.js": "^5.1.1", "browserify-rsa": "^4.0.1", @@ -15617,6 +15756,8 @@ "readable-stream": { "version": "3.6.2", "dev": true, + "optional": true, + "peer": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -15628,6 +15769,8 @@ "browserify-zlib": { "version": "0.2.0", "dev": true, + "optional": true, + "peer": true, "requires": { "pako": "~1.0.5" } @@ -15645,6 +15788,8 @@ "buffer": { "version": "5.2.1", "dev": true, + "optional": true, + "peer": true, "requires": { "base64-js": "^1.0.2", "ieee754": "^1.1.4" @@ -15660,11 +15805,15 @@ }, "buffer-xor": { "version": "1.0.3", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "builtin-status-codes": { "version": "3.0.0", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "c8": { "version": "7.14.0", @@ -15696,7 +15845,9 @@ }, "cached-path-relative": { "version": "1.1.0", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "cachedir": { "version": "2.3.0", @@ -15786,6 +15937,8 @@ "chokidar": { "version": "3.5.2", "dev": true, + "optional": true, + "peer": true, "requires": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -15809,6 +15962,8 @@ "cipher-base": { "version": "1.0.4", "dev": true, + "optional": true, + "peer": true, "requires": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -15867,16 +16022,11 @@ "wrap-ansi": "^7.0.0" } }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true, - "optional": true - }, "coffeeify": { "version": "3.0.1", "dev": true, + "optional": true, + "peer": true, "requires": { "convert-source-map": "^1.3.0", "through2": "^2.0.0" @@ -15884,7 +16034,9 @@ }, "coffeescript": { "version": "1.12.7", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "color-convert": { "version": "2.0.1", @@ -15904,6 +16056,8 @@ "combine-source-map": { "version": "0.8.0", "dev": true, + "optional": true, + "peer": true, "requires": { "convert-source-map": "~1.1.0", "inline-source-map": "~0.6.0", @@ -15913,7 +16067,9 @@ "dependencies": { "convert-source-map": { "version": "1.1.3", - "dev": true + "dev": true, + "optional": true, + "peer": true } } }, @@ -15946,6 +16102,8 @@ "concat-stream": { "version": "1.6.2", "dev": true, + "optional": true, + "peer": true, "requires": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", @@ -15969,20 +16127,15 @@ }, "console-browserify": { "version": "1.2.0", - "dev": true - }, - "console.table": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/console.table/-/console.table-0.10.0.tgz", - "integrity": "sha512-dPyZofqggxuvSf7WXvNjuRfnsOk1YazkVP8FdxH4tcH2c37wc79/Yl6Bhr7Lsu00KMgy2ql/qCMuNu8xctZM8g==", "dev": true, - "requires": { - "easy-table": "1.1.0" - } + "optional": true, + "peer": true }, "constants-browserify": { "version": "1.0.0", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "convert-source-map": { "version": "1.9.0", @@ -15991,17 +16144,22 @@ "core-js-compat": { "version": "3.31.0", "dev": true, + "peer": true, "requires": { "browserslist": "^4.21.5" } }, "core-util-is": { "version": "1.0.3", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "create-ecdh": { "version": "4.0.4", "dev": true, + "optional": true, + "peer": true, "requires": { "bn.js": "^4.1.0", "elliptic": "^6.5.3" @@ -16009,13 +16167,17 @@ "dependencies": { "bn.js": { "version": "4.12.0", - "dev": true + "dev": true, + "optional": true, + "peer": true } } }, "create-hash": { "version": "1.2.0", "dev": true, + "optional": true, + "peer": true, "requires": { "cipher-base": "^1.0.1", "inherits": "^2.0.1", @@ -16027,6 +16189,8 @@ "create-hmac": { "version": "1.1.7", "dev": true, + "optional": true, + "peer": true, "requires": { "cipher-base": "^1.0.3", "create-hash": "^1.1.0", @@ -16048,6 +16212,8 @@ "crypto-browserify": { "version": "3.12.0", "dev": true, + "optional": true, + "peer": true, "requires": { "browserify-cipher": "^1.0.0", "browserify-sign": "^4.0.0", @@ -16149,7 +16315,9 @@ }, "dash-ast": { "version": "1.0.0", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "dashdash": { "version": "1.14.1", @@ -16241,16 +16409,6 @@ "strip-bom": "^4.0.0" } }, - "defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, - "optional": true, - "requires": { - "clone": "^1.0.2" - } - }, "define-properties": { "version": "1.2.0", "dev": true, @@ -16261,7 +16419,9 @@ }, "defined": { "version": "1.0.1", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "delayed-stream": { "version": "1.0.0" @@ -16269,6 +16429,8 @@ "deps-sort": { "version": "2.0.1", "dev": true, + "optional": true, + "peer": true, "requires": { "JSONStream": "^1.0.3", "shasum-object": "^1.0.0", @@ -16283,6 +16445,8 @@ "des.js": { "version": "1.1.0", "dev": true, + "optional": true, + "peer": true, "requires": { "inherits": "^2.0.1", "minimalistic-assert": "^1.0.0" @@ -16291,6 +16455,8 @@ "detective": { "version": "5.2.1", "dev": true, + "optional": true, + "peer": true, "requires": { "acorn-node": "^1.8.2", "defined": "^1.0.0", @@ -16308,6 +16474,8 @@ "diffie-hellman": { "version": "5.0.3", "dev": true, + "optional": true, + "peer": true, "requires": { "bn.js": "^4.1.0", "miller-rabin": "^4.0.0", @@ -16316,7 +16484,9 @@ "dependencies": { "bn.js": { "version": "4.12.0", - "dev": true + "dev": true, + "optional": true, + "peer": true } } }, @@ -16340,7 +16510,9 @@ }, "domain-browser": { "version": "1.2.0", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "domexception": { "version": "4.0.0", @@ -16356,6 +16528,8 @@ "duplexer2": { "version": "0.1.4", "dev": true, + "optional": true, + "peer": true, "requires": { "readable-stream": "^2.0.2" } @@ -16364,15 +16538,6 @@ "version": "0.2.0", "dev": true }, - "easy-table": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.1.0.tgz", - "integrity": "sha512-oq33hWOSSnl2Hoh00tZWaIPi1ievrD9aFG82/IgjlycAnW9hHx5PkJiXpxPsgEE+H7BsbVQXFVFST8TEXS6/pA==", - "dev": true, - "requires": { - "wcwidth": ">=1.0.1" - } - }, "ecc-jsbn": { "version": "0.1.2", "dev": true, @@ -16388,6 +16553,8 @@ "elliptic": { "version": "6.5.4", "dev": true, + "optional": true, + "peer": true, "requires": { "bn.js": "^4.11.9", "brorand": "^1.1.0", @@ -16400,7 +16567,9 @@ "dependencies": { "bn.js": { "version": "4.12.0", - "dev": true + "dev": true, + "optional": true, + "peer": true } } }, @@ -16689,11 +16858,15 @@ }, "events": { "version": "2.1.0", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "evp_bytestokey": { "version": "1.0.3", "dev": true, + "optional": true, + "peer": true, "requires": { "md5.js": "^1.3.4", "safe-buffer": "^5.1.1" @@ -16779,7 +16952,9 @@ }, "fast-safe-stringify": { "version": "2.1.1", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "fastq": { "version": "1.15.0", @@ -16920,7 +17095,9 @@ }, "get-assigned-identifiers": { "version": "1.2.0", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "get-caller-file": { "version": "2.0.5", @@ -17078,6 +17255,8 @@ "hash-base": { "version": "3.1.0", "dev": true, + "optional": true, + "peer": true, "requires": { "inherits": "^2.0.4", "readable-stream": "^3.6.0", @@ -17087,6 +17266,8 @@ "readable-stream": { "version": "3.6.2", "dev": true, + "optional": true, + "peer": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -17098,6 +17279,8 @@ "hash.js": { "version": "1.1.7", "dev": true, + "optional": true, + "peer": true, "requires": { "inherits": "^2.0.3", "minimalistic-assert": "^1.0.1" @@ -17124,6 +17307,8 @@ "hmac-drbg": { "version": "1.0.1", "dev": true, + "optional": true, + "peer": true, "requires": { "hash.js": "^1.0.3", "minimalistic-assert": "^1.0.0", @@ -17143,7 +17328,9 @@ }, "htmlescape": { "version": "1.1.1", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "http-proxy-agent": { "version": "5.0.0", @@ -17165,7 +17352,9 @@ }, "https-browserify": { "version": "1.0.0", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "https-proxy-agent": { "version": "5.0.1", @@ -17233,6 +17422,8 @@ "inline-source-map": { "version": "0.6.2", "dev": true, + "optional": true, + "peer": true, "requires": { "source-map": "~0.5.3" } @@ -17240,6 +17431,8 @@ "insert-module-globals": { "version": "7.2.1", "dev": true, + "optional": true, + "peer": true, "requires": { "acorn-node": "^1.5.2", "combine-source-map": "^0.8.0", @@ -17307,7 +17500,9 @@ }, "is-buffer": { "version": "1.1.6", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "is-callable": { "version": "1.2.7", @@ -17323,6 +17518,7 @@ "is-core-module": { "version": "2.12.1", "dev": true, + "peer": true, "requires": { "has": "^1.0.3" } @@ -17345,6 +17541,8 @@ "is-generator-function": { "version": "1.0.10", "dev": true, + "optional": true, + "peer": true, "requires": { "has-tostringtag": "^1.0.0" } @@ -17465,7 +17663,9 @@ }, "isarray": { "version": "1.0.0", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "isexe": { "version": "2.0.0", @@ -17795,6 +17995,8 @@ "json-stable-stringify": { "version": "0.0.1", "dev": true, + "optional": true, + "peer": true, "requires": { "jsonify": "~0.0.0" } @@ -17825,15 +18027,21 @@ }, "jsonify": { "version": "0.0.1", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "jsonparse": { "version": "1.3.1", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "JSONStream": { "version": "1.3.5", "dev": true, + "optional": true, + "peer": true, "requires": { "jsonparse": "^1.2.0", "through": ">=2.2.7 <3" @@ -17874,6 +18082,8 @@ "labeled-stream-splicer": { "version": "2.0.2", "dev": true, + "optional": true, + "peer": true, "requires": { "inherits": "^2.0.1", "stream-splicer": "^2.0.0" @@ -17935,11 +18145,14 @@ }, "lodash.clonedeep": { "version": "4.5.0", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "lodash.debounce": { "version": "4.0.8", - "dev": true + "dev": true, + "peer": true }, "lodash.flattendeep": { "version": "4.4.0", @@ -17947,7 +18160,9 @@ }, "lodash.memoize": { "version": "3.0.4", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "lodash.merge": { "version": "4.6.2", @@ -18068,6 +18283,8 @@ "md5.js": { "version": "1.3.5", "dev": true, + "optional": true, + "peer": true, "requires": { "hash-base": "^3.0.0", "inherits": "^2.0.1", @@ -18093,6 +18310,8 @@ "miller-rabin": { "version": "4.0.1", "dev": true, + "optional": true, + "peer": true, "requires": { "bn.js": "^4.0.0", "brorand": "^1.0.1" @@ -18100,7 +18319,9 @@ "dependencies": { "bn.js": { "version": "4.12.0", - "dev": true + "dev": true, + "optional": true, + "peer": true } } }, @@ -18123,11 +18344,15 @@ }, "minimalistic-assert": { "version": "1.0.1", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "minimalistic-crypto-utils": { "version": "1.0.1", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "minimatch": { "version": "3.1.2", @@ -18150,7 +18375,9 @@ }, "mkdirp-classic": { "version": "0.5.3", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "mlly": { "version": "1.4.0", @@ -18276,6 +18503,8 @@ "module-deps": { "version": "6.2.3", "dev": true, + "optional": true, + "peer": true, "requires": { "browser-resolve": "^2.0.0", "cached-path-relative": "^1.0.2", @@ -18546,7 +18775,9 @@ }, "os-browserify": { "version": "0.3.0", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "ospath": { "version": "1.2.2", @@ -18555,6 +18786,8 @@ "outpipe": { "version": "1.1.1", "dev": true, + "optional": true, + "peer": true, "requires": { "shell-quote": "^1.4.2" } @@ -18603,7 +18836,9 @@ }, "pako": { "version": "1.0.11", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "parent-module": { "version": "1.0.1", @@ -18615,6 +18850,8 @@ "parents": { "version": "1.0.1", "dev": true, + "optional": true, + "peer": true, "requires": { "path-platform": "~0.11.15" } @@ -18622,6 +18859,8 @@ "parse-asn1": { "version": "5.1.6", "dev": true, + "optional": true, + "peer": true, "requires": { "asn1.js": "^5.2.0", "browserify-aes": "^1.0.0", @@ -18639,7 +18878,9 @@ }, "path-browserify": { "version": "0.0.1", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "path-exists": { "version": "4.0.0", @@ -18655,11 +18896,14 @@ }, "path-parse": { "version": "1.0.7", - "dev": true + "dev": true, + "peer": true }, "path-platform": { "version": "0.11.15", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "path-scurry": { "version": "1.9.2", @@ -18697,6 +18941,8 @@ "pbkdf2": { "version": "3.1.2", "dev": true, + "optional": true, + "peer": true, "requires": { "create-hash": "^1.1.2", "create-hmac": "^1.1.4", @@ -18810,11 +19056,15 @@ }, "process": { "version": "0.11.10", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "process-nextick-args": { "version": "2.0.1", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "process-on-spawn": { "version": "1.0.0", @@ -18848,6 +19098,8 @@ "public-encrypt": { "version": "4.0.3", "dev": true, + "optional": true, + "peer": true, "requires": { "bn.js": "^4.1.0", "browserify-rsa": "^4.0.0", @@ -18859,7 +19111,9 @@ "dependencies": { "bn.js": { "version": "4.12.0", - "dev": true + "dev": true, + "optional": true, + "peer": true } } }, @@ -18873,7 +19127,9 @@ }, "punycode": { "version": "1.4.1", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "qs": { "version": "6.10.4", @@ -18884,7 +19140,9 @@ }, "querystring-es3": { "version": "0.2.1", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "querystringify": { "version": "2.2.0", @@ -18904,6 +19162,8 @@ "randomfill": { "version": "1.0.4", "dev": true, + "optional": true, + "peer": true, "requires": { "randombytes": "^2.0.5", "safe-buffer": "^5.1.0" @@ -18950,6 +19210,8 @@ "read-only-stream": { "version": "2.0.0", "dev": true, + "optional": true, + "peer": true, "requires": { "readable-stream": "^2.0.2" } @@ -18957,6 +19219,8 @@ "readable-stream": { "version": "2.3.8", "dev": true, + "optional": true, + "peer": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -18969,11 +19233,15 @@ "dependencies": { "safe-buffer": { "version": "5.1.2", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "string_decoder": { "version": "1.1.1", "dev": true, + "optional": true, + "peer": true, "requires": { "safe-buffer": "~5.1.0" } @@ -19001,11 +19269,13 @@ }, "regenerate": { "version": "1.4.2", - "dev": true + "dev": true, + "peer": true }, "regenerate-unicode-properties": { "version": "10.1.0", "dev": true, + "peer": true, "requires": { "regenerate": "^1.4.2" } @@ -19017,6 +19287,7 @@ "regenerator-transform": { "version": "0.15.1", "dev": true, + "peer": true, "requires": { "@babel/runtime": "^7.8.4" } @@ -19044,6 +19315,7 @@ "regexpu-core": { "version": "5.3.2", "dev": true, + "peer": true, "requires": { "@babel/regjsgen": "^0.8.0", "regenerate": "^1.4.2", @@ -19056,13 +19328,15 @@ "regjsparser": { "version": "0.9.1", "dev": true, + "peer": true, "requires": { "jsesc": "~0.5.0" }, "dependencies": { "jsesc": { "version": "0.5.0", - "dev": true + "dev": true, + "peer": true } } }, @@ -19104,6 +19378,7 @@ "resolve": { "version": "1.22.2", "dev": true, + "peer": true, "requires": { "is-core-module": "^2.11.0", "path-parse": "^1.0.7", @@ -19153,6 +19428,8 @@ "ripemd160": { "version": "2.0.2", "dev": true, + "optional": true, + "peer": true, "requires": { "hash-base": "^3.0.0", "inherits": "^2.0.1" @@ -19284,6 +19561,8 @@ "sha.js": { "version": "2.4.11", "dev": true, + "optional": true, + "peer": true, "requires": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -19292,6 +19571,8 @@ "shasum": { "version": "1.0.2", "dev": true, + "optional": true, + "peer": true, "requires": { "json-stable-stringify": "~0.0.0", "sha.js": "~2.4.4" @@ -19300,6 +19581,8 @@ "shasum-object": { "version": "1.0.0", "dev": true, + "optional": true, + "peer": true, "requires": { "fast-safe-stringify": "^2.0.7" } @@ -19317,7 +19600,9 @@ }, "shell-quote": { "version": "1.8.1", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "side-channel": { "version": "1.0.4", @@ -19338,7 +19623,9 @@ }, "simple-concat": { "version": "1.0.1", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "slash": { "version": "3.0.0", @@ -19353,19 +19640,11 @@ "is-fullwidth-code-point": "^3.0.0" } }, - "sort-array": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/sort-array/-/sort-array-4.1.5.tgz", - "integrity": "sha512-Ya4peoS1fgFN42RN1REk2FgdNOeLIEMKFGJvs7VTP3OklF8+kl2SkpVliZ4tk/PurWsrWRsdNdU+tgyOBkB9sA==", - "dev": true, - "requires": { - "array-back": "^5.0.0", - "typical": "^6.0.1" - } - }, "source-map": { "version": "0.5.7", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "source-map-js": { "version": "1.0.2", @@ -19497,6 +19776,8 @@ "stream-browserify": { "version": "2.0.2", "dev": true, + "optional": true, + "peer": true, "requires": { "inherits": "~2.0.1", "readable-stream": "^2.0.2" @@ -19512,6 +19793,8 @@ "stream-combiner2": { "version": "1.1.1", "dev": true, + "optional": true, + "peer": true, "requires": { "duplexer2": "~0.1.0", "readable-stream": "^2.0.2" @@ -19520,6 +19803,8 @@ "stream-http": { "version": "3.2.0", "dev": true, + "optional": true, + "peer": true, "requires": { "builtin-status-codes": "^3.0.0", "inherits": "^2.0.4", @@ -19530,6 +19815,8 @@ "readable-stream": { "version": "3.6.2", "dev": true, + "optional": true, + "peer": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -19541,6 +19828,8 @@ "stream-splicer": { "version": "2.0.1", "dev": true, + "optional": true, + "peer": true, "requires": { "inherits": "^2.0.1", "readable-stream": "^2.0.2" @@ -19549,6 +19838,8 @@ "string_decoder": { "version": "1.3.0", "dev": true, + "optional": true, + "peer": true, "requires": { "safe-buffer": "~5.2.0" } @@ -19630,6 +19921,8 @@ "subarg": { "version": "1.0.0", "dev": true, + "optional": true, + "peer": true, "requires": { "minimist": "^1.1.0" } @@ -19643,7 +19936,8 @@ }, "supports-preserve-symlinks-flag": { "version": "1.0.0", - "dev": true + "dev": true, + "peer": true }, "symbol-tree": { "version": "3.2.4", @@ -19652,6 +19946,8 @@ "syntax-error": { "version": "1.4.0", "dev": true, + "optional": true, + "peer": true, "requires": { "acorn-node": "^1.2.0" } @@ -19741,6 +20037,8 @@ "through2": { "version": "2.0.5", "dev": true, + "optional": true, + "peer": true, "requires": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" @@ -19753,6 +20051,8 @@ "timers-browserify": { "version": "1.4.2", "dev": true, + "optional": true, + "peer": true, "requires": { "process": "~0.11.0" } @@ -19841,13 +20141,9 @@ }, "tty-browserify": { "version": "0.0.1", - "dev": true - }, - "tunnel": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "tunnel-agent": { "version": "0.6.0", @@ -19877,7 +20173,9 @@ }, "typedarray": { "version": "0.0.6", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "typedarray-to-buffer": { "version": "3.1.5", @@ -19890,23 +20188,21 @@ "version": "5.1.3", "dev": true }, - "typical": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/typical/-/typical-6.0.1.tgz", - "integrity": "sha512-+g3NEp7fJLe9DPa1TArHm9QAA7YciZmWnfAqEaFrBihQ7epOv9i99rjtgb6Iz0wh3WuQDjsCTDfgRoGnmHN81A==", - "dev": true - }, "ufo": { "version": "1.1.2", "dev": true }, "umd": { "version": "3.0.3", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "undeclared-identifiers": { "version": "1.1.3", "dev": true, + "optional": true, + "peer": true, "requires": { "acorn-node": "^1.3.0", "dash-ast": "^1.0.0", @@ -19917,11 +20213,13 @@ }, "unicode-canonical-property-names-ecmascript": { "version": "2.0.0", - "dev": true + "dev": true, + "peer": true }, "unicode-match-property-ecmascript": { "version": "2.0.0", "dev": true, + "peer": true, "requires": { "unicode-canonical-property-names-ecmascript": "^2.0.0", "unicode-property-aliases-ecmascript": "^2.0.0" @@ -19929,11 +20227,13 @@ }, "unicode-match-property-value-ecmascript": { "version": "2.1.0", - "dev": true + "dev": true, + "peer": true }, "unicode-property-aliases-ecmascript": { "version": "2.1.0", - "dev": true + "dev": true, + "peer": true }, "universalify": { "version": "2.0.0", @@ -19974,6 +20274,8 @@ "url": { "version": "0.11.1", "dev": true, + "optional": true, + "peer": true, "requires": { "punycode": "^1.4.1", "qs": "^6.11.0" @@ -19982,6 +20284,8 @@ "qs": { "version": "6.11.2", "dev": true, + "optional": true, + "peer": true, "requires": { "side-channel": "^1.0.4" } @@ -19999,13 +20303,17 @@ "util": { "version": "0.10.4", "dev": true, + "optional": true, + "peer": true, "requires": { "inherits": "2.0.3" }, "dependencies": { "inherits": { "version": "2.0.3", - "dev": true + "dev": true, + "optional": true, + "peer": true } } }, @@ -20015,7 +20323,9 @@ }, "util-deprecate": { "version": "1.0.2", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "uuid": { "version": "9.0.0", @@ -20172,7 +20482,9 @@ }, "vm-browserify": { "version": "1.1.2", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "w3c-xmlserializer": { "version": "4.0.0", @@ -20205,6 +20517,8 @@ "watchify": { "version": "4.0.0", "dev": true, + "optional": true, + "peer": true, "requires": { "anymatch": "^3.1.0", "browserify": "^17.0.0", @@ -20218,6 +20532,8 @@ "browserify": { "version": "17.0.0", "dev": true, + "optional": true, + "peer": true, "requires": { "assert": "^1.4.0", "browser-pack": "^6.0.1", @@ -20272,6 +20588,8 @@ "through2": { "version": "2.0.5", "dev": true, + "optional": true, + "peer": true, "requires": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" @@ -20281,15 +20599,21 @@ }, "events": { "version": "3.3.0", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "path-browserify": { "version": "1.0.1", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "stream-browserify": { "version": "3.0.0", "dev": true, + "optional": true, + "peer": true, "requires": { "inherits": "~2.0.4", "readable-stream": "^3.5.0" @@ -20298,6 +20622,8 @@ "readable-stream": { "version": "3.6.2", "dev": true, + "optional": true, + "peer": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -20309,6 +20635,8 @@ "through2": { "version": "4.0.2", "dev": true, + "optional": true, + "peer": true, "requires": { "readable-stream": "3" }, @@ -20316,6 +20644,8 @@ "readable-stream": { "version": "3.6.2", "dev": true, + "optional": true, + "peer": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -20327,6 +20657,8 @@ "util": { "version": "0.12.5", "dev": true, + "optional": true, + "peer": true, "requires": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", @@ -20346,16 +20678,6 @@ "graceful-fs": "^4.1.2" } }, - "wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, - "optional": true, - "requires": { - "defaults": "^1.0.3" - } - }, "webidl-conversions": { "version": "7.0.0", "dev": true @@ -20547,7 +20869,9 @@ }, "xtend": { "version": "4.0.2", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "y18n": { "version": "5.0.8", diff --git a/src/App.tsx b/src/App.tsx index 0df674e2..9670ea29 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,22 +1,30 @@ -import { createBrowserRouter, RouterProvider } from "react-router-dom"; -import CreateTest from "./app/home/pages/CreateTest"; -import ListTests from "./app/home/pages/ListTests"; +import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; +import Login from "../src/app/home/pages/Login"; +import Cadastro from "../src/app/home/pages/Cadastro"; +import Reservar from "../src/app/home/pages/Reservar"; +import Perfil from "../src/app/home/pages/Perfil"; +import Avaliacoes from "../src/app/home/pages/Avaliacoes"; +import Recursos from "./app/home/pages/Recursos"; +import Manutencoes from "./app/home/pages/Manutencoes"; +import 'bootstrap/dist/css/bootstrap.min.css'; -const router = createBrowserRouter([ - { - path: "*", - Component: CreateTest, - }, - { - path: "/create-test", - Component: CreateTest, - }, - { - path: "/tests", - Component: ListTests, - }, -]); -export default function App() { - return Loading...

} />; +function App() { + + + return ( + + + } /> {/* Tela inicial = Login */} + } /> {/* Rota para Cadastro */} + } /> {/* Rota para Reservas */} + } /> {/* Rota para Modal */} + } /> {/* Rota para Avaliações */} + } /> {/* Rota para Recursos */} + } /> {/* Rota para Manutenções */} + + + ); } + +export default App; diff --git a/src/app/home/pages/Avaliacoes.tsx b/src/app/home/pages/Avaliacoes.tsx new file mode 100644 index 00000000..d1e3d8d5 --- /dev/null +++ b/src/app/home/pages/Avaliacoes.tsx @@ -0,0 +1,24 @@ +//import { useEffect } from "react"; +//import { useNavigate } from "react-router-dom"; +import SideBar from "../../../shared/components/SideBar/SideBar"; +import styles from "../../../shared/components/SideBar/SideBar.module.css"; + +const Avaliacoes = () => { + // const navigate = useNavigate(); + return ( +
+ {/* Sidebar fixa à esquerda */} +
+ +
+ + {/* Conteúdo da página */} +
+

Avaliações

+

Este é o restante da sua aplicação

+
+
+ ); +}; + +export default Avaliacoes; diff --git a/src/app/home/pages/Cadastro.tsx b/src/app/home/pages/Cadastro.tsx new file mode 100644 index 00000000..6bb6f27f --- /dev/null +++ b/src/app/home/pages/Cadastro.tsx @@ -0,0 +1,96 @@ +import { useState } from "react"; +import { cadastrar } from "../../../shared/services/autorizacao"; +import { useNavigate } from "react-router-dom"; +import Button from "../../../shared/components/Button"; +import Input from "../../../shared/components/Input"; +import ErrorMessage from "../../../shared/components/ErrorMessage"; +import styles from "../styles/Cadastro.module.css"; +import globalStyles from "../../../shared/components/LoginCadastro.module.css"; + +const Cadastro = () => { + const [nome, setNome] = useState(""); + const [cpf, setCpf] = useState(""); + const [email, setEmail] = useState(""); + const [professor, setProfessor] = useState("N"); + const [siape, setSiape] = useState(""); + const [senha, setSenha] = useState(""); + const [confirmarSenha, setConfirmarSenha] = useState(""); + const [error, setError] = useState(""); + const [successMessage, setSuccessMessage] = useState(""); + const navigate = useNavigate(); + + const handleCadastro = async (e: React.FormEvent) => { + e.preventDefault(); + + try { + const response = await cadastrar({ nome, cpf, email, professor, siape, senha, confirmarSenha }); + + if (!response.success) { + setError(response.error || ""); + return; + } + + setSuccessMessage("Cadastro realizado com sucesso!"); + setNome(""); + setCpf(""); + setEmail(""); + setProfessor("N"); + setSiape(""); + setSenha(""); + setConfirmarSenha(""); + setError(""); + } catch (err) { + setError("Falha no cadastro. Verifique os dados e tente novamente."); + } + }; + + return ( +
+
+

SAGAA

+

+ Sistema de Agendamento e
+ Gerenciamento Acadêmico Automático +

+ + {error && } + + {successMessage ? ( +
+
{successMessage}
+ +
+ ) : ( +
+ + + + + + + + + + {professor === "S" && } + +
+ navigate("/")}>Já possuo uma conta + +
+
+ )} +
+
+ ); +}; + +export default Cadastro; diff --git a/src/app/home/pages/Login.tsx b/src/app/home/pages/Login.tsx new file mode 100644 index 00000000..bce1109e --- /dev/null +++ b/src/app/home/pages/Login.tsx @@ -0,0 +1,68 @@ +import { useState } from "react"; +import { login } from "../../../shared/services/autorizacao"; +import { useNavigate } from "react-router-dom"; +import Button from "../../../shared/components/Button"; +import Input from "../../../shared/components/Input"; +import ErrorMessage from "../../../shared/components/ErrorMessage"; +import styles from "../styles/Login.module.css"; +import globalStyles from "../../../shared/components/LoginCadastro.module.css"; +import Loader from "../../../shared/components/Loader"; + +const Login = () => { + const [email, setEmail] = useState(""); + const [senha, setPassword] = useState(""); + const [error, setError] = useState(""); + const [loading, setLoading] = useState(false); + const navigate = useNavigate(); + + const handleLogin = async (e: React.FormEvent) => { + e.preventDefault(); + setLoading(true); + + try { + const response = await login(email, senha); + + if (!response.success) { + setError(response.error ?? ""); + setLoading(false); + return; + } + + navigate("/reservas"); + } catch (err) { + setError("Falha no login. Verifique suas credenciais."); + } finally { + setLoading(false); + } + }; + + return ( +
+
+

SAGAA

+

+ Sistema de Agendamento e
+ Gerenciamento Acadêmico Automático +

+ + {error && } + +
+ + + + +
+ +
+ Esqueceu a senha? + navigate("/cadastro")} className={styles.link}>Não tem conta ainda? +
+
+
+ ); +}; + +export default Login; diff --git a/src/app/home/pages/Manutencoes.tsx b/src/app/home/pages/Manutencoes.tsx new file mode 100644 index 00000000..08db54cb --- /dev/null +++ b/src/app/home/pages/Manutencoes.tsx @@ -0,0 +1,293 @@ +import { useState, useEffect } from "react"; +import SideBar from "../../../shared/components/SideBar/SideBar"; +import styles from "../../../shared/components/SideBar/SideBar.module.css"; + +type Reserva = { + id: number; + sala_id: string; + data: string; + start_time: string; + end_time: string; + status: string; +}; + +type SolicitacaoManutencao = { + id: number; + reserva_id: number; + descricao: string; +}; + +const PegarReservasFinalizadas = async (professorId: number): Promise => { + try { + const resposta = await fetch(`http://127.0.0.1:5000/api/reservas/${professorId}`); + const reservas: Reserva[] = await resposta.json(); + return reservas.filter((reserva) => reserva.status === "finalizada" || reserva.status === "ativa"); + } catch (erro) { + console.error("Erro ao buscar reservas finalizadas:", erro); + return []; + } +}; + +const Manutencoes = () => { + const [reservasFinalizadas, setReservasFinalizadas] = useState([]); + const [solicitacoesManutencao, setSolicitacoesManutencao] = useState<{ + [key: number]: SolicitacaoManutencao; + }>({}); + const [descricaoPorReserva, setDescricaoPorReserva] = useState<{ [key: number]: string }>({}); + const [editando, setEditando] = useState(null); // ID da reserva em edição + const [reservaParaExcluir, setReservaParaExcluir] = useState(null); // ID da reserva a ser excluída + + // Supondo que o ID do professor logado seja 3 (substitua pelo valor real) + const professorId = 3; + + useEffect(() => { + const fetchReservas = async () => { + const reservas = await PegarReservasFinalizadas(professorId); + setReservasFinalizadas(reservas); + }; + fetchReservas(); + }, [professorId]); + + const handleSolicitarManutencao = async (reservaId: number) => { + const descricao = descricaoPorReserva[reservaId] || ""; + if (!descricao.trim()) { + alert("O campo 'O que havia de errado na sala?' não pode estar vazio."); + return; + } + + try { + const resposta = await fetch("http://127.0.0.1:5000/solicitacoes/manutencao", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ reserva_id: reservaId, descricao }), + }); + + const resultado = await resposta.json(); + if (resposta.ok) { + alert("Solicitação de manutenção criada com sucesso!"); + setSolicitacoesManutencao((prev) => ({ + ...prev, + [reservaId]: { id: resultado.id, reserva_id: reservaId, descricao }, + })); + setDescricaoPorReserva((prev) => ({ ...prev, [reservaId]: "" })); // Limpa o campo de texto + } else { + alert(`Erro: ${resultado.erro || "Erro ao criar solicitação"}`); + } + } catch (erro) { + console.error("Erro ao enviar solicitação:", erro); + alert("Erro ao enviar solicitação. Verifique a conexão com o servidor."); + } + }; + + const handleEditarManutencao = async (reservaId: number) => { + const descricao = descricaoPorReserva[reservaId] || ""; + if (!descricao.trim()) { + alert("O campo 'O que havia de errado na sala?' não pode estar vazio."); + return; + } + + const solicitacaoId = solicitacoesManutencao[reservaId].id; + try { + const resposta = await fetch(`http://127.0.0.1:5000/solicitacoes/manutencao/${solicitacaoId}`, { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ descricao }), + }); + + const resultado = await resposta.json(); + if (resposta.ok) { + alert("Solicitação de manutenção atualizada com sucesso!"); + setSolicitacoesManutencao((prev) => ({ + ...prev, + [reservaId]: { ...prev[reservaId], descricao }, + })); + setDescricaoPorReserva((prev) => ({ ...prev, [reservaId]: "" })); // Limpa o campo de texto + setEditando(null); // Sai do modo de edição + } else { + alert(`Erro: ${resultado.erro || "Erro ao editar solicitação"}`); + } + } catch (erro) { + console.error("Erro ao editar solicitação:", erro); + alert("Erro ao editar solicitação. Verifique a conexão com o servidor."); + } + }; + + const handleExcluirManutencao = async (reservaId: number) => { + const solicitacaoId = solicitacoesManutencao[reservaId].id; + try { + const resposta = await fetch(`http://127.0.0.1:5000/solicitacoes/manutencao/${solicitacaoId}`, { + method: "DELETE", + }); + + if (resposta.ok) { + alert("Solicitação de manutenção excluída com sucesso!"); + setSolicitacoesManutencao((prev) => { + const newSolicitacoes = { ...prev }; + delete newSolicitacoes[reservaId]; + return newSolicitacoes; + }); + setDescricaoPorReserva((prev) => ({ ...prev, [reservaId]: "" })); // Limpa o campo de texto + setEditando(null); // Sai do modo de edição + } else { + const resultado = await resposta.json(); + alert(`Erro: ${resultado.erro || "Erro ao excluir solicitação"}`); + } + } catch (erro) { + console.error("Erro ao excluir solicitação:", erro); + alert("Erro ao excluir solicitação. Verifique a conexão com o servidor."); + } + setReservaParaExcluir(null); // Fecha o popup após a exclusão + }; + + return ( +
+ {/* Sidebar fixa à esquerda */} +
+ +
+ + {/* Conteúdo da página */} +
+

Solicitação de Manutenções

+ {reservasFinalizadas.length > 0 ? ( + reservasFinalizadas.map((reserva) => ( +
+

Sala {reserva.sala_id}

+

Data: {reserva.data} | Hora: {reserva.start_time} às {reserva.end_time}

+ + {solicitacoesManutencao[reserva.id] && editando !== reserva.id ? ( + <> +

Solicitação realizada:

+

{solicitacoesManutencao[reserva.id].descricao}

+
+ + +
+ + ) : ( + <> +