Ctrl K
ComunidadGuías…

scripts/e2e-diagram-scope.spec.ts

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

Archivo original ↗Preguntar a KiBot
import { describe, it, expect } from 'vitest';
import { detectScope, SPEC_BASE_DIR, DIAGRAMS_DIR, SKIP } from './e2e-diagram-scope.mjs';

// Source folders mix kebab/camelCase; fixture folders are kebab. Compare on a
// canonical lowercased, hyphen-stripped name.
const canonical = (name: string) => name.toLowerCase().replace(/-/g, '');

// The tests run in the repo root, so the spec subfolders created by the file
// reorganisation are present on disk — no mocking needed.

describe('detectScope', () => {
  it('returns empty string for no changed files', () => {
    expect(detectScope([])).toBe('');
  });

  it('scopes to a single diagram when only that diagram changed', () => {
    const result = detectScope([
      'packages/mermaid/src/diagrams/flowchart/flowchartDb.ts',
      'packages/mermaid/src/diagrams/flowchart/flowchartRenderer.ts',
    ]);
    expect(result.split(',')).toEqual(
      expect.arrayContaining([
        `${SPEC_BASE_DIR}/flowchart/`,
        `${SPEC_BASE_DIR}/mmd-snapshots.spec.ts`,
      ])
    );
  });

  it('returns patterns that Playwright can compile as regular expressions', () => {
    const result = detectScope(['packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts']);

    for (const pattern of result.split(',')) {
      expect(() => new RegExp(pattern, 'gi')).not.toThrow();
    }
  });

  it('falls back to full suite when rendering-util is touched', () => {
    expect(
      detectScope([
        'packages/mermaid/src/diagrams/flowchart/flowchartDb.ts',
        'packages/mermaid/src/rendering-util/edgeDetails.ts',
      ])
    ).toBe('');
  });

  it('falls back to full suite when themes are touched', () => {
    expect(detectScope(['packages/mermaid/src/themes/theme-default.js'])).toBe('');
  });

  it('falls back to full suite when config is touched', () => {
    expect(detectScope(['packages/mermaid/src/config.ts'])).toBe('');
  });

  it('falls back to full suite when diagrams/common is touched', () => {
    expect(detectScope(['packages/mermaid/src/diagrams/common/commonDb.ts'])).toBe('');
  });

  it('falls back to full suite when parser package is touched', () => {
    expect(detectScope(['packages/parser/src/language/flowchart.langium'])).toBe('');
  });

  it('falls back to full suite when CI config is touched', () => {
    expect(detectScope(['.github/workflows/e2e.yml'])).toBe('');
  });

  it('falls back to full suite when package.json is touched', () => {
    expect(detectScope(['package.json'])).toBe('');
  });

  it('combines patterns from two different diagrams', () => {
    const result = detectScope([
      'packages/mermaid/src/diagrams/gantt/ganttDb.ts',
      'packages/mermaid/src/diagrams/pie/pieDb.ts',
    ]);
    expect(result.split(',')).toEqual(
      expect.arrayContaining([
        `${SPEC_BASE_DIR}/gantt/`,
        `${SPEC_BASE_DIR}/pie/`,
        `${SPEC_BASE_DIR}/mmd-snapshots.spec.ts`,
      ])
    );
  });

  it('scopes a fixture-only change to the mmd snapshot runner (not the full suite)', () => {
    const result = detectScope(['e2e/diagrams/pie/new-case.mmd']);
    expect(result).toBe(`${SPEC_BASE_DIR}/mmd-snapshots.spec.ts`);
  });

  it('includes the mmd runner when a fixture changes alongside a diagram source', () => {
    const result = detectScope([
      'packages/mermaid/src/diagrams/flowchart/flowchartDb.ts',
      'e2e/diagrams/flowchart/new-case.mmd',
    ]);
    expect(result.split(',')).toEqual(
      expect.arrayContaining([
        `${SPEC_BASE_DIR}/flowchart/`,
        `${SPEC_BASE_DIR}/mmd-snapshots.spec.ts`,
      ])
    );
  });

  it('still falls back to full suite when shared code changes alongside a fixture', () => {
    expect(
      detectScope([
        'packages/mermaid/src/rendering-util/edgeDetails.ts',
        'e2e/diagrams/flowchart/new-case.mmd',
      ])
    ).toBe('');
  });

  it('falls back to full suite for a diagram with no spec subfolder', () => {
    // Use a custom specBaseDir that has no subfolder for 'unknownDiagram'
    expect(
      detectScope(['packages/mermaid/src/diagrams/unknownDiagram/unknownDb.ts'], {
        specBaseDir: SPEC_BASE_DIR,
      })
    ).toBe('');
  });

  it('scopes to the subfolder when a spec file in that subfolder is modified', () => {
    const result = detectScope([`${SPEC_BASE_DIR}/gantt/gantt.spec.js`]);
    expect(result.split(',')).toEqual(
      expect.arrayContaining([`${SPEC_BASE_DIR}/gantt/`, `${SPEC_BASE_DIR}/mmd-snapshots.spec.ts`])
    );
  });

  it('falls back to full suite for any spec at the rendering root (positional convention)', () => {
    // Any *.spec.* at the root of SPEC_BASE_DIR is treated as cross-cutting —
    // no explicit list needed.
    expect(detectScope([`${SPEC_BASE_DIR}/theme.spec.js`])).toBe('');
    expect(detectScope([`${SPEC_BASE_DIR}/brandNewCrossCutting.spec.ts`])).toBe('');
  });

  it('falls back to full suite when an e2e/other spec is modified', () => {
    expect(detectScope(['e2e/other/pr/xss.spec.js'])).toBe('');
  });

  it('deduplicates when diagram source and its spec subfolder both change', () => {
    const result = detectScope([
      'packages/mermaid/src/diagrams/gantt/ganttDb.ts',
      `${SPEC_BASE_DIR}/gantt/gantt.spec.js`,
    ]);
    // Both point to the same subfolder — should deduplicate
    expect(result.split(',').filter((s) => s === `${SPEC_BASE_DIR}/gantt/`).length).toBe(1);
  });
});

