Giter Club home page Giter Club logo

ghub-frontend's Introduction

🖖

PT-BR

Sobre mim

Bem-vindo ao meu perfil, me chamo Isac, sou de São Paulo, Brasil, e atualmente trabalho na Alice Saúde, sou Desenvolvedor de Software, especialista em Frontend (e vez ou outra faço alguma tarefa de outras especialidades como backend ou infra), gosto muito de interfaces do usuário, procuro extrair o máximo de usabilidade, desempenho, sem perder a beleza (que vem dos Product Designers, porque não sou bom com isso haha, mas quando não tenho um, eu tento, sempre da para se inspirar em algum Dribble, Pinterest ou outras ideias pela web).

Atualmente tenho trabalhado em um projeto open source chamado Clingon, que é uma ferramenta para desenvolvedores, ficaria grato se você desse uma olhada: https://clingon.dev

EN

About me

Welcome to my profile, my name is Isac, I'm from São Paulo, Brazil, and I currently work at Alice Saúde, I'm a Software Developer, specialist in Frontend (and occasionally I do some task from other specialties such as backend or infra ), I really like user interfaces, I try to extract the maximum usability, performance, without losing beauty (which comes from Product Designers, because I'm not good with that haha, but when I don't have one, I try, you can always inspiration from some Dribble, Pinterest or other ideas around the web).

Currently I have been working on an open source project called Clingon, which is a tool for developers, I would be grateful if you would take a look: https://clingon.dev


Stats

stats graph languages graph

Social

ghub-frontend's People

Contributors

ipetinate avatar

Watchers

 avatar

ghub-frontend's Issues

Cypress

GHub

Pesquise repositórios e usuários do GitHub.

Objetivo

Motivação para criar o projeto

  • Este projeto foi criado como exemplo para uma decisão técnica sobre a escolha de ferramentas de teste.
  • Irei criar branchs separadas para testar diferentes implementações de bibliotecas de teste, como exemplo o Vitest, Cypress, Jest, Axios Mock Adapter, MSW, Playwright.

Tecnologias

O que foi utilizado neste exemplo?

Instalação [CYPRESS]

Como faz para configurar e rodar os testes e2e com Cy?

  1. Instalar pacote do cypress

    # npm
    npm i -D cypress
    
    # yarn
    yarn add -D cypress
  2. Adicionar o script para rodar o cypress no seu package.json

    "cypress": "cypress open"
  3. Configurar o ambiente

    1. Rode npm run cypress
    2. Uma janela do navegador será aberta e irá apresentar duas opções
      1. Cypress setup window
    3. Selecione a opção E2E Testing
    4. Veja todos os itens que ele configurou e continue
      1. Cypress todo list
    5. Serão criados alguns arquivos no seu projeto
      1. Captura de Tela 2023-03-20 às 13 45 02
    6. E será perguntado qual browser você quer usar para rodar os testes, se escolher Electron ele irá utilizar uma soluçãso própria para execução dos testes.
      1. Cypress choose browser
    7. Após a escolha de browser, ele irá abrir outra janela com uma interface onde você tem acesso aos testes que irão ser e executados e/ou criar novos testes por lá, alé, de consultar a documentação, debug de testes, configurações etc.
      1. Cypress UI after choose browser
  4. Escrever os testes

    1. Vou utilizar a opção da direita: Create new spec para criar um arquivo de teste
      1. Cypress choose a option on welcome screen
    2. Ele vai perguntar o nome do arquivo
      1. Enter path o new file
      2. Vou seguir um padrão semelhante ao da aplicação, criando uma pasta chamada pages/ dentro da pasta e2e do pattern do cypress
      3. Fill path
    3. Ao finalizar ele vai criar o arquivo e perguntar se quero rodar ou criar outro arquivo de spec, vou escolher rodar esse spec criado pela UI
      1. Choose an option
      2. Ao rodar ele abriu um endereço de exemplo do cypress
      3. Example spec
    4. Vou editar o arquivo de spec e implementar meus testes dentro dele, no teste, deve ser acessada a home do projeto e visualizada as opções
      1. Access home page on test
  5. Após escrever os testes, podemos vê-los nessa interface do Cypress

    1. Cypress spec list

    2. Para rodar os testes, clique no arquivo, que poderá rodá-lo e ver o resultado da execução

      Gravacao.de.Tela.2023-03-20.as.17.10.31.mov
  6. Podemos também rodar os testes via terminal

    1. Adicione o comando no scripts do package.json

      "cy:run": "cypress run"
    2. Abra um terminal e rode

      # npm
      npm run cy:run
      
      # yarn
      yarn cy:run
    3. Ao rodar veremos a seguinte saída no terminal

      Captura de Tela 2023-03-20 às 17 20 09
      Captura de Tela 2023-03-20 às 17 20 21
      Captura de Tela 2023-03-20 às 17 20 38
      Captura de Tela 2023-03-20 às 17 20 48

