<commit_before>import 'jest-specific-snapshot';
import path from 'path';
import { Configuration } from 'webpack';
import Cache from 'file-system-cache';
import { resolvePathInStorybookCache } from '@storybook/core-common';
import { executor as previewExecutor } from '@storybook/builder-webpack4';
import { executor as managerExecutor } from './manager/builder';

import { buildDevStandalone } from './build-dev';
import { buildStaticStandalone } from './build-static';

import reactOptions from '../../../app/react/src/server/options';
import vue3Options from '../../../app/vue3/src/server/options';
import htmlOptions from '../../../app/html/src/server/options';
import webComponentsOptions from '../../../app/web-components/src/server/options';

jest.mock('@storybook/builder-webpack5', () => {
  const actualBuilder = jest.requireActual('@storybook/builder-webpack5');
  // MUTATION! we couldn't mock webpack5, so we added a level of indirection instead
  actualBuilder.executor.get = jest.fn();
  return actualBuilder;
});

jest.mock('@storybook/builder-webpack4', () => {
  const actualBuilder = jest.requireActual('@storybook/builder-webpack4');
  // MUTATION! we couldn't mock webpack5, so we added a level of indirection instead
  actualBuilder.executor.get = jest.fn();
  return actualBuilder;
});

jest.mock('./manager/builder', () => {
  const actualBuilder = jest.requireActual('./manager/builder');
  // MUTATION!
  actualBuilder.executor.get = jest.fn();
  return actualBuilder;
});

jest.mock('cpy', () => () => Promise.resolve());
jest.mock('http', () => ({
  ...jest.requireActual('http'),
  createServer: () => ({ listen: (_options, cb) => cb() }),
}));
jest.mock('@storybook/node-logger', () => ({
  logger: {
    info: jest.fn(),
    warn: jest.fn(),
    error: jest.fn(),
  },
}));
jest.mock('./utils/output-startup-information', () => ({
  outputStartupInformation: jest.fn(),
}));

const cache = Cache({
  basePath: resolvePathInStorybookCache('dev-server'),
  ns: 'storybook-test', // Optional. A grouping namespace for items.
});

const managerOnly = false;
const baseOptions = {
  ignorePreview: managerOnly,
  // FIXME: this should just be ignorePreview everywhere
  managerOnly, // production
  docsMode: false,
  cache,
  configDir: path.resolve(`${__dirname}/../../../examples/react-ts`),
  outputDir: `${__dirname}/storybook-static`, // production
  ci: true,
  managerCache: false,
};

const ROOT = process.cwd();
const NODE_MODULES = /.*node_modules/g;
const cleanRoots = (obj): any => {
  if (!obj) return obj;
  if (typeof obj === 'string')
    return obj.replace(ROOT, 'ROOT').replace(NODE_MODULES, 'NODE_MODULES');
  if (Array.isArray(obj)) return obj.map(cleanRoots);
  if (obj instanceof RegExp) return cleanRoots(obj.toString());
  if (typeof obj === 'object') {
    return Object.fromEntries(
      Object.entries(obj).map(([key, val]) => {
        if (key === 'version' && typeof val === 'string') {
          return [key, '*'];
        }
        return [key, cleanRoots(val)];
      })
    );
  }
  return obj;
};

const getConfig = (fn: any, name): Configuration | null => {
  const call = fn.mock.calls.find((c) => c[0].name === name);
  if (!call) return null;
  return call[0];
};

const prepareSnap = (fn: any, name): Pick<Configuration, 'module' | 'entry' | 'plugins'> => {
  const config = getConfig(fn, name);
  if (!config) return null;

  const keys = Object.keys(config);
  const { module, entry, plugins } = config;

  return cleanRoots({ keys, module, entry, plugins: plugins.map((p) => p.constructor.name) });
};

const snap = (name: string) => `__snapshots__/${name}`;

describe.each([
  ['cra-ts-essentials', reactOptions],
  ['vue-3-cli', vue3Options],
  ['web-components-kitchen-sink', webComponentsOptions],
  ['html-kitchen-sink', htmlOptions],
])(
  '%s',
  (example, frameworkOptions) => {
    beforeEach(() => {
      jest.clearAllMocks();
      cache.clear();
    });

    const options = {
      ...baseOptions,
      ...frameworkOptions,
      configDir: path.resolve(`${__dirname}/../../../examples/${example}/.storybook`),
    };

    describe('manager', () => {
      it('dev mode', async () => {
        await buildDevStandalone({ ...options, ignorePreview: true });

        const managerConfig = prepareSnap(managerExecutor.get, 'manager');
        expect(managerConfig).toMatchSpecificSnapshot(snap(`${example}_manager-dev`));
      });
      it('production mode', async () => {
        await buildStaticStandalone({ ...options, ignorePreview: true });

        const managerConfig = prepareSnap(managerExecutor.get, 'manager');
        expect(managerConfig).toMatchSpecificSnapshot(snap(`${example}_manager-prod`));
      });
    });

    describe('preview', () => {
      it('dev mode', async () => {
        await buildDevStandalone({ ...options, managerCache: true });

        const previewConfig = prepareSnap(previewExecutor.get, 'preview');
        expect(previewConfig).toMatchSpecificSnapshot(snap(`${example}_preview-dev`));
      });
      it('production mode', async () => {
        await buildStaticStandalone({ ...options, managerCache: true });

        const previewConfig = prepareSnap(previewExecutor.get, 'preview');
        expect(previewConfig).toMatchSpecificSnapshot(snap(`${example}_preview-prod`));
      });
    });
  },
  10000
);

