Ctrl K
ComunidadGuías…

agents-manage-ui/cypress/cypress.config.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { createHash } from 'node:crypto';
import fs from 'node:fs/promises';
import { defineConfig } from 'cypress';

try {
  process.loadEnvFile('../../.env');
} catch {
  // In CI, setup-dev generates .env and exports values to $GITHUB_ENV
}

export default defineConfig({
  experimentalFastVisibility: true,
  experimentalMemoryManagement: true,
  numTestsKeptInMemory: 0,
  defaultBrowser: 'chrome',
  waitForAnimations: false,
  retries: {
    runMode: 2,
    openMode: 0,
  },
  env: {
    TEST_USER_EMAIL: process.env.INKEEP_AGENTS_MANAGE_UI_USERNAME,
    TEST_USER_PASSWORD: process.env.INKEEP_AGENTS_MANAGE_UI_PASSWORD,
    BYPASS_SECRET: process.env.INKEEP_AGENTS_MANAGE_API_BYPASS_SECRET,
    API_URL: process.env.INKEEP_AGENTS_API_URL || 'http://localhost:3002',
  },
  e2e: {
    video: true,
    baseUrl: `http://localhost:${process.env.MANAGE_UI_PORT || 3000}`,
    viewportWidth: 1_440,
    viewportHeight: 900,
    defaultCommandTimeout: 15_000,
    setupNodeEvents(on, _config) {
      on('task', {
        log(message: string) {
          console.log(message);
          return null;
        },
        solvePow(challenge: {
          algorithm: string;
          challenge: string;
          maxnumber: number;
          salt: string;
          signature: string;
        }): string {
          const algo = challenge.algorithm.replace('-', '').toLowerCase();
          for (let n = 0; n <= challenge.maxnumber; n++) {
            const hash = createHash(algo).update(`${challenge.salt}${n}`).digest('hex');
            if (hash === challenge.challenge) {
              return Buffer.from(
                JSON.stringify({
                  algorithm: challenge.algorithm,
                  challenge: challenge.challenge,
                  number: n,
                  salt: challenge.salt,
                  signature: challenge.signature,
                })
              ).toString('base64');
            }
          }
          throw new Error('PoW solve exhausted maxnumber');
        },
        async chatSSE(params: {
          url: string;
          headers: Record<string, string>;
          body: string;
        }): Promise<{ text: string; conversationId: string }> {
          const res = await fetch(params.url, {
            method: 'POST',
            headers: params.headers,
            body: params.body,
          });
          if (res.status !== 200) {
            const text = await res.text();
            throw new Error(`Chat failed (${res.status}): ${text.slice(0, 500)}`);
          }
          const raw = await res.text();
          let fullText = '';
          let convId = '';
          for (const line of raw.split('\n')) {
            if (!line.startsWith('data: ') || line === 'data: [DONE]') continue;
            try {
              const chunk = JSON.parse(line.slice(6));
              const delta = chunk.choices?.[0]?.delta;
              if (delta?.content) {
                if (delta.content.startsWith('{"type":"data-operation"')) {
                  try {
                    const op = JSON.parse(delta.content);
                    if (op.data?.details?.conversationId) convId = op.data.details.conversationId;
                  } catch {
                    /* ignore */
                  }
                } else {
                  fullText += delta.content;
                }
              }
              if (chunk.conversationId) convId = chunk.conversationId;
              if (chunk.id && !convId) convId = chunk.id;
              const dataOp = chunk.data ?? chunk;
              if (dataOp?.type === 'completion_complete' && dataOp?.details?.conversationId) {
                convId = dataOp.details.conversationId;
              }
            } catch {
              /* skip */
            }
          }
          if (!fullText) throw new Error(`Empty agent response. Raw: ${raw.slice(0, 500)}`);
          return { text: fullText, conversationId: convId };
        },
      });

      on('before:browser:launch', (browser, launchOptions) => {
        if (browser.family === 'chromium' && browser.isHeadless) {
          launchOptions.args.push(
            '--no-sandbox',
            '--disable-features=IsolateOrigins,site-per-process',
            '--disable-extensions',
            '--disable-translate',
            '--mute-audio'
          );
        }
        return launchOptions;
      });

      /**
       * Only keep failed videos
       * @see https://docs.cypress.io/app/guides/screenshots-and-videos#Delete-videos-for-specs-without-failing-or-retried-tests
       */
      on('after:spec', async (_spec, results) => {
        if (!results?.video) {
          return;
        }
        const failures = results.tests?.some((test) =>
          test.attempts?.some((attempt) => attempt.state === 'failed')
        );
        if (failures) {
          return;
        }
        await fs.rm(results.video, { force: true });
        await fs.rm(results.video.replace('.mp4', '-compressed.mp4'), { force: true });
      });
    },
  },
});