Erros e problemas

  1. O primeiro problema encontrado foi relacionado aos modulos isolados do TS, como o teste não importa nada até o momento, fica dando esse erro (pode ser resolvido usando a gambiarra de exportar um objeto do arquivo export {}, mas não recomendo, o warning do eslint não faz os testes quebrarem nem nada do tipo)
    1. Captura de Tela 2023-03-20 às 13 56 12

Playwright

GHub

Pesquise repositórios e usuários do GitHub.

Objetivo

Motivação para criar o projeto

  • Este projeto foi criado como exemplo para uma decisão técnica sobre a escolha de ferramentas de teste.
  • Irei criar branchs separadas para testar diferentes implementações de bibliotecas de teste, como exemplo o Vitest, Cypress, Jest, Axios Mock Adapter, MSW, Playwright.

Tecnologias

O que foi utilizado neste exemplo?

Instalação [PLAYWRIGHT]

Como foi instalar o playwright? O que achei de implementar testes como ele?

  1. Executar comando de init para configurar o ambiente de testes

    # npm
    npm init playwright@latest
    
    # yarn
    yarn create playwright
    
    # pnpm
    pnpm dlx create-playwright
    1. O comando acima, fará algumas perguntas:

      1. Aonde você quer por os testes: escolhi a pasta tests na raiz, igual ele sugere por padrão.
      2. Se quer configurar o fluxo do Github Actions: eu não quis, pois não irei utilizar o GH Actions, mas caso queira, é só apertar y
      3. Instalar browsers do Playwright: eu escolhi sim

      Captura de Tela 2023-03-21 às 07 41 39

    2. Após a execução do terminal concluir, alguns arquivos serão criados no seu projeto:

      Captura de Tela 2023-03-21 às 07 47 19

  2. Como o Playwright é uma tecnologia desenvolvida pela Microsoft, o VS Code possui uma extensão dedicada para ele. E após o a configuração ele irá sugerir que você instale a extensão oficial, caso queira, clique em install

    Captura de Tela 2023-03-21 às 07 51 21

    Captura de Tela 2023-03-21 às 07 53 45

  3. Execução dos testes

    1. Vamos adicionar um comando para executar nossos testes, adicione a linha abaixo na seção scripts do seu package.json

      "test:e2e": "playwright test"
    2. Execute o comando em um terminal

      npm run test:e2e

      Captura de Tela 2023-03-21 às 07 57 22

    3. Os testes de exemplo executaram com sucesso (agora podemos implementar nossos testes e ver se tudo está funcionando corretamente)

      image

  4. Implementar nossos testes

    1. Home

      import { test, expect } from '@playwright/test'
      
      test.beforeEach(async ({ page }) => {
          await page.goto('http://localhost:3000')
      })
      
      test.describe('Home Page', () => {
          test('Visit home', async ({ page }) => {
              await expect(page).toHaveTitle(/GHub/)
      
              const welcomeText = page.getByText('Olá, bem-vindo ao GHub')
              const subWelcomeText = page.getByText(
                  'Encontre usuários ou repositórios do GitHub'
              )
      
              const usersLink = page.getByTestId('Usuários')
              const reposLink = page.getByTestId('Repositórios')
      
              expect(welcomeText).toBeDefined()
              expect(subWelcomeText).toBeDefined()
      
              expect(usersLink).toBeDefined()
              expect(reposLink).toBeDefined()
          })
      
          test('On home, visit Users page', async ({ page }) => {
              await page.goto('http://localhost:3000')
      
              // Click the get started link.
              await page.getByTestId('Usuários').click()
      
              // Expects the URL to contain intro.
              await expect(page).toHaveURL(/.*users/)
          })
      
          test('On home, visit Repos page', async ({ page }) => {
              await page.goto('http://localhost:3000')
      
              // Click the get started link.
              await page.getByTestId('Repositórios').click()
      
              // Expects the URL to contain intro.
              await expect(page).toHaveURL(/.*repos/)
          })
      })