const progressPlugin = (config) =>
  config.plugins.find((p) => p.constructor.name === 'ProgressPlugin');

describe('dev cli flags', () => {
  beforeEach(() => {
    jest.clearAllMocks();
    cache.clear();
  });

  const cliOptions = { ...reactOptions, ...baseOptions };

  it('baseline', async () => {
    await buildDevStandalone(cliOptions);
    const config = getConfig(previewExecutor.get, 'preview');
    expect(progressPlugin(config)).toBeTruthy();
  });

  it('--quiet', async () => {
    const options = { ...cliOptions, quiet: true };
    await buildDevStandalone(options);
    const config = getConfig(previewExecutor.get, 'preview');
    expect(progressPlugin(config)).toBeFalsy();
  });

  describe.each([
    ['root directory /', '/', "Won't remove directory '/'. Check your outputDir!"],
    ['empty string ""', '', "Won't remove current directory. Check your outputDir!"],
  ])('Invalid outputDir must throw: %s', (_, outputDir, expectedErrorMessage) => {
    const optionsWithInvalidDir = {
      ...cliOptions,
      outputDir,
    };
    beforeEach(() => {
      jest.clearAllMocks();
      cache.clear();
    });
    it('production mode', async () => {
      expect.assertions(1);
      await expect(buildStaticStandalone(optionsWithInvalidDir)).rejects.toThrow(
        expectedErrorMessage
      );
    });
  });
});
<commit_msg>Fix test timeout<commit_after>import 'jest-specific-snapshot';
import path from 'path';
import { Configuration } from 'webpack';
import Cache from 'file-system-cache';
import { resolvePathInStorybookCache } from '@storybook/core-common';
import { executor as previewExecutor } from '@storybook/builder-webpack4';
import { executor as managerExecutor } from './manager/builder';

import { buildDevStandalone } from './build-dev';
import { buildStaticStandalone } from './build-static';

import reactOptions from '../../../app/react/src/server/options';
import vue3Options from '../../../app/vue3/src/server/options';
import htmlOptions from '../../../app/html/src/server/options';
import webComponentsOptions from '../../../app/web-components/src/server/options';

// this only applies to this file
jest.setTimeout(10000);

jest.mock('@storybook/builder-webpack5', () => {
  const actualBuilder = jest.requireActual('@storybook/builder-webpack5');
  // MUTATION! we couldn't mock webpack5, so we added a level of indirection instead
  actualBuilder.executor.get = jest.fn();
  return actualBuilder;
});

jest.mock('@storybook/builder-webpack4', () => {
  const actualBuilder = jest.requireActual('@storybook/builder-webpack4');
  // MUTATION! we couldn't mock webpack5, so we added a level of indirection instead
  actualBuilder.executor.get = jest.fn();
  return actualBuilder;
});

jest.mock('./manager/builder', () => {
  const actualBuilder = jest.requireActual('./manager/builder');
  // MUTATION!
  actualBuilder.executor.get = jest.fn();
  return actualBuilder;
});

jest.mock('cpy', () => () => Promise.resolve());
jest.mock('http', () => ({
  ...jest.requireActual('http'),
  createServer: () => ({ listen: (_options, cb) => cb() }),
}));
jest.mock('@storybook/node-logger', () => ({
  logger: {
    info: jest.fn(),
    warn: jest.fn(),
    error: jest.fn(),
  },
}));
jest.mock('./utils/output-startup-information', () => ({
  outputStartupInformation: jest.fn(),
}));

const cache = Cache({
  basePath: resolvePathInStorybookCache('dev-server'),
  ns: 'storybook-test', // Optional. A grouping namespace for items.
});

const managerOnly = false;
const baseOptions = {
  ignorePreview: managerOnly,
  // FIXME: this should just be ignorePreview everywhere
  managerOnly, // production
  docsMode: false,
  cache,
  configDir: path.resolve(`${__dirname}/../../../examples/react-ts`),
  outputDir: `${__dirname}/storybook-static`, // production
  ci: true,
  managerCache: false,
};

