import { Home } from "../views/Home.js";
import { Chat } from "../views/pages/Chat/index.js";
import { Error } from "../views/pages/Error/index.js";
import { Grupo } from "../views/pages/Grupo/index.js";

import { navigateTo, queryStringToObject, setRootEl, setRoutes, rootEl } from '../router.js';

describe('Teste da aplicação de roteamento', () => {
  let mockRoutes; // Defina mockRoutes em um escopo mais amplo

  // Mocking the DOM
  beforeAll(() => {
    // Simulate root element
    document.body.innerHTML = '<div id="root"></div>';
    setRootEl(document.getElementById('root'));

    // Define mock routes
    mockRoutes = {
      "/": Home,
      "/chat": Chat,
      "/error": Error,
      "/grupo": Grupo
    };
    setRoutes(mockRoutes);
  });

  test('Teste de definição de rotas', () => {
    expect(Object.keys(mockRoutes).length).toBe(4);
    expect(mockRoutes['/']).toBe(Home);
    expect(mockRoutes['/chat']).toBe(Chat);
    expect(mockRoutes['/error']).toBe(Error);
    expect(mockRoutes['/grupo']).toBe(Grupo);
  });

  test('Teste de inicialização da aplicação', () => {
    // Simulate DOMContentLoaded event
    window.dispatchEvent(new Event('DOMContentLoaded'));
    expect(document.getElementById('root')).toBeDefined();
    expect(window.location.pathname).toBe('/');
  });
});
describe('Testes adicionais', () => {
  let rootElement;

  beforeEach(() => {
    // Crie um novo elemento raiz antes de cada teste
    rootElement = document.createElement('div');
    rootElement.id = 'root';
    document.body.appendChild(rootElement);
  });

  afterEach(() => {
    // Remova o elemento raiz após cada teste
    document.body.removeChild(rootElement);
  });

  test('navigateTo modifica a URL corretamente', () => {
    navigateTo('/nova-rota');
    expect(window.location.pathname).toBe('/nova-rota');
  });

  test('navigateTo chama renderView com os argumentos corretos', () => {
    const renderViewMock = jest.fn();
    window.history.pushState = jest.fn(); // Mock pushState para evitar mudanças reais na URL
    setRoutes({ '/nova-rota': jest.fn() }); // Mock das rotas
    setRootEl(rootElement); // Defina o elemento raiz

    navigateTo('/nova-rota');

    expect(renderViewMock).toHaveBeenCalledWith('/nova-rota', {});
  });

  test('queryStringToObject converte corretamente uma string de consulta em um objeto', () => {
    const queryString = 'param1=value1&param2=value2';
    const expectedObject = { param1: 'value1', param2: 'value2' };

    expect(queryStringToObject(queryString)).toEqual(expectedObject);
  });

  test('setRootEl atribui corretamente o elemento raiz', () => {
    const element = document.createElement('div');
    setRootEl(element);
    expect(rootEl).toEqual(element);
  });

  test('setRoutes atribui corretamente as rotas', () => {
    const routes = { '/nova-rota': jest.fn() };
    setRoutes(routes);
    expect(routes).toEqual(routes);
  });

  // Adicione mais testes conforme necessário para cobrir outras partes do código
});

let routes = {};
let rootEl = null;
const ERROR_PATH = "/error";

export const setRoutes = (newRoutes) => {
  // Adiciona validação para garantir que newRoutes seja um objeto
  if (typeof newRoutes !== 'object' || newRoutes === null) {
    throw new Error('As rotas devem ser um objeto.');
  }
  routes = newRoutes;
};

export const setRootEl = (element) => {
  // Adiciona validação para garantir que element seja um elemento DOM válido
  if (!(element instanceof Element)) {
    throw new Error('O elemento raiz deve ser um elemento DOM válido.');
  }
  rootEl = element;
};

const queryStringToObject = (queryString) =>
  Object.fromEntries(new URLSearchParams(queryString).entries());

const renderView = (pathName, props = {}) => {
  // Adiciona tratamento para rota não encontrada
  if (!(pathName in routes)) {
    console.error(`Rota não encontrada: ${pathName}`);
    pathName = ERROR_PATH;
  }
  // Usa textContent para evitar injeção de HTML
  rootEl.textContent = "";
  const viewEl = routes[pathName](props);
  rootEl.appendChild(viewEl);
};

export const navigateTo = (pathname, props = {}) => {
  const url = window.location.origin + pathname;
  window.history.pushState({}, pathname, url);
  renderView(pathname, props);
};

export const onURLChange = () => {
  const { pathname, search } = window.location;
  const props = queryStringToObject(search);
  renderView(pathname, props);
};

O que é um aplicativo de página única (SPA)?