Captura de Tela 2023-03-21 às 11 17 21

Captura de Tela 2023-03-21 às 11 18 50

Plugin do VSCode

Experiência utilizando o plugin oficial

  • Gostei do plugin, como o Playwright aparentemente (ao menos não achei nada sobre) não possui um modo de watch, o plugin facilita a execução dos testes, e evita ficar usando o terminal o tempo todo.

  • Como pode ser visto na imagem abaixo, o plugin é bem completo, tem um menu lateral que agrega os testes, e ao abrir o arquivo ele mostra um ícone para executar aquele bloco de teste. E quando executa com sucesso, ele mostra um ícone de sucesso ao lado do teste.

    Captura de Tela 2023-03-21 às 09 49 48

    1. Rodando os testes via plugin

      Gravacao.de.Tela.2023-03-21.as.10.55.52.mov
    2. Simulando erro

      Gravacao.de.Tela.2023-03-21.as.11.11.48.mov

Problemas e Erros

  1. O locator getByRole() não encontra os links da home page, apesar de estarem acessíveis, e o Cypress ter conseguido localizá-los normalmente, tentei várias abordagens, mas não funciona. No lugar eu utilizei o .getByTestId() (não queria, mas foi o que funcionou).
  2. O matcher .tobeInViewport() não funcionou com as queries que eu estava fazendo, não sei se foi uso errado, ou se algo precisava ser configurado, no lugar usei .toBeDefined() (mas não sei sobre a eficacia).

Vitest

GHub

Pesquise repositórios e usuários do GitHub.

Objetivo

Motivação para criar o projeto

  • Este projeto foi criado como exemplo para uma decisão técnica sobre a escolha de ferramentas de teste.
  • Irei criar branchs separadas para testar diferentes implementações de bibliotecas de teste, como exemplo o Vitest, Cypress, Jest, Axios Mock Adapter, MSW, Playwright.

Tecnologias

O que foi utilizado neste exemplo?

Instalação [VITEST]

