diff --git a/code/lib/preview-api/src/modules/store/csf/portable-stories.test.ts b/code/lib/preview-api/src/modules/store/csf/portable-stories.test.ts
new file mode 100644
index 000000000000..0531a5acdc18
--- /dev/null
+++ b/code/lib/preview-api/src/modules/store/csf/portable-stories.test.ts
@@ -0,0 +1,146 @@
+import { describe, expect, vi, it } from 'vitest';
+import { composeStory, composeStories } from './portable-stories';
+
+// Most integration tests for this functionality are located under renderers/react
+describe('composeStory', () => {
+ const meta = {
+ title: 'Button',
+ parameters: {
+ firstAddon: true,
+ },
+ args: {
+ label: 'Hello World',
+ primary: true,
+ },
+ };
+
+ it('should return story with composed args and parameters', () => {
+ const Story = () => {};
+ Story.args = { primary: true };
+ Story.parameters = {
+ parameters: {
+ secondAddon: true,
+ },
+ };
+
+ const composedStory = composeStory(Story, meta);
+ expect(composedStory.args).toEqual({ ...Story.args, ...meta.args });
+ expect(composedStory.parameters).toEqual(
+ expect.objectContaining({ ...Story.parameters, ...meta.parameters })
+ );
+ });
+
+ it('should throw an error if Story is undefined', () => {
+ expect(() => {
+ // @ts-expect-error (invalid input)
+ composeStory(undefined, meta);
+ }).toThrow();
+ });
+
+ describe('Id of the story', () => {
+ it('is exposed correctly when composeStories is used', () => {
+ const module = {
+ default: {
+ title: 'Example/Button',
+ },
+ CSF3Primary: () => {},
+ };
+ const Primary = composeStory(module.CSF3Primary, module.default, {});
+ expect(Primary.id).toBe('example-button--csf-3-primary');
+ });
+ it('is exposed correctly when composeStory is used and exportsName is passed', () => {
+ const module = {
+ default: {
+ title: 'Example/Button',
+ },
+ CSF3Primary: () => {},
+ };
+ const Primary = composeStory(module.CSF3Primary, module.default, {}, {}, 'overwritten');
+ expect(Primary.id).toBe('example-button--overwritten');
+ });
+ it("is not unique when composeStory is used and exportsName isn't passed", () => {
+ const Primary = composeStory({ render: () => {} }, {});
+ expect(Primary.id).toContain('unknown');
+ });
+ });
+});
+
+describe('composeStories', () => {
+ const composeStoryFn = vi.fn((v) => v);
+ const defaultAnnotations = { render: () => '' };
+ it('should call composeStoryFn with stories', () => {
+ const composeStorySpy = vi.fn((v) => v);
+ const module = {
+ default: {
+ title: 'Button',
+ },
+ StoryOne: () => {},
+ StoryTwo: () => {},
+ };
+ const globalConfig = {};
+ composeStories(module, globalConfig, composeStorySpy);
+ expect(composeStorySpy).toHaveBeenCalledWith(
+ module.StoryOne,
+ module.default,
+ globalConfig,
+ 'StoryOne'
+ );
+ expect(composeStorySpy).toHaveBeenCalledWith(
+ module.StoryTwo,
+ module.default,
+ globalConfig,
+ 'StoryTwo'
+ );
+ });
+
+ it('should not call composeStoryFn for non-story exports', () => {
+ const composeStorySpy = vi.fn((v) => v);
+ const module = {
+ default: {
+ title: 'Button',
+ excludeStories: /Data/,
+ },
+ mockData: {},
+ };
+ composeStories(module, defaultAnnotations, composeStoryFn);
+ expect(composeStorySpy).not.toHaveBeenCalled();
+ });
+
+ describe('non-story exports', () => {
+ it('should filter non-story exports with excludeStories', () => {
+ const StoryModuleWithNonStoryExports = {
+ default: {
+ title: 'Some/Component',
+ excludeStories: /.*Data/,
+ },
+ LegitimateStory: () => 'hello world',
+ mockData: {},
+ };
+
+ const result = composeStories(
+ StoryModuleWithNonStoryExports,
+ defaultAnnotations,
+ composeStoryFn
+ );
+ expect(Object.keys(result)).not.toContain('mockData');
+ });
+
+ it('should filter non-story exports with includeStories', () => {
+ const StoryModuleWithNonStoryExports = {
+ default: {
+ title: 'Some/Component',
+ includeStories: /.*Story/,
+ },
+ LegitimateStory: () => 'hello world',
+ mockData: {},
+ };
+
+ const result = composeStories(
+ StoryModuleWithNonStoryExports,
+ defaultAnnotations,
+ composeStoryFn
+ );
+ expect(Object.keys(result)).not.toContain('mockData');
+ });
+ });
+});
diff --git a/code/lib/preview-api/src/modules/store/csf/portable-stories/index.ts b/code/lib/preview-api/src/modules/store/csf/portable-stories.ts
similarity index 90%
rename from code/lib/preview-api/src/modules/store/csf/portable-stories/index.ts
rename to code/lib/preview-api/src/modules/store/csf/portable-stories.ts
index b15a3345ed0c..b9d7abc31213 100644
--- a/code/lib/preview-api/src/modules/store/csf/portable-stories/index.ts
+++ b/code/lib/preview-api/src/modules/store/csf/portable-stories.ts
@@ -15,12 +15,12 @@ import type {
} from '@storybook/types';
import { HooksContext } from '../../../addons';
-import { composeConfigs } from '../composeConfigs';
-import { prepareContext, prepareStory } from '../prepareStory';
-import { normalizeStory } from '../normalizeStory';
-import { normalizeComponentAnnotations } from '../normalizeComponentAnnotations';
-import { getValuesFromArgTypes } from '../getValuesFromArgTypes';
-import { normalizeProjectAnnotations } from '../normalizeProjectAnnotations';
+import { composeConfigs } from './composeConfigs';
+import { prepareContext, prepareStory } from './prepareStory';
+import { normalizeStory } from './normalizeStory';
+import { normalizeComponentAnnotations } from './normalizeComponentAnnotations';
+import { getValuesFromArgTypes } from './getValuesFromArgTypes';
+import { normalizeProjectAnnotations } from './normalizeProjectAnnotations';
let GLOBAL_STORYBOOK_PROJECT_ANNOTATIONS = composeConfigs([]);
diff --git a/code/lib/preview-api/src/modules/store/csf/portable-stories/index.test.ts b/code/lib/preview-api/src/modules/store/csf/portable-stories/index.test.ts
deleted file mode 100644
index f0c811aa5abc..000000000000
--- a/code/lib/preview-api/src/modules/store/csf/portable-stories/index.test.ts
+++ /dev/null
@@ -1,79 +0,0 @@
-import { describe, expect, vi, it } from 'vitest';
-import { composeStory, composeStories } from './index';
-
-// Most integration tests for this functionality are located under renderers/react
-describe('composeStory', () => {
- const meta = {
- title: 'Button',
- parameters: {
- firstAddon: true,
- },
- args: {
- label: 'Hello World',
- primary: true,
- },
- };
-
- it('should return story with composed args and parameters', () => {
- const Story = () => {};
- Story.args = { primary: true };
- Story.parameters = {
- parameters: {
- secondAddon: true,
- },
- };
-
- const composedStory = composeStory(Story, meta);
- expect(composedStory.args).toEqual({ ...Story.args, ...meta.args });
- expect(composedStory.parameters).toEqual(
- expect.objectContaining({ ...Story.parameters, ...meta.parameters })
- );
- });
-
- it('should throw an error if Story is undefined', () => {
- expect(() => {
- // @ts-expect-error (invalid input)
- composeStory(undefined, meta);
- }).toThrow();
- });
-});
-
-describe('composeStories', () => {
- it('should call composeStoryFn with stories', () => {
- const composeConfigFn = vi.fn((v) => v);
- const module = {
- default: {
- title: 'Button',
- },
- StoryOne: () => {},
- StoryTwo: () => {},
- };
- const globalConfig = {};
- composeStories(module, globalConfig, composeConfigFn);
- expect(composeConfigFn).toHaveBeenCalledWith(
- module.StoryOne,
- module.default,
- globalConfig,
- 'StoryOne'
- );
- expect(composeConfigFn).toHaveBeenCalledWith(
- module.StoryTwo,
- module.default,
- globalConfig,
- 'StoryTwo'
- );
- });
-
- it('should not call composeStoryFn for non-story exports', () => {
- const composeConfigFn = vi.fn((v) => v);
- const module = {
- default: {
- title: 'Button',
- excludeStories: /Data/,
- },
- mockData: {},
- };
- composeStories(module, {}, composeConfigFn);
- expect(composeConfigFn).not.toHaveBeenCalled();
- });
-});
diff --git a/code/renderers/react/src/__test__/__snapshots__/internals.test.tsx.snap b/code/renderers/react/src/__test__/__snapshots__/portable-stories.test.tsx.snap
similarity index 100%
rename from code/renderers/react/src/__test__/__snapshots__/internals.test.tsx.snap
rename to code/renderers/react/src/__test__/__snapshots__/portable-stories.test.tsx.snap
diff --git a/code/renderers/react/src/__test__/internals.test.tsx b/code/renderers/react/src/__test__/internals.test.tsx
deleted file mode 100644
index 8bed037619be..000000000000
--- a/code/renderers/react/src/__test__/internals.test.tsx
+++ /dev/null
@@ -1,117 +0,0 @@
-import { describe, it, expect } from 'vitest';
-import React from 'react';
-import { addons } from '@storybook/preview-api';
-import { cleanup, render, screen } from '@testing-library/react';
-
-import { composeStories, composeStory } from '..';
-
-import * as stories from './Button.stories';
-
-const { CSF2StoryWithParamsAndDecorator } = composeStories(stories);
-
-it('returns composed args including default values from argtypes', () => {
- expect(CSF2StoryWithParamsAndDecorator.args).toEqual({
- ...stories.CSF2StoryWithParamsAndDecorator.args,
- });
-});
-
-it('returns composed parameters from story', () => {
- expect(CSF2StoryWithParamsAndDecorator.parameters).toEqual(
- expect.objectContaining({
- ...stories.CSF2StoryWithParamsAndDecorator.parameters,
- })
- );
-});
-
-describe('Id of the story', () => {
- it('is exposed correctly when composeStories is used', () => {
- expect(CSF2StoryWithParamsAndDecorator.id).toBe(
- 'example-button--csf-2-story-with-params-and-decorator'
- );
- });
- it('is exposed correctly when composeStory is used and exportsName is passed', () => {
- const exportName = Object.entries(stories).filter(
- ([_, story]) => story === stories.CSF3Primary
- )[0][0];
- const Primary = composeStory(stories.CSF3Primary, stories.default, {}, exportName);
- expect(Primary.id).toBe('example-button--csf-3-primary');
- });
- it("is not unique when composeStory is used and exportsName isn't passed", () => {
- const Primary = composeStory(stories.CSF3Primary, stories.default);
- expect(Primary.id).toContain('unknown');
- });
-});
-
-// common in addons that need to communicate between manager and preview
-it('should pass with decorators that need addons channel', () => {
- const PrimaryWithChannels = composeStory(stories.CSF3Primary, stories.default, {
- decorators: [
- (StoryFn: any) => {
- addons.getChannel();
- return