mirror of
https://github.com/storybookjs/storybook.git
synced 2025-04-09 00:19:13 +08:00
596 lines
16 KiB
TypeScript
596 lines
16 KiB
TypeScript
function wrapPreset(basePresets: any): { babel: Function; webpack: Function } {
|
|
return {
|
|
babel: async (config: any, args: any) => basePresets.apply('babel', config, args),
|
|
webpack: async (config: any, args: any) => basePresets.apply('webpack', config, args),
|
|
};
|
|
}
|
|
|
|
function mockPreset(name: string, mockPresetObject: any) {
|
|
jest.mock(name, () => mockPresetObject, { virtual: true });
|
|
}
|
|
|
|
jest.mock('@storybook/node-logger', () => ({
|
|
logger: {
|
|
info: jest.fn(),
|
|
warn: jest.fn(),
|
|
error: jest.fn(),
|
|
},
|
|
}));
|
|
|
|
jest.mock('./utils/safeResolve', () => {
|
|
const KNOWN_FILES = [
|
|
'@storybook/react',
|
|
'@storybook/addon-actions/manager',
|
|
'@storybook/addon-actions/register',
|
|
'./local/preset',
|
|
'./local/addons',
|
|
'/absolute/preset',
|
|
'/absolute/addons',
|
|
'@storybook/addon-docs',
|
|
'@storybook/addon-cool',
|
|
'@storybook/addon-docs/preset',
|
|
'@storybook/addon-interactions/preset',
|
|
'@storybook/addon-essentials',
|
|
'@storybook/addon-knobs/manager',
|
|
'@storybook/addon-knobs/register',
|
|
'@storybook/addon-notes/register-panel',
|
|
'@storybook/preset-create-react-app',
|
|
'@storybook/preset-typescript',
|
|
'addon-bar/preset.js',
|
|
'addon-bar',
|
|
'addon-baz/register.js',
|
|
'addon-foo/register.js',
|
|
];
|
|
|
|
return {
|
|
safeResolveFrom: jest.fn((l: any, name: string) => {
|
|
if (KNOWN_FILES.includes(name)) {
|
|
return name;
|
|
}
|
|
return undefined;
|
|
}),
|
|
safeResolve: jest.fn((name: string) => {
|
|
if (KNOWN_FILES.includes(name)) {
|
|
return name;
|
|
}
|
|
return undefined;
|
|
}),
|
|
};
|
|
});
|
|
|
|
describe('presets', () => {
|
|
it('does not throw when there is no preset file', async () => {
|
|
const { getPresets } = jest.requireActual('./presets');
|
|
let presets;
|
|
|
|
async function testPresets() {
|
|
presets = wrapPreset(await getPresets());
|
|
await presets.webpack();
|
|
await presets.babel();
|
|
}
|
|
|
|
await expect(testPresets()).resolves.toBeUndefined();
|
|
|
|
expect(presets).toBeDefined();
|
|
});
|
|
|
|
it('does not throw when presets are empty', async () => {
|
|
const { getPresets } = jest.requireActual('./presets');
|
|
const presets = wrapPreset(await getPresets([]));
|
|
|
|
async function testPresets() {
|
|
await presets.webpack();
|
|
await presets.babel();
|
|
}
|
|
|
|
await expect(testPresets()).resolves.toBeUndefined();
|
|
});
|
|
|
|
it('does not throw when preset can not be loaded', async () => {
|
|
const { getPresets } = jest.requireActual('./presets');
|
|
const presets = wrapPreset(await getPresets(['preset-foo']));
|
|
|
|
async function testPresets() {
|
|
await presets.webpack();
|
|
await presets.babel();
|
|
}
|
|
|
|
await expect(testPresets()).resolves.toBeUndefined();
|
|
});
|
|
|
|
it('loads and applies presets when they are combined in another preset', async () => {
|
|
mockPreset('preset-foo', {
|
|
foo: (exec: string[]) => exec.concat('foo'),
|
|
});
|
|
|
|
mockPreset('preset-bar', {
|
|
foo: (exec: string[]) => exec.concat('bar'),
|
|
});
|
|
|
|
mockPreset('preset-got', [
|
|
'preset-dracarys',
|
|
{ name: 'preset-valar', options: { custom: 'morghulis' } },
|
|
]);
|
|
|
|
mockPreset('preset-dracarys', {
|
|
foo: (exec: string[]) => exec.concat('dracarys'),
|
|
});
|
|
|
|
mockPreset('preset-valar', {
|
|
foo: (exec: string[], options: any) => exec.concat(`valar ${options.custom}`),
|
|
});
|
|
|
|
const { getPresets } = jest.requireActual('./presets');
|
|
const presets = await getPresets(['preset-foo', 'preset-got', 'preset-bar'], {});
|
|
|
|
const result = await presets.apply('foo', []);
|
|
|
|
expect(result).toEqual(['foo', 'dracarys', 'valar morghulis', 'bar']);
|
|
});
|
|
|
|
it('loads and applies presets when they are declared as a string', async () => {
|
|
const mockPresetFooExtendWebpack = jest.fn();
|
|
const mockPresetBarExtendBabel = jest.fn();
|
|
|
|
mockPreset('preset-foo', {
|
|
webpack: mockPresetFooExtendWebpack,
|
|
});
|
|
|
|
mockPreset('preset-bar', {
|
|
babel: mockPresetBarExtendBabel,
|
|
});
|
|
|
|
const { getPresets } = jest.requireActual('./presets');
|
|
const presets = wrapPreset(await getPresets(['preset-foo', 'preset-bar'], {}));
|
|
|
|
async function testPresets() {
|
|
await presets.webpack();
|
|
await presets.babel();
|
|
}
|
|
|
|
await expect(testPresets()).resolves.toBeUndefined();
|
|
|
|
expect(mockPresetFooExtendWebpack).toHaveBeenCalled();
|
|
expect(mockPresetBarExtendBabel).toHaveBeenCalled();
|
|
});
|
|
|
|
it('loads and applies presets when they are declared as an object without props', async () => {
|
|
const mockPresetFooExtendWebpack = jest.fn();
|
|
const mockPresetBarExtendBabel = jest.fn();
|
|
|
|
mockPreset('preset-foo', {
|
|
webpack: mockPresetFooExtendWebpack,
|
|
});
|
|
|
|
mockPreset('preset-bar', {
|
|
babel: mockPresetBarExtendBabel,
|
|
});
|
|
|
|
const { getPresets } = jest.requireActual('./presets');
|
|
const presets = wrapPreset(
|
|
await getPresets([{ name: 'preset-foo' }, { name: 'preset-bar' }], {})
|
|
);
|
|
|
|
async function testPresets() {
|
|
await presets.webpack();
|
|
await presets.babel();
|
|
}
|
|
|
|
await expect(testPresets()).resolves.toBeUndefined();
|
|
|
|
expect(mockPresetFooExtendWebpack).toHaveBeenCalled();
|
|
expect(mockPresetBarExtendBabel).toHaveBeenCalled();
|
|
});
|
|
|
|
it('loads and applies presets when they are declared as an object with props', async () => {
|
|
const mockPresetFooExtendWebpack = jest.fn();
|
|
const mockPresetBarExtendBabel = jest.fn();
|
|
|
|
mockPreset('preset-foo', {
|
|
webpack: mockPresetFooExtendWebpack,
|
|
});
|
|
|
|
mockPreset('preset-bar', {
|
|
babel: mockPresetBarExtendBabel,
|
|
});
|
|
|
|
const { getPresets } = jest.requireActual('./presets');
|
|
const presets = wrapPreset(
|
|
await getPresets(
|
|
[
|
|
{ name: 'preset-foo', options: { foo: 1 } },
|
|
{ name: 'preset-bar', options: { bar: 'a' } },
|
|
],
|
|
{}
|
|
)
|
|
);
|
|
|
|
async function testPresets() {
|
|
await presets.webpack({});
|
|
await presets.babel({});
|
|
}
|
|
|
|
await expect(testPresets()).resolves.toBeUndefined();
|
|
|
|
expect(mockPresetFooExtendWebpack).toHaveBeenCalledWith(expect.anything(), {
|
|
foo: 1,
|
|
presetsList: expect.anything(),
|
|
presets: expect.anything(),
|
|
});
|
|
expect(mockPresetBarExtendBabel).toHaveBeenCalledWith(expect.anything(), {
|
|
bar: 'a',
|
|
presetsList: expect.anything(),
|
|
presets: expect.anything(),
|
|
});
|
|
});
|
|
|
|
it('loads and applies presets when they are declared as a string and as an object', async () => {
|
|
const mockPresetFooExtendWebpack = jest.fn();
|
|
const mockPresetBarExtendBabel = jest.fn();
|
|
|
|
mockPreset('preset-foo', {
|
|
webpack: mockPresetFooExtendWebpack,
|
|
});
|
|
|
|
mockPreset('preset-bar', {
|
|
babel: mockPresetBarExtendBabel,
|
|
});
|
|
|
|
const { getPresets } = jest.requireActual('./presets');
|
|
const presets = wrapPreset(
|
|
await getPresets(
|
|
[
|
|
'preset-foo',
|
|
{
|
|
name: 'preset-bar',
|
|
options: {
|
|
bar: 'a',
|
|
},
|
|
},
|
|
],
|
|
{}
|
|
)
|
|
);
|
|
|
|
async function testPresets() {
|
|
await presets.webpack({});
|
|
await presets.babel({});
|
|
}
|
|
|
|
await expect(testPresets()).resolves.toBeUndefined();
|
|
|
|
expect(mockPresetFooExtendWebpack).toHaveBeenCalled();
|
|
expect(mockPresetBarExtendBabel).toHaveBeenCalledWith(expect.anything(), {
|
|
bar: 'a',
|
|
presetsList: expect.arrayContaining([
|
|
expect.objectContaining({ name: 'preset-foo' }),
|
|
expect.objectContaining({ name: 'preset-bar' }),
|
|
]),
|
|
presets: expect.anything(),
|
|
});
|
|
});
|
|
|
|
it('applies presets in chain', async () => {
|
|
const mockPresetFooExtendWebpack = jest.fn((...args: any[]) => ({}));
|
|
const mockPresetBarExtendWebpack = jest.fn((...args: any[]) => ({}));
|
|
|
|
mockPreset('preset-foo', {
|
|
webpack: mockPresetFooExtendWebpack,
|
|
});
|
|
|
|
mockPreset('preset-bar', {
|
|
webpack: mockPresetBarExtendWebpack,
|
|
});
|
|
|
|
const { getPresets } = jest.requireActual('./presets');
|
|
const presets = wrapPreset(
|
|
await getPresets(
|
|
[
|
|
'preset-foo',
|
|
{
|
|
name: 'preset-bar',
|
|
options: {
|
|
bar: 'a',
|
|
presetsList: expect.arrayContaining([
|
|
expect.objectContaining({ name: 'preset-foo' }),
|
|
expect.objectContaining({ name: 'preset-bar' }),
|
|
]),
|
|
presets: expect.anything(),
|
|
},
|
|
},
|
|
],
|
|
{}
|
|
)
|
|
);
|
|
|
|
async function testPresets() {
|
|
await presets.webpack();
|
|
await presets.babel();
|
|
}
|
|
|
|
await expect(testPresets()).resolves.toBeUndefined();
|
|
|
|
expect(mockPresetFooExtendWebpack).toHaveBeenCalled();
|
|
expect(mockPresetBarExtendWebpack).toHaveBeenCalledWith(expect.anything(), {
|
|
bar: 'a',
|
|
presetsList: expect.arrayContaining([
|
|
expect.objectContaining({ name: 'preset-foo' }),
|
|
expect.objectContaining({ name: 'preset-bar' }),
|
|
]),
|
|
presets: expect.anything(),
|
|
});
|
|
});
|
|
|
|
it('allows for presets to export presets array', async () => {
|
|
const { getPresets } = jest.requireActual('./presets');
|
|
const input = {};
|
|
const mockPresetBar = jest.fn((...args: any[]) => input);
|
|
|
|
mockPreset('preset-foo', {
|
|
presets: ['preset-bar'],
|
|
});
|
|
|
|
mockPreset('preset-bar', {
|
|
bar: mockPresetBar,
|
|
});
|
|
|
|
const presets = await getPresets(['preset-foo'], {});
|
|
|
|
const output = await presets.apply('bar');
|
|
|
|
expect(mockPresetBar).toHaveBeenCalledWith(undefined, expect.any(Object));
|
|
|
|
expect(input).toBe(output);
|
|
});
|
|
|
|
it('allows for presets to export presets fn', async () => {
|
|
const { getPresets } = jest.requireActual('./presets');
|
|
const input = {};
|
|
const storybookOptions = { a: 1 };
|
|
const presetOptions = { b: 2 };
|
|
const mockPresetBar = jest.fn((...args: any[]) => input);
|
|
const mockPresetFoo = jest.fn((...args: any[]) => ['preset-bar']);
|
|
|
|
mockPreset('preset-foo', {
|
|
presets: mockPresetFoo,
|
|
});
|
|
|
|
mockPreset('preset-bar', {
|
|
bar: mockPresetBar,
|
|
});
|
|
|
|
const presets = await getPresets([{ name: 'preset-foo', options: { b: 2 } }], storybookOptions);
|
|
|
|
const output = await presets.apply('bar');
|
|
|
|
expect(mockPresetFoo).toHaveBeenCalledWith({ ...storybookOptions, ...presetOptions });
|
|
expect(mockPresetBar).toHaveBeenCalledWith(undefined, expect.any(Object));
|
|
|
|
expect(input).toBe(output);
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.resetModules();
|
|
});
|
|
});
|
|
|
|
describe('resolveAddonName', () => {
|
|
const { resolveAddonName } = jest.requireActual('./presets');
|
|
|
|
it('should resolve packages with metadata (relative path)', () => {
|
|
mockPreset('./local/preset', {
|
|
presets: [],
|
|
});
|
|
expect(resolveAddonName({}, './local/preset')).toEqual({
|
|
name: './local/preset',
|
|
type: 'presets',
|
|
});
|
|
});
|
|
|
|
it('should resolve packages with metadata (absolute path)', () => {
|
|
mockPreset('/absolute/preset', {
|
|
presets: [],
|
|
});
|
|
expect(resolveAddonName({}, '/absolute/preset')).toEqual({
|
|
name: '/absolute/preset',
|
|
type: 'presets',
|
|
});
|
|
});
|
|
|
|
it('should resolve packages without metadata', () => {
|
|
expect(resolveAddonName({}, '@storybook/preset-create-react-app')).toEqual({
|
|
name: '@storybook/preset-create-react-app',
|
|
type: 'presets',
|
|
});
|
|
});
|
|
|
|
it('should resolve managerEntries', () => {
|
|
expect(resolveAddonName({}, '@storybook/addon-actions/register')).toEqual({
|
|
name: '@storybook/addon-actions/register',
|
|
managerEntries: ['@storybook/addon-actions/register'],
|
|
type: 'virtual',
|
|
});
|
|
});
|
|
|
|
it('should resolve managerEntries from new /manager path', () => {
|
|
expect(resolveAddonName({}, '@storybook/addon-actions/manager')).toEqual({
|
|
name: '@storybook/addon-actions/manager',
|
|
managerEntries: ['@storybook/addon-actions/manager'],
|
|
type: 'virtual',
|
|
});
|
|
});
|
|
|
|
it('should resolve presets', () => {
|
|
expect(resolveAddonName({}, '@storybook/addon-docs/preset')).toEqual({
|
|
name: '@storybook/addon-docs/preset',
|
|
type: 'presets',
|
|
});
|
|
});
|
|
|
|
it('should resolve preset packages', () => {
|
|
expect(resolveAddonName({}, '@storybook/addon-essentials')).toEqual({
|
|
name: '@storybook/addon-essentials',
|
|
type: 'presets',
|
|
});
|
|
});
|
|
|
|
it('should error on invalid inputs', () => {
|
|
expect(() => resolveAddonName({}, null)).toThrow();
|
|
});
|
|
});
|
|
|
|
describe('loadPreset', () => {
|
|
mockPreset('@storybook/react', {});
|
|
mockPreset('@storybook/preset-typescript', {});
|
|
mockPreset('@storybook/addon-docs/preset', {});
|
|
mockPreset('@storybook/addon-actions/register', {});
|
|
mockPreset('addon-foo/register.js', {});
|
|
mockPreset('@storybook/addon-cool', {});
|
|
mockPreset('@storybook/addon-interactions/preset', {});
|
|
mockPreset('addon-bar', {
|
|
addons: ['@storybook/addon-cool'],
|
|
presets: ['@storybook/addon-interactions/preset'],
|
|
});
|
|
mockPreset('addon-baz/register.js', {});
|
|
mockPreset('@storybook/addon-notes/register-panel', {});
|
|
|
|
const { loadPreset } = jest.requireActual('./presets');
|
|
|
|
it('should prepend framework field to list of presets', async () => {
|
|
const loaded = await loadPreset(
|
|
{
|
|
name: '',
|
|
type: 'virtual',
|
|
framework: '@storybook/react',
|
|
presets: ['@storybook/preset-typescript'],
|
|
addons: ['@storybook/addon-docs/preset'],
|
|
},
|
|
0,
|
|
{}
|
|
);
|
|
expect(loaded).toMatchInlineSnapshot(`
|
|
Array [
|
|
Object {
|
|
"name": "@storybook/preset-typescript",
|
|
"options": Object {},
|
|
"preset": Object {},
|
|
},
|
|
Object {
|
|
"name": "@storybook/addon-docs/preset",
|
|
"options": Object {},
|
|
"preset": Object {},
|
|
},
|
|
Object {
|
|
"name": Object {
|
|
"addons": Array [
|
|
"@storybook/addon-docs/preset",
|
|
],
|
|
"framework": "@storybook/react",
|
|
"name": "",
|
|
"presets": Array [
|
|
"@storybook/preset-typescript",
|
|
],
|
|
"type": "virtual",
|
|
},
|
|
"options": Object {},
|
|
"preset": Object {
|
|
"framework": "@storybook/react",
|
|
},
|
|
},
|
|
]
|
|
`);
|
|
});
|
|
|
|
it('should resolve all addons & presets in correct order', async () => {
|
|
const loaded = await loadPreset(
|
|
{
|
|
name: '',
|
|
type: 'virtual',
|
|
presets: ['@storybook/preset-typescript'],
|
|
addons: [
|
|
'@storybook/addon-docs/preset',
|
|
'@storybook/addon-actions/register',
|
|
'addon-foo/register.js',
|
|
'addon-bar',
|
|
'addon-baz/register.js',
|
|
'@storybook/addon-notes/register-panel',
|
|
],
|
|
},
|
|
0,
|
|
{}
|
|
);
|
|
expect(loaded).toEqual([
|
|
{
|
|
name: '@storybook/preset-typescript',
|
|
options: {},
|
|
preset: {},
|
|
},
|
|
{
|
|
name: '@storybook/addon-docs/preset',
|
|
options: {},
|
|
preset: {},
|
|
},
|
|
{
|
|
name: '@storybook/addon-actions/register',
|
|
options: {},
|
|
preset: {
|
|
managerEntries: ['@storybook/addon-actions/register'],
|
|
},
|
|
},
|
|
{
|
|
name: 'addon-foo/register.js',
|
|
options: {},
|
|
preset: {
|
|
managerEntries: ['addon-foo/register.js'],
|
|
},
|
|
},
|
|
{
|
|
name: '@storybook/addon-interactions/preset',
|
|
options: {},
|
|
preset: {},
|
|
},
|
|
{
|
|
name: '@storybook/addon-cool',
|
|
options: {},
|
|
preset: {},
|
|
},
|
|
{
|
|
name: 'addon-bar',
|
|
options: {},
|
|
preset: {},
|
|
},
|
|
{
|
|
name: 'addon-baz/register.js',
|
|
options: {},
|
|
preset: {
|
|
managerEntries: ['addon-baz/register.js'],
|
|
},
|
|
},
|
|
{
|
|
name: '@storybook/addon-notes/register-panel',
|
|
options: {},
|
|
preset: {
|
|
managerEntries: ['@storybook/addon-notes/register-panel'],
|
|
},
|
|
},
|
|
{
|
|
name: {
|
|
presets: ['@storybook/preset-typescript'],
|
|
addons: [
|
|
'@storybook/addon-docs/preset',
|
|
'@storybook/addon-actions/register',
|
|
'addon-foo/register.js',
|
|
'addon-bar',
|
|
'addon-baz/register.js',
|
|
'@storybook/addon-notes/register-panel',
|
|
],
|
|
name: '',
|
|
type: 'virtual',
|
|
},
|
|
options: {},
|
|
preset: {},
|
|
},
|
|
]);
|
|
});
|
|
});
|