Como foi instalar o Vitest? Foi simples? Deu trabalho?

  • Passo a passo:
    • Instalar os pacotes

      npm i -D vitest @vitejs/plugin-react @testing-library/react @testing-library/dom @testing-library/user-event
    • Criar o arquivo de configuração vitest.config.ts:

      import react from '@vitejs/plugin-react'
      
      import { defineConfig } from 'vitest/config'
      
      export default defineConfig({
          plugins: [react()],
          test: {
              globals: true,
              environment: 'jsdom'
          }
      })
    • Configurar o tsconfig.json para enxergar os tipos globais, adicione a linha abaixo dentro do objeto compilerOptions

      "types": ["vitest/globals"]
    • Adicionar scripts no package.json

      {
          "test": "vitest",
          "coverage": "vitest run --coverage"
      }
    • Agora rode o comando

      npm run test
      • Vai ver o seguinte erro

        226350360-9018f07d-b04d-49db-832a-c35e5bba45d1

    • O vitest não vai conseguir entender os caminhos com aliases (@/...) que configuramos no init do Next, então precisamos adicionar seguinte trecho de código no objeto test do vitest.config.js pra resolver esse problema:

      alias: {
          '@': path.resolve(__dirname, './src')
      },
      • O arquivo deve ficar assim:

        import { defineConfig } from 'vitest/config'
        
        import path from 'path'
        import react from '@vitejs/plugin-react'
        
        export default defineConfig({
            plugins: [react()],
            test: {
                globals: true,
                environment: 'jsdom',
                alias: {
                    '@': path.resolve(__dirname, './src')
                },
            }
        })
    • Depois disso, tudo deve "funcionar", digo isso pois os problemas com o Vitest acabaram, mas temos que resolver os problemas com dependencias (esses problemas nos acompanham em qualquer framework). Como fizemos com o Jest, vamos fazer aqui, criar um arquivo com um customRender() que irá ter um Wrapper com os providers do projeto (React Query, NextRouter, etc) e o mock do NextRouter.

      Captura de Tela 2023-03-20 às 08 16 39

      // test/index.tsx
      
      import type { PropsWithChildren } from 'react'
      
      import user from '@testing-library/user-event'
      import { render as rtlRender, RenderOptions } from '@testing-library/react'
      
      import { NextRouter } from 'next/router'
      import { RouterContext } from 'next/dist/shared/lib/router-context'
      
      import { createRouterMock } from './mocks/createRouterMock'
      import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
      
      type TestWrapperProps = {
          router?: Partial<NextRouter>
      }
      type CustomRenderProps = {
          router: Partial<NextRouter>
          options?: Omit<RenderOptions, 'wrapper'>
      }
      
      const AllProviders = ({
          children,
          router = {}
      }: PropsWithChildren<TestWrapperProps>) => (
          <RouterContext.Provider value={createRouterMock(router)}>
              <QueryClientProvider client={new QueryClient()}>
                  {children}
              </QueryClientProvider>
          </RouterContext.Provider>
      )
      
      const customRender = (ui: JSX.Element, props?: CustomRenderProps) =>
          rtlRender(ui, {
              wrapper: ({ children }: PropsWithChildren) => (
                  <AllProviders router={props?.router}>{children}</AllProviders>
              ),
              ...props?.options
          })
      
      export * from '@testing-library/react'
      export { user, customRender }
      // test/mocks/createRouterMock.ts
      
      import { vi } from 'vitest'
      import { NextRouter } from 'next/router'
      
      export function createRouterMock(router: Partial<NextRouter>): NextRouter {
          return {
              route: '/',
              asPath: '/',
              basePath: '',
              pathname: '/',
              defaultLocale: 'en',
              query: {},
              domainLocales: [],
              back: vi.fn(),
              push: vi
                  .fn()
                  .mockImplementation((path: string) =>
                      window?.history?.pushState({}, 'Test', path)
                  ),
              reload: vi.fn(),
              replace: vi.fn(),
              forward: vi.fn(),
              prefetch: vi.fn(),
              beforePopState: vi.fn(),
              events: {
                  on: vi.fn(),
                  off: vi.fn(),
                  emit: vi.fn()
              },
              isReady: true,
              isPreview: false,
              isFallback: false,
              isLocaleDomain: false,
              ...router
          }
      }
      • Fique atento ao detalhe: eu troquei o jest.fn() pelo vi.fn() do Vitest.
    • Agora vou trocar o render do RTL no meu teste inicial pelo customRender e tudo funciona como deveria.

      Captura de Tela 2023-03-20 às 08 20 22

    • Começando a implementar os testes eu senti falta de alguns matchers que tinhamos no jest (através da @testing-library/jest-dom) e que faziam muito sentido, como o .toBeInTheDocument() ou o .toHaveValue(), e para isso vou instalar essa lib e fazer a configuração para poder usar esses e outros matchers do @testing-library/jest-dom

      • Instalar o pacote

        npm i -D @testing-library/jest-dom
      • Criar um arquivo chamado setupTests.ts dentro da pasta /test e importar o pacote dentro dele

        // test/setupTests.ts
        
        import '@testing-library/jest-dom'
      • Registrar o arquivo setupTests.ts no arquivo de configuração do vitest, dentro do objeto test:

        setupFiles: './test/setupTests.ts'
        • O arquivo deve ficar assim:

          import { defineConfig } from 'vitest/config'
          
          import path from 'path'
          import react from '@vitejs/plugin-react'
          
          export default defineConfig({
              plugins: [react()],
              test: {
                  globals: true,
                  environment: 'jsdom',
                  setupFiles: './test/setupTests.ts',
                  alias: {
                      '@': path.resolve(__dirname, './src')
                  },
              }
          })
    • Pronto, todos os nossos testes estão funcionando corretamente, inclusive, vou usar os mesmo testes que implementei no setup do jest.

      Captura de Tela 2023-03-20 às 10 13 27

