> ## Documentation Index
> Fetch the complete documentation index at: https://twenty-c--pull-3-cli.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Tests

> Vitest-Setup, Integrationstests gegen einen realen Twenty-Server, Typprüfung und CI mit GitHub Actions.

Das SDK stellt programmgesteuerte APIs bereit, mit denen Sie Ihre App aus Testcode heraus bauen, bereitstellen, installieren und deinstallieren können. In Kombination mit [Vitest](https://vitest.dev/) und den typisierten API-Clients können Sie Integrationstests schreiben, die prüfen, dass Ihre App End-to-End gegen einen echten Twenty-Server funktioniert.

## Verwendung von npm-Paketen

Sie können in Ihrer App beliebige npm-Pakete installieren und verwenden. Sowohl Logikfunktionen als auch Frontend-Komponenten werden mit [esbuild](https://esbuild.github.io/) gebündelt, das deren Abhängigkeiten in die Ausgabe einbettet — zur Laufzeit sind keine `node_modules` erforderlich.

### Ein Paket installieren

```bash filename="Terminal" theme={null}
yarn add axios
```

Importieren Sie es anschließend in Ihrem Code:

```ts src/logic-functions/fetch-data.ts theme={null}
import { defineLogicFunction } from 'twenty-sdk/define';
import axios from 'axios';

const handler = async (): Promise<any> => {
  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,
});
```

Dasselbe funktioniert für Frontend-Komponenten:

```tsx src/front-components/chart.tsx theme={null}
import { defineFrontComponent } from 'twenty-sdk/define';
import { format } from 'date-fns';

const DateWidget = () => {
  return <p>Today is {format(new Date(), 'MMMM do, yyyy')}</p>;
};

export default defineFrontComponent({
  universalIdentifier: '...',
  name: 'date-widget',
  component: DateWidget,
});
```

### Wie das Bundling funktioniert

Der Build-Schritt verwendet esbuild, um pro Logikfunktion und pro Frontend-Komponente eine einzelne, in sich geschlossene Datei zu erzeugen. Importierte Pakete werden in das Bundle eingebettet, mit Ausnahme der unten aufgeführten.

**Logikfunktionen** laufen in einer Node.js-Umgebung. Eingebaute Node.js-Module (`fs`, `path`, `crypto`, `http` usw.) stehen zur Verfügung und müssen nicht installiert werden.

**Frontend-Komponenten** laufen in einem Web Worker. Eingebaute Node.js-Module sind **nicht** verfügbar — nur npm-Pakete, die in einer Browserumgebung funktionieren. Beachte, dass die Sandbox nur ein *teilweises* DOM implementiert, sodass ein Paket sauber gebaut werden kann und trotzdem zur Laufzeit fehlschlägt; siehe [Aktuelle Einschränkungen](/l/de/developers/extend/apps/layout/front-components#current-limitations).

In beiden Umgebungen stehen `twenty-client-sdk/core` und `twenty-client-sdk/metadata` als vorab bereitgestellte Module zur Verfügung — sie werden nicht gebündelt, sondern zur Laufzeit vom Server aufgelöst.

Frontend-Komponenten können Abhängigkeiten auch über ein [Bundle für gemeinsame Abhängigkeiten](/l/de/developers/extend/apps/layout/front-components#sharing-dependencies-across-front-components) teilen. Pakete, die im Feld `frontComponentSharedDependencies` der package.json der App aufgeführt sind, werden einmal in eine separate Datei gebaut und zur Laufzeit daraus geladen, anstatt in jede Komponente eingebettet zu werden. Dadurch enthält eine Komponente, die nur gemeinsame Pakete importiert, fast keinen eigenen Abhängigkeitscode.

## Einrichtung

Die erzeugte App enthält bereits Vitest. Wenn Sie es manuell einrichten, installieren Sie die Abhängigkeiten:

```bash filename="Terminal" theme={null}
yarn add -D vitest vite-tsconfig-paths
```

Erstellen Sie eine `vitest.config.ts` im Stammverzeichnis Ihrer App:

```ts vitest.config.ts theme={null}
import tsconfigPaths from 'vite-tsconfig-paths';
import { defineConfig } from 'vitest/config';

const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:2020';
const TWENTY_API_KEY = process.env.TWENTY_API_KEY ?? '<the pre-seeded local dev key>';

// Make env vars available to globalSetup (test.env only applies to workers)
process.env.TWENTY_API_URL = TWENTY_API_URL;
process.env.TWENTY_API_KEY = TWENTY_API_KEY;

export default defineConfig({
  plugins: [
    tsconfigPaths({
      projects: ['tsconfig.spec.json'],
      ignoreConfigErrors: true,
    }),
  ],
  test: {
    testTimeout: 120_000,
    hookTimeout: 120_000,
    fileParallelism: false,
    include: ['src/**/*.integration-test.ts'],
    globalSetup: ['src/__tests__/global-setup.ts'],
    env: {
      TWENTY_API_URL,
      TWENTY_API_KEY,
    },
  },
});
```

Erstellen Sie eine globale Setup-Datei, die überprüft, ob der Server erreichbar ist, eine Testkonfiguration für das SDK schreibt (`~/.twenty/config.test.json`) und die App synchronisiert, bevor die Tests ausgeführt werden:

```ts src/__tests__/global-setup.ts theme={null}
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';

import { appDevOnce, appUninstall } from 'twenty-sdk/cli';

const APP_PATH = process.cwd();
const CONFIG_DIR = path.join(os.homedir(), '.twenty');

export async function setup() {
  const apiUrl = process.env.TWENTY_API_URL!;
  const apiKey = process.env.TWENTY_API_KEY!;

  // Verify the server is running
  const response = await fetch(`${apiUrl}/healthz`);
  if (!response.ok) {
    throw new Error(`Twenty server is not reachable at ${apiUrl}.`);
  }

  // Write the SDK's test config (the CLI reads config.test.json when NODE_ENV=test)
  fs.mkdirSync(CONFIG_DIR, { recursive: true });
  fs.writeFileSync(
    path.join(CONFIG_DIR, 'config.test.json'),
    JSON.stringify({
      remotes: { local: { apiUrl, apiKey } },
      defaultRemote: 'local',
    }, null, 2),
  );

  // Start from a clean slate, then sync the app
  await appUninstall({ appPath: APP_PATH }).catch(() => {});

  const result = await appDevOnce({ appPath: APP_PATH });
  if (!result.success) {
    throw new Error(`Dev sync failed: ${result.error?.message}`);
  }
}

export async function teardown() {
  await appUninstall({ appPath: APP_PATH });
}
```

## Programmgesteuerte SDK-APIs

Der Subpfad `twenty-sdk/cli` exportiert Funktionen, die Sie direkt aus Testcode aufrufen können:

| Funktion       | Beschreibung                                                                |
| -------------- | --------------------------------------------------------------------------- |
| `appBuild`     | Die App bauen und optional ein Tarball erstellen                            |
| `appDeploy`    | Ein Tarball auf den Server hochladen                                        |
| `appDevOnce`   | Erstellt und synchronisiert die App einmal (entspricht `yarn twenty apply`) |
| `appInstall`   | Die App im aktiven Arbeitsbereich installieren                              |
| `appUninstall` | Die App aus dem aktiven Arbeitsbereich deinstallieren                       |

Jede Funktion gibt ein Ergebnisobjekt mit `success: boolean` und entweder `data` oder `error` zurück.

## Einen Integrationstest schreiben

Hier ist ein vollständiges Beispiel, das die App baut, bereitstellt und installiert und anschließend prüft, dass sie im Arbeitsbereich erscheint:

```ts src/__tests__/app-install.integration-test.ts theme={null}
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();
  });
});
```

## Tests ausführen

Stellen Sie sicher, dass Ihr lokaler Twenty-Server läuft, und führen Sie dann Folgendes aus:

```bash filename="Terminal" theme={null}
yarn test
```

Oder im Watch-Modus während der Entwicklung:

```bash filename="Terminal" theme={null}
yarn test:watch
```

## Typprüfung

Sie können die Typprüfung Ihrer App auch ohne Tests ausführen:

```bash filename="Terminal" theme={null}
yarn twenty dev:typecheck
```

Dies führt `tsc --noEmit` unter Verwendung der `tsconfig.json` Ihrer App aus und meldet etwaige Typfehler. Vom Scaffolding-Tool erzeugte Apps liefern außerdem ein `yarn typecheck`-Skript mit, das auch Testdateien abdeckt (`tsconfig.spec.json`).

## CI mit GitHub Actions

Das Scaffolding-Tool erzeugt einen einsatzbereiten Workflow unter `.github/workflows/ci.yml`. Bei jedem Push auf `main` und jeder Pull-Request startet es einen kurzlebigen Twenty-Server im Runner (über die Aktion `twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test`) und führt anschließend `yarn lint`, `yarn typecheck`, `yarn test:unit` und `yarn test` aus, wobei `TWENTY_API_URL` / `TWENTY_API_KEY` auf diesen Server verweisen. Es sind keine Geheimnisse erforderlich, und Sie können die Serverversion über die Umgebungsvariable `TWENTY_VERSION` oben im Workflow fixieren.

Unter [Veröffentlichen → Automatisiertes CI/CD](/l/de/developers/extend/apps/operations/publishing#automated-cicd-scaffolded-workflows) finden Sie eine vollständige Schritt-für-Schritt-Anleitung zu den drei eingerichteten Workflows (`ci.yml`, der `cd.yml`-Bereitstellungspipeline und `publish.yml` für die npm-Veröffentlichung).