Um aplicativo de página única (SPA) é uma aplicação web que carrega todo o seu conteúdo a partir de um único arquivo HTML (comumente chamado de index.html) e atualiza dinamicamente o conteúdo enquanto o usuário interage com a aplicação.

Em vez de carregar páginas HTML completas e separadas cada vez que uma ação é executada, como clicar em um link, ele apenas carrega o conteúdo necessário para atualizar a visualização atual. Uma SPA dá a ilusão de que estamos navegando em páginas HTML separadas, mas na verdade é a mesma página com conteúdo diferente renderizado (desenhado) dinamicamente.

// Objeto que armazena as rotas da aplicação
let routes = {};

// Elemento raiz da aplicação
let rootEl = null;

// Caminho de erro padrão
const ERROR_PATH = "/error";

// Função para definir as rotas da aplicação
export const setRoutes = (newRoutes) => {
  routes = newRoutes;
};

// Função para definir o elemento raiz da aplicação
export const setRootEl = (element) => {
  rootEl = element;
};

// Função para converter uma string de consulta em um objeto
const queryStringToObject = (queryString) =>
  Object.fromEntries(new URLSearchParams(queryString).entries());

// Função para renderizar a visualização da rota especificada
const renderView = (pathName, props = {}) => {
  // Verificar se o caminho não existe nas rotas, redirecionar para o caminho de erro
  if (!(pathName in routes)) {
    pathName = ERROR_PATH;
  }

  // Limpar o conteúdo do elemento raiz
  rootEl.innerHTML = "";

  // Obter o elemento de visualização da rota e adicioná-lo ao elemento raiz
  const viewEl = routes[pathName](props);
  rootEl.appendChild(viewEl);
};

// Função para navegar para um novo caminho e renderizar sua visualização correspondente
export const navigateTo = (pathname, props = {}) => {
  // Construir a URL completa
  const url = window.location.origin + pathname;

  // Atualizar a URL no histórico do navegador
  window.history.pushState({}, pathname, url);

  // Renderizar a visualização da nova rota
  renderView(pathname, props);
};

// Função para lidar com alterações na URL do navegador
export const onURLChange = () => {
  // Obter o pathname e a string de busca da URL atual
  const { pathname, search } = window.location;

  // Converter a string de busca em um objeto de parâmetros
  const props = queryStringToObject(search);

  // Renderizar a visualização da rota correspondente ao pathname atual
  renderView(pathname, props);
};

O que é um router?

No contexto de SPAs, um router é um módulo JavaScript que gerencia a navegação dentro do aplicativo sem precisar recarregar a página inteira. Sua principal função é atribuir URLs às diferentes visualizações ou componentes do aplicativo e atualizar a interface com base no URL atual.

Por exemplo, se um usuário estiver na página inicial de um SPA www.website.com, e em seguida, você clica no link "About" que leva você para www.website.com/about, o router detecta alterações na URL, encontra e carrega dinamicamente o conteúdo correspondente para "about". Não existe um arquivo about.html, mas o router e a view criam a sensação de que estamos navegando para uma nova página. Isso permite que o SPA simule a experiência de navegação de uma aplicação web de várias páginas, mesmo que você tenha carregado apenas uma única página HTML a princípio.

Esta página implementa um sistema de roteamento e renderização dinâmica de visualizações em uma aplicação web. Aqui está um resumo das principais funcionalidades e conceitos abordados:

  1. Roteamento Dinâmico: A página permite a definição e manipulação dinâmica de rotas, onde cada rota está associada a uma função que renderiza uma visualização específica.
  2. Renderização Dinâmica de Visualizações: Quando a URL da página é alterada, a função onURLChange é acionada. Ela obtém o pathname da URL e usa-o para determinar qual visualização deve ser renderizada. A função renderView é então chamada para renderizar a visualização correspondente no elemento raiz da aplicação (rootEl).
  3. Histórico de Navegação: A função navigateTo é responsável por navegar para um novo caminho, atualizando a URL no histórico do navegador e renderizando a visualização correspondente.
  4. Conceito de Single Page Application (SPA): Sim, esta página está utilizando o conceito de SPAs. SPAs são aplicações web que carregam uma única página HTML e atualizam dinamicamente partes dessa página à medida que o usuário interage com a aplicação, em vez de carregar páginas completamente novas a cada navegação. Isso é alcançado através da manipulação dinâmica do DOM e do uso de APIs do navegador, como o history.pushState, para manipular a URL e o histórico de navegação sem recarregar a página.

Em resumo, esta página implementa um sistema de roteamento e renderização dinâmica de visualizações em uma aplicação web, seguindo o conceito de Single Page Application (SPA). Isso permite uma experiência de usuário mais fluída e responsiva, sem a necessidade de recarregar a página a cada mudança de rota.