Desenvolvimento dos testes

O que muda?O que notou? Como escrever testes com Vitest?

  • Uma das primeiras coisas que notei, foi que o Vitest não possui o matcher .toBeInTheDocument() que o @testing-library/jest-dom fornece e normalmente configuramos no Jest. Apesar de ser um método bem intuitivo, como o nome sugere, podemos viver sem ele, e trocá-lo pelo toBeDefined() que tem efeito similar.
  • Outro detalhe que vale citar, é que o Vitest não expõe os métodos describe, test/it, expect globalmente como o jest, ao invés disso precisamos importar nos arquivos de teste. Mas tem como fazer a configuração para que esses métodos fiquem disponíveis sem a necessidade de importa-los, e para isso só precisamos adicionar a chave/valor globals: true no arquivo de configuração do vitest e adicionar os tipos vitest/globals no types do tsconfig.json (como foi feito na configuração acima).
  • Os mocks funcionaram normalmente apenas trocando o objeto root (de jest para vi, ex: jest.fn() virou vi.fn())
  • Todos os testes que funcionavam no Jest, funcionaram normalmente no Vitest

Considerações finais

  • Como eu configurei o jest primeiro e o vitest depois, vou deixar registrado aqui minhas percepções.

  • Instalar o jest foi bem trabalhoso, e posso destacar os seguintes pontos ruins:

    • A doc do Jest não é precisa, ela só serve para projetos Vanilla (JS puro)
    • Tem muitos guias na doc oficial o que faz com que você encha seu projeto de configurações na esperança de que tudo funcione
    • Na internet tem milhares de guias, tutoriais, etc, mostrando como configurar, mas poucos deles funcionam, devido as variações com versões entre jest, babel, ts-node, etc
    • O jest precisa de muitos pacotes extras para funcionar, o que faz com que a instalação seja pautada por diversos passos e configurações
    • Arquivos de imagens, componentes do Next e outras coisas não são interpretados pelo jest o que faz com que você precise implementar mocks e stubs para que as coisas funcionem
  • O setup do vitest foi bem simples, ele funcionar out-of-box com TS fez com tudo ficasse bem mais simples, e não foi necessário implementar aquele monte de mocks e setups para o Next, ele rodou tranquilo.

  • A únicas configurações adicionais para o Vitest, foi o resolve de aliases para que entenda @/path e a adição dos matchers do @testing-library/jest-dom

Jest

GHub

Pesquise repositórios e usuários do GitHub.

Objetivo

Motivação para criar o projeto

  • Este projeto foi criado como exemplo para uma decisão técnica sobre a escolha de ferramentas de teste.
  • Irei criar branchs separadas para testar diferentes implementações de bibliotecas de teste, como exemplo o Vitest, Cypress, Jest, Axios Mock Adapter, MSW, Playwright.

