--- title: Testes description: Configuração do Vitest, testes de integração contra um servidor Twenty real, verificação de tipos e CI com GitHub Actions. icon: flask --- O SDK fornece APIs programáticas que permitem compilar, implantar, instalar e desinstalar seu aplicativo a partir de código de teste. Em conjunto com [Vitest](https://vitest.dev/) e os clientes de API tipados, você pode escrever testes de integração que verificam que seu aplicativo funciona de ponta a ponta em um servidor Twenty real. ## Usando pacotes npm Você pode instalar e usar qualquer pacote npm no seu app. Tanto funções lógicas quanto componentes de front-end são empacotados com [esbuild](https://esbuild.github.io/), que incorpora todas as dependências na saída — nenhum `node_modules` é necessário em tempo de execução. ### Instalando um pacote ```bash filename="Terminal" yarn add axios ``` Em seguida, importe-o no seu código: ```ts src/logic-functions/fetch-data.ts import { defineLogicFunction } from 'twenty-sdk/define'; import axios from 'axios'; const handler = async (): Promise => { const { data } = await axios.get('https://api.example.com/data'); return { data }; }; export default defineLogicFunction({ universalIdentifier: '...', name: 'fetch-data', description: 'Fetches data from an external API', timeoutSeconds: 10, handler, }); ``` O mesmo vale para componentes de front-end: ```tsx src/front-components/chart.tsx import { defineFrontComponent } from 'twenty-sdk/define'; import { format } from 'date-fns'; const DateWidget = () => { return

Today is {format(new Date(), 'MMMM do, yyyy')}

; }; export default defineFrontComponent({ universalIdentifier: '...', name: 'date-widget', component: DateWidget, }); ``` ### Como o empacotamento funciona A etapa de build usa o esbuild para produzir um único arquivo independente por função lógica e por componente de front-end. Todos os pacotes importados são incorporados ao bundle. **Funções lógicas** são executadas em um ambiente Node.js. Módulos nativos do Node (`fs`, `path`, `crypto`, `http`, etc.) estão disponíveis e não precisam ser instalados. **Componentes de front-end** são executados em um Web Worker. Módulos nativos do Node **não** estão disponíveis — apenas APIs do navegador e pacotes npm que funcionam em um ambiente de navegador. Ambos os ambientes têm `twenty-client-sdk/core` e `twenty-client-sdk/metadata` disponíveis como módulos pré-fornecidos — eles não são empacotados, mas resolvidos em tempo de execução pelo servidor. ## Configuração O aplicativo gerado pelo scaffolder já inclui o Vitest. Se você configurá-lo manualmente, instale as dependências: ```bash filename="Terminal" yarn add -D vitest vite-tsconfig-paths ``` Crie um `vitest.config.ts` na raiz do seu aplicativo: ```ts vitest.config.ts import tsconfigPaths from 'vite-tsconfig-paths'; import { defineConfig } from 'vitest/config'; export default defineConfig({ plugins: [ tsconfigPaths({ projects: ['tsconfig.spec.json'], ignoreConfigErrors: true, }), ], test: { testTimeout: 120_000, hookTimeout: 120_000, include: ['src/**/*.integration-test.ts'], setupFiles: ['src/__tests__/setup-test.ts'], env: { TWENTY_API_URL: 'http://localhost:2020', TWENTY_API_KEY: 'your-api-key', }, }, }); ``` Crie um arquivo de configuração que verifique se o servidor está acessível antes da execução dos testes: ```ts src/__tests__/setup-test.ts import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; import { beforeAll } from 'vitest'; const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:2020'; const TEST_CONFIG_DIR = path.join(os.tmpdir(), '.twenty-sdk-test'); beforeAll(async () => { // Verify the server is running const response = await fetch(`${TWENTY_API_URL}/healthz`); if (!response.ok) { throw new Error( `Twenty server is not reachable at ${TWENTY_API_URL}. ` + 'Start the server before running integration tests.', ); } // Write a temporary config for the SDK fs.mkdirSync(TEST_CONFIG_DIR, { recursive: true }); fs.writeFileSync( path.join(TEST_CONFIG_DIR, 'config.json'), JSON.stringify({ remotes: { local: { apiUrl: process.env.TWENTY_API_URL, apiKey: process.env.TWENTY_API_KEY, }, }, defaultRemote: 'local', }, null, 2), ); }); ``` ## APIs programáticas do SDK O subcaminho `twenty-sdk/cli` exporta funções que você pode chamar diretamente a partir do código de teste: | Função | Descrição | | -------------- | ------------------------------------------------------------ | | `appBuild` | Compilar o aplicativo e, opcionalmente, empacotar um tarball | | `appDeploy` | Enviar um tarball para o servidor | | `appInstall` | Instalar o aplicativo no espaço de trabalho ativo | | `appUninstall` | Desinstalar o aplicativo do espaço de trabalho ativo | Cada função retorna um objeto de resultado com `success: boolean` e `data` ou `error`. ## Escrevendo um teste de integração Aqui está um exemplo completo que compila, implanta e instala o aplicativo e, em seguida, verifica se ele aparece no espaço de trabalho: ```ts src/__tests__/app-install.integration-test.ts import { APPLICATION_UNIVERSAL_IDENTIFIER } from 'src/application-config'; import { appBuild, appDeploy, appInstall, appUninstall } from 'twenty-sdk/cli'; import { MetadataApiClient } from 'twenty-client-sdk/metadata'; import { afterAll, beforeAll, describe, expect, it } from 'vitest'; const APP_PATH = process.cwd(); describe('App installation', () => { beforeAll(async () => { const buildResult = await appBuild({ appPath: APP_PATH, tarball: true, onProgress: (message: string) => console.log(`[build] ${message}`), }); if (!buildResult.success) { throw new Error(`Build failed: ${buildResult.error?.message}`); } const deployResult = await appDeploy({ tarballPath: buildResult.data.tarballPath!, onProgress: (message: string) => console.log(`[deploy] ${message}`), }); if (!deployResult.success) { throw new Error(`Deploy failed: ${deployResult.error?.message}`); } const installResult = await appInstall({ appPath: APP_PATH }); if (!installResult.success) { throw new Error(`Install failed: ${installResult.error?.message}`); } }); afterAll(async () => { await appUninstall({ appPath: APP_PATH }); }); it('should find the installed app in the workspace', async () => { const metadataClient = new MetadataApiClient(); const result = await metadataClient.query({ findManyApplications: { id: true, name: true, universalIdentifier: true, }, }); const installedApp = result.findManyApplications.find( (app: { universalIdentifier: string }) => app.universalIdentifier === APPLICATION_UNIVERSAL_IDENTIFIER, ); expect(installedApp).toBeDefined(); }); }); ``` ## Executando testes Certifique-se de que seu servidor Twenty local esteja em execução e, em seguida: ```bash filename="Terminal" yarn test ``` Ou no modo watch durante o desenvolvimento: ```bash filename="Terminal" yarn test:watch ``` ## Verificação de tipos Você também pode executar a verificação de tipos no seu aplicativo sem executar os testes: ```bash filename="Terminal" yarn twenty typecheck ``` Isso executa `tsc --noEmit` e informa quaisquer erros de tipo. ## CI com GitHub Actions O gerador de scaffold cria um workflow do GitHub Actions pronto para uso em `.github/workflows/ci.yml`. Ele executa seus testes de integração automaticamente a cada push para `main` e em pull requests. O workflow: 1. Faz checkout do seu código 2. Inicializa um servidor Twenty temporário usando a ação `twentyhq/twenty/.github/actions/spawn-twenty-docker-image` 3. Instala as dependências com `yarn install --immutable` 4. Executa `yarn test` com `TWENTY_API_URL` e `TWENTY_API_KEY` injetados a partir das saídas da ação ```yaml .github/workflows/ci.yml name: CI on: push: branches: - main pull_request: {} env: TWENTY_VERSION: latest jobs: test: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Spawn Twenty instance id: twenty uses: twentyhq/twenty/.github/actions/spawn-twenty-docker-image@main with: twenty-version: ${{ env.TWENTY_VERSION }} github-token: ${{ secrets.GITHUB_TOKEN }} - name: Enable Corepack run: corepack enable - name: Setup Node.js uses: actions/setup-node@v4 with: node-version-file: '.nvmrc' cache: 'yarn' - name: Install dependencies run: yarn install --immutable - name: Run integration tests run: yarn test env: TWENTY_API_URL: ${{ steps.twenty.outputs.server-url }} TWENTY_API_KEY: ${{ steps.twenty.outputs.access-token }} ``` Você não precisa configurar nenhum segredo — a ação `spawn-twenty-docker-image` inicia um servidor Twenty efêmero diretamente no runner e fornece os detalhes de conexão. O segredo `GITHUB_TOKEN` é fornecido automaticamente pelo GitHub. Para fixar uma versão específica do Twenty em vez de `latest`, altere a variável de ambiente `TWENTY_VERSION` no topo do workflow.