const ROOT = process.cwd();
const NODE_MODULES = /.*node_modules/g;
const cleanRoots = (obj): any => {
  if (!obj) return obj;
  if (typeof obj === 'string')
    return obj.replace(ROOT, 'ROOT').replace(NODE_MODULES, 'NODE_MODULES');
  if (Array.isArray(obj)) return obj.map(cleanRoots);
  if (obj instanceof RegExp) return cleanRoots(obj.toString());
  if (typeof obj === 'object') {
    return Object.fromEntries(
      Object.entries(obj).map(([key, val]) => {
        if (key === 'version' && typeof val === 'string') {
          return [key, '*'];
        }
        return [key, cleanRoots(val)];
      })
    );
  }
  return obj;
};

const getConfig = (fn: any, name): Configuration | null => {
  const call = fn.mock.calls.find((c) => c[0].name === name);
  if (!call) return null;
  return call[0];
};

const prepareSnap = (fn: any, name): Pick<Configuration, 'module' | 'entry' | 'plugins'> => {
  const config = getConfig(fn, name);
  if (!config) return null;

  const keys = Object.keys(config);
  const { module, entry, plugins } = config;

  return cleanRoots({ keys, module, entry, plugins: plugins.map((p) => p.constructor.name) });
};

const snap = (name: string) => `__snapshots__/${name}`;

describe.each([
  ['cra-ts-essentials', reactOptions],
  ['vue-3-cli', vue3Options],
  ['web-components-kitchen-sink', webComponentsOptions],
  ['html-kitchen-sink', htmlOptions],
])('%s', (example, frameworkOptions) => {
  beforeEach(() => {
    jest.clearAllMocks();
    cache.clear();
  });

  const options = {
    ...baseOptions,
    ...frameworkOptions,
    configDir: path.resolve(`${__dirname}/../../../examples/${example}/.storybook`),
  };

  describe('manager', () => {
    it('dev mode', async () => {
      await buildDevStandalone({ ...options, ignorePreview: true });

      const managerConfig = prepareSnap(managerExecutor.get, 'manager');
      expect(managerConfig).toMatchSpecificSnapshot(snap(`${example}_manager-dev`));
    });
    it('production mode', async () => {
      await buildStaticStandalone({ ...options, ignorePreview: true });

      const managerConfig = prepareSnap(managerExecutor.get, 'manager');
      expect(managerConfig).toMatchSpecificSnapshot(snap(`${example}_manager-prod`));
    });
  });

  describe('preview', () => {
    it('dev mode', async () => {
      await buildDevStandalone({ ...options, managerCache: true });

      const previewConfig = prepareSnap(previewExecutor.get, 'preview');
      expect(previewConfig).toMatchSpecificSnapshot(snap(`${example}_preview-dev`));
    });
    it('production mode', async () => {
      await buildStaticStandalone({ ...options, managerCache: true });

      const previewConfig = prepareSnap(previewExecutor.get, 'preview');
      expect(previewConfig).toMatchSpecificSnapshot(snap(`${example}_preview-prod`));
    });
  });
});

const progressPlugin = (config) =>
  config.plugins.find((p) => p.constructor.name === 'ProgressPlugin');

describe('dev cli flags', () => {
  beforeEach(() => {
    jest.clearAllMocks();
    cache.clear();
  });

  const cliOptions = { ...reactOptions, ...baseOptions };

  it('baseline', async () => {
    await buildDevStandalone(cliOptions);
    const config = getConfig(previewExecutor.get, 'preview');
    expect(progressPlugin(config)).toBeTruthy();
  });

  it('--quiet', async () => {
    const options = { ...cliOptions, quiet: true };
    await buildDevStandalone(options);
    const config = getConfig(previewExecutor.get, 'preview');
    expect(progressPlugin(config)).toBeFalsy();
  });

  describe.each([
    ['root directory /', '/', "Won't remove directory '/'. Check your outputDir!"],
    ['empty string ""', '', "Won't remove current directory. Check your outputDir!"],
  ])('Invalid outputDir must throw: %s', (_, outputDir, expectedErrorMessage) => {
    const optionsWithInvalidDir = {
      ...cliOptions,
      outputDir,
    };
    beforeEach(() => {
      jest.clearAllMocks();
      cache.clear();
    });
    it('production mode', async () => {
      expect.assertions(1);
      await expect(buildStaticStandalone(optionsWithInvalidDir)).rejects.toThrow(
        expectedErrorMessage
      );
    });
  });
});

{'fraction_non_alphanumeric': 0.18245114921015199, 'fraction_numerical': 0.002844950213371266, 'mean_word_length': 3.7469793887704337, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 73, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6122018', 'n_tokens_mistral': 4041, 'n_tokens_neox': 4010, 'n_words': 866}