Tecnologias

O que foi utilizado neste exemplo?

Instalação [JEST]

Como foi instalar o Jest? Muito trabalhoso?

  • Nota do autor

    • Não perca tempo seguindo os passos de instalação e setuo no site do Jest, não funciona, te induz ao erro, e faz você criar um monte de configuração em cima da inicial para ver se funciona e no final nem sabe mais o que fez dar certo.
  • Setup

    • Instale os seguintes pacotes

      • jest
      • ts-jest
      • react-test-renderer
      • @types/jest
      • @testing-library/react
      • @testing-library/user-event
      • @testing-library/jest/dom
      • @testing-library/dom
      npm i -D jest typescript ts-jest @types/jest react-test-renderer @testing-library/react @testing-library/user-event @testing-library/jest-dom @testing-library/dom
    • Crie o arquivo jest.config.js na raiz do projeto, com o seguinte conteúdo dentro:

      module.exports = {
          preset: 'ts-jest',
          testEnvironment: 'node'
      };
    • Adicione o comando de execução aos scripts do package.json:

      {
          "test": "jest",
          "test:watch": "jest --watch",
          "test:coverage": "jest --coverage",
      }
    • Após isso, ao tentar rodar os testes, vai rolar vários erros, erro de transformação de arquivos do ts-jest, erros com path absolute com aliases, etc, etc, porque o jest é bem chato de configurar. Então vamos lá:

      • Para corrigir o erro de transformação de JSX para o jest entender os componentes, vamos precisar sobrescrever uma regra jsx do tsconfig.json que o next mantém como preserve ao invés de react, para sobrescrever a regra, crie um arquivo chamado tsconfig.jest.json e adicione o seguinte trecho de código:
      {
          "extends": "./tsconfig.json",
          "compilerOptions": {
              "jsx": "react"
          }
      }
      
      • Esse json extende o tsconfig padrão e sobrescreve a rerga jsx que o next obriga ser preserve mas para o ts-jest funcionar precisa estar configrada como react.
    • Após criar o novo arquivo, você precisa indicar para o jest, que o ts-jest irá usá-lo ao invés do arquivo padrão, para isso, adicione o trecho abaixo no jest.config.js

      'ts-jest': {
          isolatedModules: true,
          tsconfig: 'tsconfig.jest.json'
      }
      • Logo após, o conteúdo do arquivo deve ficar assim:
      module.exports = {
          preset: 'ts-jest',
          testEnvironment: 'node',
          transform: {
              'ts-jest': {
                  isolatedModules: true,
                  tsconfig: 'tsconfig.jest.json'
              }
          }
      }
    • Agora vamos corrigir o problema com os caminhos absolutos, o jest não entende o a aliases @/ nos imports dos arquivos, para isso vamos criar uma entrada no objeto moduleNameMapper que irá converter os imports com @/ para o caminho real que aponta para o arquivo:

      moduleNameMapper: {
          '@/(.*)': '<rootDir>/src/$1'
      }
    • Outro problema: imagens. Precisamos transformar as imagens para que o jest entenda e renderize os teste. Para isso vamos adicionar um arquivo chamado fileTransformer.js na pasta test dentro da raiz do projeto, esse arquivo deve ter o seguinte código:

      const path = require('path')
      
      module.exports = {
          process(sourceText, sourcePath, options) {
              return {
                  code: `module.exports = ${JSON.stringify(
                      path.basename(sourcePath)
                  )};`
              }
          }
      }
    • Depois de criar esse arquivo, registre-o no jest.config.js:

      transform: {
          '^.+\\.tsx?$': [
              'ts-jest',
              {
                  isolatedModules: true,
                  tsconfig: 'tsconfig.jest.json'
              }
          ],
          // Adicione a linha abaixo
          '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
              '<rootDir>/test/fileTransformer.js'
      },
    • Depois de configurar tudo, ganhei uns vários erros do Next.js (router, next image, etc, muita coisa fora de ordem) e do React, e pra isso, foi necessário adicionar umas configurações adicionais no arquivo jest.setup.tsx:

      • Importar o react de maneira global

        import React from 'react'
        
        global.React = React
      • Importar o jest-dom para adicionar métodos de asserção ao expect

        import '@testing-library/jest-dom'
      • Importar e registrar os métodos do next para o jest

        const nextJest = require('next/jest')
        
        const createJestConfig = nextJest({ dir: './' })
        
        module.exports = createJestConfig()
      • Mockar o next router e image component

        jest.mock('next/image', () => ({
            __esModule: true,
            default: (props: any) => {
                // eslint-disable-next-line @next/next/no-img-element, jsx-a11y/alt-text
                return <img {...props} />
            }
        }))
        
        jest.mock('next/router', () => ({
            useRouter() {
                return {
                    route: '/',
                    pathname: '',
                    query: '',
                    asPath: '',
                    push: jest.fn(),
                    events: {
                        on: jest.fn(),
                        off: jest.fn()
                    },
                    beforePopState: jest.fn(() => null),
                    prefetch: jest.fn(() => null)
                }
            }
        }))
      • No final o arquivo deve ficar assim:

        /* Make react global to components inside jest */
        
        import React from 'react'
        
        global.React = React
        
        /* Add assertions methods */
        
        import '@testing-library/jest-dom'
        
        /* Next.js setup for Jest */
        
        const nextJest = require('next/jest')
        
        const createJestConfig = nextJest({ dir: './' })
        
        module.exports = createJestConfig()
        
        /* Mock Next components and router */
        
        jest.mock('next/image', () => ({
            __esModule: true,
            default: (props: any) => {
                // eslint-disable-next-line @next/next/no-img-element, jsx-a11y/alt-text
                return <img {...props} />
            }
        }))
        
        jest.mock('next/router', () => ({
            useRouter() {
                return {
                    route: '/',
                    pathname: '',
                    query: '',
                    asPath: '',
                    push: jest.fn(),
                    events: {
                        on: jest.fn(),
                        off: jest.fn()
                    },
                    beforePopState: jest.fn(() => null),
                    prefetch: jest.fn(() => null)
                }
            }
        }))
    • Após todo esse setup, consegui finalmente rodar um teste:

      import { render, screen } from '@testing-library/react'
      
      import { Navbar } from '@/components/Navbar'
      import { useRouter } from 'next/router'
      
      describe('Navbar', () => {
          const renderComponent = () => render(<Navbar />)
      
          test('Should render properly', async () => {
              renderComponent()
      
              const link = await screen.findByRole('link', { name: /GHub/i })
              const logo = await screen.findByRole('img', { name: /GHub logo/i })
              const searchInput = await screen.findByPlaceholderText('Pesquisar')
      
              expect(link).toBeInTheDocument()
              expect(searchInput).toBeInTheDocument()
          })
      })

      Primeiro teste