describe('ignorable files (docs-only, changesets, etc.)', () => {
  it('returns SKIP when only docs source files change', () => {
    expect(
      detectScope([
        'packages/mermaid/src/docs/syntax/flowchart.md',
        'packages/mermaid/src/docs/config/theming.md',
      ])
    ).toBe(SKIP);
  });

  it('returns SKIP when only root markdown files change', () => {
    expect(detectScope(['README.md', 'CONTRIBUTING.md'])).toBe(SKIP);
  });

  it('returns SKIP when only changeset files change', () => {
    expect(detectScope(['.changeset/cool-feature.md'])).toBe(SKIP);
  });

  it('returns SKIP when only generated docs change', () => {
    expect(detectScope(['docs/syntax/flowchart.md', 'docs/intro/index.md'])).toBe(SKIP);
  });

  it('returns SKIP when only AI/assistant config changes', () => {
    expect(detectScope(['.claude/settings.json', 'assistant/CONVENTIONS.md'])).toBe(SKIP);
  });

  it('returns SKIP when only docs CI workflows change', () => {
    expect(
      detectScope(['.github/workflows/build-docs.yml', '.github/workflows/publish-docs.yml'])
    ).toBe(SKIP);
  });

  it('returns SKIP when only demo files change', () => {
    expect(detectScope(['demos/architecture.html'])).toBe(SKIP);
  });

  it('scopes to diagram when diagram source + file in demo folder changed', () => {
    const result = detectScope([
      'packages/mermaid/src/diagrams/flowchart/flowchartDb.ts',
      'demos/sequence.html',
    ]);
    expect(result.split(',')).toEqual(
      expect.arrayContaining([
        `${SPEC_BASE_DIR}/flowchart/`,
        `${SPEC_BASE_DIR}/mmd-snapshots.spec.ts`,
      ])
    );
  });

  it('scopes to diagram when diagram source + docs both change', () => {
    const result = detectScope([
      'packages/mermaid/src/diagrams/flowchart/flowchartDb.ts',
      'packages/mermaid/src/docs/syntax/flowchart.md',
    ]);
    expect(result.split(',')).toEqual(
      expect.arrayContaining([
        `${SPEC_BASE_DIR}/flowchart/`,
        `${SPEC_BASE_DIR}/mmd-snapshots.spec.ts`,
      ])
    );
  });

  it('falls back to full suite when shared code + docs both change', () => {
    expect(
      detectScope([
        'packages/mermaid/src/rendering-util/edgeDetails.ts',
        'packages/mermaid/src/docs/syntax/flowchart.md',
      ])
    ).toBe('');
  });

  it('falls back to full suite for non-docs CI workflows', () => {
    // e2e.yml affects test execution — not ignorable
    expect(detectScope(['.github/workflows/e2e.yml'])).toBe('');
  });
});

describe('diagram coverage', () => {
  it('every known diagram has either a rendering spec subfolder or mmd fixtures', async () => {
    const fs = await import('fs');

    const diagramsRoot = 'packages/mermaid/src/diagrams';
    const diagramFolders = fs
      .readdirSync(diagramsRoot, { withFileTypes: true })
      .filter((entry) => entry.isDirectory() && entry.name !== 'common')
      .map((entry) => entry.name);

    // A diagram is covered by a per-diagram spec subfolder OR by the global mmd
    // snapshot runner (fixtures under e2e/diagrams/<name>/). The migration moved
    // several diagrams (block, c4, packet, …) to fixtures only.
    const covered = new Set(
      [SPEC_BASE_DIR, DIAGRAMS_DIR].flatMap((dir) =>
        fs
          .readdirSync(dir, { withFileTypes: true })
          .filter((entry) => entry.isDirectory())
          .map((entry) => canonical(entry.name))
      )
    );

    const missing = diagramFolders.filter((name) => !covered.has(canonical(name)));
    expect(
      missing,
      `Diagrams with no spec subfolder and no fixtures: ${missing.join(', ')}`
    ).toEqual([]);
  });
});