4
respostas

Trabalhando com Jsdom - Não consigo obter o objeto FileList

Ola, gostaria de uma ajuda dos mais experientes. Estou fazendo uma api de cadastro, e nesse formulário tem um input do tipo file. Como template estou usando handlebars. O problema esta quando uso o Jsdom para obter o arquivo do input:

JSDOM.fromFile('templates/editar.handlebars').then((dom)=>{ const { document } = dom.window const file = document.querySelector('#file')

console.log(file.files[0].name);
// undefined

})

Fiz uma tag script dentro do arquivo do template para testar e esta me retornando tudo o que preciso para renderizar a imagem. Uso método URL.createObjectURL que me retorna um blob a atraves disso consigo manipular para renderizar em image.src, mas pelo node parece que o objeto FileList vem vazio. Ainda estou lendo a documentação do Jsdom, mas ainda não encontrei nada a respeito.

Porque não consigo obter o objeto FileList pelo node.js?

4 respostas

Olá Gustavo, tudo bem?

O que você está enfrentando é uma limitação do jsdom. O jsdom é uma biblioteca que simula um ambiente de navegador no Node.js, mas ele não implementa todas as funcionalidades que um navegador real possui.

No caso do input do tipo file, o jsdom não tem a capacidade de simular a seleção de arquivos, pois isso envolve interações com o sistema de arquivos do usuário, algo que um ambiente de servidor como o Node.js não pode fazer por questões de segurança.

Quando você usa document.querySelector('#file').files, está tentando acessar a propriedade files de um elemento de input do tipo file, que em um navegador real seria preenchida quando o usuário seleciona um arquivo. No jsdom, essa propriedade permanece vazia porque não há interação real do usuário para selecionar um arquivo.

Se você precisa testar essa funcionalidade, uma abordagem seria simular a seleção de arquivos nos seus testes. Você pode criar um objeto File manualmente e atribuí-lo ao elemento de input no seu código de teste. Aqui está um exemplo de como você pode fazer isso:

const { JSDOM } = require('jsdom');
const fs = require('fs');

JSDOM.fromFile('templates/editar.handlebars').then((dom) => {
  const { document } = dom.window;
  const fileInput = document.querySelector('#file');

  // Simulando um arquivo
  const file = new dom.window.File(['conteúdo do arquivo'], 'arquivo.txt', {
    type: 'text/plain',
  });

  // Atribuindo o arquivo simulado ao input
  Object.defineProperty(fileInput, 'files', {
    value: [file],
  });

  console.log(fileInput.files[0].name); // Deve imprimir 'arquivo.txt'
});

Dessa forma, você pode testar a lógica do seu código sem precisar de uma interação real com o sistema de arquivos do usuário.

Espero ter ajudado e bons estudos!

Caso este post tenha lhe ajudado, por favor, marcar como solucionado ✓.

Bom dia Armano, obrigado pela resposta!

Bem, ja estava desconfiando disso! Na verdade, o que pretendo fazer é atualizar uma imagem quando o usuario desejar muda-la, e isso sem dar refresh na página, e com node, vejo que é um pouco complicado, pois preciso ter acesso ao dom para poder pegar o arquivo inserido no input e trocar a imagem. Como disse, consegui fazer isso direto no arquivo do tamplete do handlebar porque o método URL.createObjectURL me devolve um blob, se eu pudesse passar esse retorno para o arquivo do node, talvez resolveria meu problema. Porque para atualizar a imagem estou tendo que criar um botão atualizar imagem para mandar o file, fazer upload, trata-la, e mandar para a paginar renderizar novamente, buscando todas a informações do banco de dados novamente e depois salvar, para chamar o update. Se tivesse como eu pegar esse arquivo, sem ter que chamar essa rota para receber a imagem, economizaria varias linhas de código, mas por enquanto esta me servindo. No caso de simular uma imagem, pensei em usar o req.file.filename do body, mas de qualquer forma vou ter que chamar essa rota para enviar o arquivo. No issues do repositório do Jsdom vi alguma coisa parecida, preciso dar uma olhada com calma la e testar. Não sei se fiz entender e se você puder me dar uma luz, ficarei grato.

Obrigado

Oi, Gustavo! Tudo bem?

Sobre sua última dúvida, realmente o jsdom não consegue simular a seleção de arquivos devido às limitações de ambiente. Uma abordagem para atualizar uma imagem sem refresh na página, usando Node.js e handlebars, é possível, mas requer uma estratégia diferente.

Você mencionou usar URL.createObjectURL para renderizar a imagem diretamente no navegador via handlebars. Para alcançar algo similar no Node.js, você pode considerar o seguinte:

  1. Enviar o Blob para o Backend: Ao escolher um arquivo no frontend, você pode usar bibliotecas como multer no Node.js para receber o arquivo.

  2. Salvar e Atualizar: Salve o arquivo no servidor, atualize a referência da imagem no banco de dados e retorne um caminho acessível para o frontend sem recarregar a página.

  3. Renderização no Frontend: No frontend, após obter o caminho da imagem, atualize o atributo src da imagem sem recarregar a página usando JavaScript.

Exemplo de como manipular o arquivo recebido no Node.js usando multer:

const multer = require('multer');
const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, 'uploads/') // diretório onde os arquivos serão salvos
  },
  filename: function (req, file, cb) {
    cb(null, file.originalname) // nome do arquivo salvo
  }
});
const upload = multer({ storage: storage });

app.post('/upload', upload.single('file'), (req, res, next) => {
  const file = req.file;
  // Salvar caminho do arquivo no banco de dados
  // Retornar o caminho do arquivo para o frontend
  res.json({ path: '/uploads/' + file.filename });
});

No frontend, manipule a resposta para atualizar dinamicamente a imagem.

Espero que isso ajude a encontrar uma solução eficiente para sua necessidade. Fico à disposição para mais dúvidas.

Bom dia Armano!

Sim, já estou fazendo isso com o multer, fazendo upload, removedo o arquivo ao deletar no bano de dados, tudo certinho. A questão é: Como atualizar uma imagem, quando o usuario quiser editar as informações, sabe, e quiser mudar a imagem, sem atualizar a pagina e a nova imagem ser enviada para a o arquivo do node sem dar o submit.