Considerações pós setup

Fiz mais algum setup? Precisou de ajustes? Como ficou?

Depois de todo o setup acima, eu comecei a escrever os testes de unidade, e como sempre, vários erros, ainda precisava de ajustes caso precisasse implementar os testes da maneira correta.

Um dos cenários que passei, foi testar componentes que usavam o hook useRouter(), no setup inicial, para os testes rodarem eu havia feito um mock do router, mas aquele mock limitava os testes, e para isso eu resolvi criar um arquivo de configuração para os testes, e com isso eu criei meu próprio render, com alguns detalhes a mais.

Esse arquivo que eu criei, possui alguns recursos, ele importa e re-exporta os recursos da RTL, e ele exporta um customRender como comentei.

Vamos ao arquivo (criado em raiz do projeto > /test/index.tsx)

// test/index.tsx

import type { PropsWithChildren } from 'react'

import { render as rtlRender, RenderOptions } from '@testing-library/react'
import user from '@testing-library/user-event'

import { RouterContext } from 'next/dist/shared/lib/router-context'

import { createRouterMock } from './mocks/createRouterMock'
import { NextRouter } from 'next/router'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'

type TestWrapperProps = {
    router?: Partial<NextRouter>
}
type CustomRenderProps = {
    router: Partial<NextRouter>
    options?: Omit<RenderOptions, 'wrapper'>
}

const AllProviders = ({
    children,
    router = {}
}: PropsWithChildren<TestWrapperProps>) => (
    <RouterContext.Provider value={createRouterMock(router)}>
        <QueryClientProvider client={new QueryClient()}>
            {children}
        </QueryClientProvider>
    </RouterContext.Provider>
)

const customRender = (ui: JSX.Element, props?: CustomRenderProps) =>
    rtlRender(ui, {
        wrapper: ({ children }: PropsWithChildren) => (
            <AllProviders router={props?.router}>{children}</AllProviders>
        ),
        ...props?.options
    })

export * from '@testing-library/react'
export { user, customRender }

O trecho a seguir, é usado para fazer o registro de todos os providers/contexts que tivermos no projeto, para que os testes e hooks funcionem corretamente (como se fosse a aplicação real rodando). Nesse exemplo eu passei o provider do roteador do Next.js e o provider do React query, para que os hooks useQuery dentro da app funcionem corretamente.

const AllProviders = ({
    children,
    router = {}
}: PropsWithChildren<TestWrapperProps>) => (
    <RouterContext.Provider value={createRouterMock(router)}>
        <QueryClientProvider client={new QueryClient()}>
            {children}
        </QueryClientProvider>
    </RouterContext.Provider>
)

Poderíamos mockar esses contexts/providers, mas nem sempre queremos mockar as coisas, e nesse caso eu quero que a aplicação funcione normalmente como se estivesse sendo usado real.

Sobre o customRender(), ele é bem simples. Uma função que retorna o render da Testing Library, mas com algumas opções no objeto, o primeiro parâmetro (ui), é o componente que eu quero testar, e depois eu passo um objeto, e preencho a chave wrapper que recebe uma função passando um children (nosso componente passado na ui) que será injetado dentro do AllProviders e receberá todos os contextos e recursos necessários para funcionar. E adicionalmente eu recebo as outras options do RTL render para caso eu queira fazer alguma customização dentro do arquivo de teste, eu tenho acesso a interface.

const customRender = (ui: JSX.Element, props?: CustomRenderProps) =>
    rtlRender(ui, {
        wrapper: ({ children }: PropsWithChildren) => (
            <AllProviders router={props?.router}>{children}</AllProviders>
        ),
        ...props?.options
    })

O restante do arquivo é somente import e reexport dos recursos.

Vamos ao mock do next router, e aqui não tem nada de mais, é só uma factory simples:

// test/mocks/createRouterMock.ts

import { NextRouter } from 'next/router'

export function createRouterMock(router: Partial<NextRouter>): NextRouter {
    return {
        route: '/',
        asPath: '/',
        basePath: '',
        pathname: '/',
        defaultLocale: 'en',
        query: {},
        domainLocales: [],
        back: jest.fn(),
        push: jest
            .fn()
            .mockImplementation((path: string) =>
                window?.history?.pushState({}, 'Test', path)
            ),
        reload: jest.fn(),
        replace: jest.fn(),
        forward: jest.fn(),
        prefetch: jest.fn(),
        beforePopState: jest.fn(),
        events: {
            on: jest.fn(),
            off: jest.fn(),
            emit: jest.fn()
        },
        isReady: true,
        isPreview: false,
        isFallback: false,
        isLocaleDomain: false,
        ...router
    }
}

Tempo total dos testes

Após implementação dos testes

Captura de Tela 2023-03-17 às 16 49 45

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.