From e46dada83c5c66388ae4176e75de33c576f69106 Mon Sep 17 00:00:00 2001 From: igor-dv Date: Sun, 10 Jun 2018 19:13:11 +0300 Subject: [PATCH 01/10] Create and expose Stories2SnapsConverter to support customization of the snapshot files + extract api to a separate dir structure --- .../src/Stories2SnapsConverter.js | 50 ++++++++ ...test.js => Stories2SnapsConverter.test.js} | 12 +- .../storyshots-core/src/api/index.js | 90 ++++++++++++++ .../src/api/integrityTestTemplate.js | 23 ++++ .../src/api/snapshotsTestsTemplate.js | 45 +++++++ .../storyshots/storyshots-core/src/index.js | 111 +----------------- .../storyshots-core/src/test-bodies.js | 10 +- .../storyshots/storyshots-core/src/utils.js | 27 ----- 8 files changed, 227 insertions(+), 141 deletions(-) create mode 100644 addons/storyshots/storyshots-core/src/Stories2SnapsConverter.js rename addons/storyshots/storyshots-core/src/{utils.test.js => Stories2SnapsConverter.test.js} (80%) create mode 100644 addons/storyshots/storyshots-core/src/api/index.js create mode 100644 addons/storyshots/storyshots-core/src/api/integrityTestTemplate.js create mode 100644 addons/storyshots/storyshots-core/src/api/snapshotsTestsTemplate.js delete mode 100644 addons/storyshots/storyshots-core/src/utils.js diff --git a/addons/storyshots/storyshots-core/src/Stories2SnapsConverter.js b/addons/storyshots/storyshots-core/src/Stories2SnapsConverter.js new file mode 100644 index 000000000000..626a467cdec0 --- /dev/null +++ b/addons/storyshots/storyshots-core/src/Stories2SnapsConverter.js @@ -0,0 +1,50 @@ +import path from 'path'; + +const defaultOptions = { + snapshotsDirName: '__snapshots__', + snapshotExtension: '.storyshot', + storiesExtensions: ['.js', '.jsx', '.ts', '.tsx'], +}; + +class DefaultStories2SnapsConverter { + constructor(options = {}) { + this.options = { + ...defaultOptions, + ...options, + }; + } + + getSnapshotExtension = () => this.options.snapshotExtension; + + getStoryshotFile(fileName) { + const { dir, name } = path.parse(fileName); + const { snapshotsDirName, snapshotExtension } = this.options; + + return path.format({ dir: path.join(dir, snapshotsDirName), name, ext: snapshotExtension }); + } + + getSnapshotFileName(context) { + const { fileName } = context; + + if (!fileName) { + return null; + } + + return this.getStoryshotFile(fileName); + } + + getPossibleStoriesFiles(storyshotFile) { + const { dir, name } = path.parse(storyshotFile); + const { storiesExtensions } = this.options; + + return storiesExtensions.map(ext => + path.format({ + dir: path.dirname(dir), + name, + ext, + }) + ); + } +} + +export default DefaultStories2SnapsConverter; diff --git a/addons/storyshots/storyshots-core/src/utils.test.js b/addons/storyshots/storyshots-core/src/Stories2SnapsConverter.test.js similarity index 80% rename from addons/storyshots/storyshots-core/src/utils.test.js rename to addons/storyshots/storyshots-core/src/Stories2SnapsConverter.test.js index 1bd20e07effd..97999832fc31 100644 --- a/addons/storyshots/storyshots-core/src/utils.test.js +++ b/addons/storyshots/storyshots-core/src/Stories2SnapsConverter.test.js @@ -1,10 +1,12 @@ -import { getPossibleStoriesFiles, getSnapshotFileName } from './utils'; +import Stories2SnapsConverter from './Stories2SnapsConverter'; + +const target = new Stories2SnapsConverter(); describe('getSnapshotFileName', () => { it('fileName is provided - snapshot is stored in __snapshots__ dir', () => { const context = { fileName: 'foo.js' }; - const result = getSnapshotFileName(context); + const result = target.getSnapshotFileName(context); const platformAgnosticResult = result.replace(/\\|\//g, '/'); expect(platformAgnosticResult).toBe('__snapshots__/foo.storyshot'); @@ -13,7 +15,7 @@ describe('getSnapshotFileName', () => { it('fileName with multiple extensions is provided - only the last extension is replaced', () => { const context = { fileName: 'foo.web.stories.js' }; - const result = getSnapshotFileName(context); + const result = target.getSnapshotFileName(context); const platformAgnosticResult = result.replace(/\\|\//g, '/'); expect(platformAgnosticResult).toBe('__snapshots__/foo.web.stories.storyshot'); @@ -22,7 +24,7 @@ describe('getSnapshotFileName', () => { it('fileName with dir is provided - __snapshots__ dir is created inside another dir', () => { const context = { fileName: 'test/foo.js' }; - const result = getSnapshotFileName(context); + const result = target.getSnapshotFileName(context); const platformAgnosticResult = result.replace(/\\|\//g, '/'); expect(platformAgnosticResult).toBe('test/__snapshots__/foo.storyshot'); @@ -33,7 +35,7 @@ describe('getPossibleStoriesFiles', () => { it('storyshots is provided and all the posible stories file names are returned', () => { const storyshots = 'test/__snapshots__/foo.web.stories.storyshot'; - const result = getPossibleStoriesFiles(storyshots); + const result = target.getPossibleStoriesFiles(storyshots); const platformAgnosticResult = result.map(path => path.replace(/\\|\//g, '/')); expect(platformAgnosticResult).toEqual([ diff --git a/addons/storyshots/storyshots-core/src/api/index.js b/addons/storyshots/storyshots-core/src/api/index.js new file mode 100644 index 000000000000..b6f190ab1f96 --- /dev/null +++ b/addons/storyshots/storyshots-core/src/api/index.js @@ -0,0 +1,90 @@ +import global, { describe } from 'global'; +import addons, { mockChannel } from '@storybook/addons'; +import snapshotsTests from './snapshotsTestsTemplate'; +import integrityTest from './integrityTestTemplate'; +import loadFramework from '../frameworkLoader'; +import getIntegrityOptions from '../getIntegrityOptions'; +import { snapshotWithOptions } from '../test-bodies'; +import Stories2SnapsConverter from '../Stories2SnapsConverter'; + +global.STORYBOOK_REACT_CLASSES = global.STORYBOOK_REACT_CLASSES || {}; + +const defaultStories2SnapsConverter = new Stories2SnapsConverter(); +const methods = ['beforeAll', 'beforeEach', 'afterEach', 'afterAll']; + +function ensureOptionsDefaults(options) { + const { + suite = 'Storyshots', + storyNameRegex, + storyKindRegex, + renderer, + serializer, + stories2snapsConverter = defaultStories2SnapsConverter, + test: testMethod = snapshotWithOptions({ renderer, serializer }), + } = options; + + const integrityOptions = getIntegrityOptions(options); + + return { + suite, + storyNameRegex, + storyKindRegex, + stories2snapsConverter, + testMethod, + integrityOptions, + }; +} + +function callTestMethodGlobals(testMethod) { + methods.forEach(method => { + if (typeof testMethod[method] === 'function') { + global[method](testMethod[method]); + } + }); +} + +function testStorySnapshots(options = {}) { + if (typeof describe !== 'function') { + throw new Error('testStorySnapshots is intended only to be used inside jest'); + } + + addons.setChannel(mockChannel()); + + const { storybook, framework, renderTree, renderShallowTree } = loadFramework(options); + const storiesGroups = storybook.getStorybook(); + + if (storiesGroups.length === 0) { + throw new Error('storyshots found 0 stories'); + } + + const { + suite, + storyNameRegex, + storyKindRegex, + stories2snapsConverter, + testMethod, + integrityOptions, + } = ensureOptionsDefaults(options); + + const testMethodParams = { + renderTree, + renderShallowTree, + stories2snapsConverter, + }; + + callTestMethodGlobals(testMethod); + + snapshotsTests({ + groups: storiesGroups, + suite, + framework, + storyKindRegex, + storyNameRegex, + testMethod, + testMethodParams, + }); + + integrityTest(integrityOptions, stories2snapsConverter); +} + +export default testStorySnapshots; diff --git a/addons/storyshots/storyshots-core/src/api/integrityTestTemplate.js b/addons/storyshots/storyshots-core/src/api/integrityTestTemplate.js new file mode 100644 index 000000000000..a1262924d504 --- /dev/null +++ b/addons/storyshots/storyshots-core/src/api/integrityTestTemplate.js @@ -0,0 +1,23 @@ +import fs from 'fs'; +import glob from 'glob'; + +function integrityTest(integrityOptions, stories2snapsConverter) { + if (integrityOptions === false) { + return; + } + + describe('Storyshots Integrity', () => { + test('Abandoned Storyshots', () => { + const snapshotExtension = stories2snapsConverter.getSnapshotExtension(); + const storyshots = glob.sync(`**/*${snapshotExtension}`, integrityOptions); + + const abandonedStoryshots = storyshots.filter(fileName => { + const possibleStoriesFiles = stories2snapsConverter.getPossibleStoriesFiles(fileName); + return !possibleStoriesFiles.some(fs.existsSync); + }); + expect(abandonedStoryshots).toHaveLength(0); + }); + }); +} + +export default integrityTest; diff --git a/addons/storyshots/storyshots-core/src/api/snapshotsTestsTemplate.js b/addons/storyshots/storyshots-core/src/api/snapshotsTestsTemplate.js new file mode 100644 index 000000000000..298b6608d037 --- /dev/null +++ b/addons/storyshots/storyshots-core/src/api/snapshotsTestsTemplate.js @@ -0,0 +1,45 @@ +import { describe, it } from 'global'; + +function snapshotTest({ story, kind, fileName, framework, testMethod, testMethodParams }) { + it(story.name, () => { + const context = { fileName, kind, story: story.name, framework }; + + return testMethod({ + story, + context, + ...testMethodParams, + }); + }); +} + +function snapshotTestSuite({ kind, stories, suite, storyNameRegex, ...restParams }) { + describe(suite, () => { + describe(kind, () => { + // eslint-disable-next-line + for (const story of stories) { + if (storyNameRegex && !story.name.match(storyNameRegex)) { + // eslint-disable-next-line + continue; + } + + snapshotTest({ story, kind, ...restParams }); + } + }); + }); +} + +function snapshotsTests({ groups, storyKindRegex, ...restParams }) { + // eslint-disable-next-line + for (const group of groups) { + const { fileName, kind, stories } = group; + + if (storyKindRegex && !kind.match(storyKindRegex)) { + // eslint-disable-next-line + continue; + } + + snapshotTestSuite({ stories, kind, fileName, ...restParams }); + } +} + +export default snapshotsTests; diff --git a/addons/storyshots/storyshots-core/src/index.js b/addons/storyshots/storyshots-core/src/index.js index cce8f0b8acca..eaf733ce4347 100644 --- a/addons/storyshots/storyshots-core/src/index.js +++ b/addons/storyshots/storyshots-core/src/index.js @@ -1,108 +1,7 @@ -import fs from 'fs'; -import glob from 'glob'; -import global, { describe, it } from 'global'; -import addons, { mockChannel } from '@storybook/addons'; -import loadFramework from './frameworkLoader'; -import getIntegrityOptions from './getIntegrityOptions'; -import { getPossibleStoriesFiles, getSnapshotFileName } from './utils'; +import Stories2SnapsConverter from './Stories2SnapsConverter'; +import api from './api'; -import { - multiSnapshotWithOptions, - snapshotWithOptions, - snapshot, - shallowSnapshot, - renderOnly, - renderWithOptions, -} from './test-bodies'; +export * from './test-bodies'; +export { Stories2SnapsConverter }; -global.STORYBOOK_REACT_CLASSES = global.STORYBOOK_REACT_CLASSES || {}; - -export { - getSnapshotFileName, - snapshot, - multiSnapshotWithOptions, - snapshotWithOptions, - shallowSnapshot, - renderOnly, - renderWithOptions, -}; - -const methods = ['beforeAll', 'beforeEach', 'afterEach', 'afterAll']; - -export default function testStorySnapshots(options = {}) { - if (typeof describe !== 'function') { - throw new Error('testStorySnapshots is intended only to be used inside jest'); - } - - addons.setChannel(mockChannel()); - - const { storybook, framework, renderTree, renderShallowTree } = loadFramework(options); - const stories = storybook.getStorybook(); - - if (stories.length === 0) { - throw new Error('storyshots found 0 stories'); - } - - const { - suite = 'Storyshots', - storyNameRegex, - storyKindRegex, - renderer, - serializer, - test: testMethod = snapshotWithOptions({ renderer, serializer }), - } = options; - - const integrityOptions = getIntegrityOptions(options); - - methods.forEach(method => { - if (typeof testMethod[method] === 'function') { - global[method](testMethod[method]); - } - }); - - // eslint-disable-next-line - for (const group of stories) { - const { fileName, kind } = group; - - if (storyKindRegex && !kind.match(storyKindRegex)) { - // eslint-disable-next-line - continue; - } - - describe(suite, () => { - describe(kind, () => { - // eslint-disable-next-line - for (const story of group.stories) { - if (storyNameRegex && !story.name.match(storyNameRegex)) { - // eslint-disable-next-line - continue; - } - - it(story.name, () => { - const context = { fileName, kind, story: story.name, framework }; - return testMethod({ - story, - context, - renderTree, - renderShallowTree, - }); - }); - } - }); - }); - } - - if (integrityOptions !== false) { - describe('Storyshots Integrity', () => { - test('Abandoned Storyshots', () => { - const storyshots = glob.sync('**/*.storyshot', integrityOptions); - - const abandonedStoryshots = storyshots.filter(fileName => { - const possibleStoriesFiles = getPossibleStoriesFiles(fileName); - return !possibleStoriesFiles.some(fs.existsSync); - }); - expect(abandonedStoryshots).toHaveLength(0); - }); - }); - } -} +export default api; diff --git a/addons/storyshots/storyshots-core/src/test-bodies.js b/addons/storyshots/storyshots-core/src/test-bodies.js index f7416fca65b2..762c6dfc6933 100644 --- a/addons/storyshots/storyshots-core/src/test-bodies.js +++ b/addons/storyshots/storyshots-core/src/test-bodies.js @@ -1,5 +1,4 @@ import 'jest-specific-snapshot'; -import { getSnapshotFileName } from './utils'; export const snapshotWithOptions = options => ({ story, @@ -28,12 +27,17 @@ export const snapshotWithOptions = options => ({ return match(result); }; -export const multiSnapshotWithOptions = options => ({ story, context, renderTree }) => +export const multiSnapshotWithOptions = options => ({ + story, + context, + renderTree, + stories2snapsConverter, +}) => snapshotWithOptions(options)({ story, context, renderTree, - snapshotFileName: getSnapshotFileName(context), + snapshotFileName: stories2snapsConverter.getSnapshotFileName(context), }); export function shallowSnapshot({ story, context, renderShallowTree, options = {} }) { diff --git a/addons/storyshots/storyshots-core/src/utils.js b/addons/storyshots/storyshots-core/src/utils.js deleted file mode 100644 index fa1281d63c02..000000000000 --- a/addons/storyshots/storyshots-core/src/utils.js +++ /dev/null @@ -1,27 +0,0 @@ -import path from 'path'; - -function getStoryshotFile(fileName) { - const { dir, name } = path.parse(fileName); - return path.format({ dir: path.join(dir, '__snapshots__'), name, ext: '.storyshot' }); -} - -export function getPossibleStoriesFiles(storyshotFile) { - const { dir, name } = path.parse(storyshotFile); - - return [ - path.format({ dir: path.dirname(dir), name, ext: '.js' }), - path.format({ dir: path.dirname(dir), name, ext: '.jsx' }), - path.format({ dir: path.dirname(dir), name, ext: '.ts' }), - path.format({ dir: path.dirname(dir), name, ext: '.tsx' }), - ]; -} - -export function getSnapshotFileName(context) { - const { fileName } = context; - - if (!fileName) { - return null; - } - - return getStoryshotFile(fileName); -} From 19b269eb010c31984d6268f1661c400fbb29b525 Mon Sep 17 00:00:00 2001 From: igor-dv Date: Sun, 10 Jun 2018 19:28:30 +0300 Subject: [PATCH 02/10] WIP restructuring --- .../storyshots-core/src/{ => api}/getIntegrityOptions.js | 0 addons/storyshots/storyshots-core/src/api/index.js | 6 +++--- .../src/{ => frameworks}/angular/app.component.ts | 0 .../src/{ => frameworks}/angular/app.token.ts | 0 .../storyshots-core/src/{ => frameworks}/angular/helpers.ts | 0 .../storyshots-core/src/{ => frameworks}/angular/loader.js | 0 .../src/{ => frameworks}/angular/renderTree.js | 0 .../storyshots-core/src/{ => frameworks}/angular/types.ts | 0 .../storyshots-core/src/{ => frameworks}/config-loader.js | 0 .../storyshots-core/src/{ => frameworks}/frameworkLoader.js | 0 .../storyshots-core/src/{ => frameworks}/hasDependency.js | 0 .../storyshots-core/src/{ => frameworks}/html/loader.js | 0 .../storyshots-core/src/{ => frameworks}/html/renderTree.js | 0 .../storyshots-core/src/{ => frameworks}/react/loader.js | 0 .../src/{ => frameworks}/react/renderShallowTree.js | 0 .../src/{ => frameworks}/react/renderTree.js | 0 .../storyshots-core/src/{ => frameworks}/require_context.js | 0 .../storyshots-core/src/{ => frameworks}/rn/loader.js | 0 .../storyshots-core/src/{ => frameworks}/vue/loader.js | 0 .../storyshots-core/src/{ => frameworks}/vue/renderTree.js | 0 20 files changed, 3 insertions(+), 3 deletions(-) rename addons/storyshots/storyshots-core/src/{ => api}/getIntegrityOptions.js (100%) rename addons/storyshots/storyshots-core/src/{ => frameworks}/angular/app.component.ts (100%) rename addons/storyshots/storyshots-core/src/{ => frameworks}/angular/app.token.ts (100%) rename addons/storyshots/storyshots-core/src/{ => frameworks}/angular/helpers.ts (100%) rename addons/storyshots/storyshots-core/src/{ => frameworks}/angular/loader.js (100%) rename addons/storyshots/storyshots-core/src/{ => frameworks}/angular/renderTree.js (100%) rename addons/storyshots/storyshots-core/src/{ => frameworks}/angular/types.ts (100%) rename addons/storyshots/storyshots-core/src/{ => frameworks}/config-loader.js (100%) rename addons/storyshots/storyshots-core/src/{ => frameworks}/frameworkLoader.js (100%) rename addons/storyshots/storyshots-core/src/{ => frameworks}/hasDependency.js (100%) rename addons/storyshots/storyshots-core/src/{ => frameworks}/html/loader.js (100%) rename addons/storyshots/storyshots-core/src/{ => frameworks}/html/renderTree.js (100%) rename addons/storyshots/storyshots-core/src/{ => frameworks}/react/loader.js (100%) rename addons/storyshots/storyshots-core/src/{ => frameworks}/react/renderShallowTree.js (100%) rename addons/storyshots/storyshots-core/src/{ => frameworks}/react/renderTree.js (100%) rename addons/storyshots/storyshots-core/src/{ => frameworks}/require_context.js (100%) rename addons/storyshots/storyshots-core/src/{ => frameworks}/rn/loader.js (100%) rename addons/storyshots/storyshots-core/src/{ => frameworks}/vue/loader.js (100%) rename addons/storyshots/storyshots-core/src/{ => frameworks}/vue/renderTree.js (100%) diff --git a/addons/storyshots/storyshots-core/src/getIntegrityOptions.js b/addons/storyshots/storyshots-core/src/api/getIntegrityOptions.js similarity index 100% rename from addons/storyshots/storyshots-core/src/getIntegrityOptions.js rename to addons/storyshots/storyshots-core/src/api/getIntegrityOptions.js diff --git a/addons/storyshots/storyshots-core/src/api/index.js b/addons/storyshots/storyshots-core/src/api/index.js index b6f190ab1f96..a1ae32a627ee 100644 --- a/addons/storyshots/storyshots-core/src/api/index.js +++ b/addons/storyshots/storyshots-core/src/api/index.js @@ -2,10 +2,10 @@ import global, { describe } from 'global'; import addons, { mockChannel } from '@storybook/addons'; import snapshotsTests from './snapshotsTestsTemplate'; import integrityTest from './integrityTestTemplate'; -import loadFramework from '../frameworkLoader'; -import getIntegrityOptions from '../getIntegrityOptions'; -import { snapshotWithOptions } from '../test-bodies'; +import getIntegrityOptions from './getIntegrityOptions'; +import loadFramework from '../frameworks/frameworkLoader'; import Stories2SnapsConverter from '../Stories2SnapsConverter'; +import { snapshotWithOptions } from '../test-bodies'; global.STORYBOOK_REACT_CLASSES = global.STORYBOOK_REACT_CLASSES || {}; diff --git a/addons/storyshots/storyshots-core/src/angular/app.component.ts b/addons/storyshots/storyshots-core/src/frameworks/angular/app.component.ts similarity index 100% rename from addons/storyshots/storyshots-core/src/angular/app.component.ts rename to addons/storyshots/storyshots-core/src/frameworks/angular/app.component.ts diff --git a/addons/storyshots/storyshots-core/src/angular/app.token.ts b/addons/storyshots/storyshots-core/src/frameworks/angular/app.token.ts similarity index 100% rename from addons/storyshots/storyshots-core/src/angular/app.token.ts rename to addons/storyshots/storyshots-core/src/frameworks/angular/app.token.ts diff --git a/addons/storyshots/storyshots-core/src/angular/helpers.ts b/addons/storyshots/storyshots-core/src/frameworks/angular/helpers.ts similarity index 100% rename from addons/storyshots/storyshots-core/src/angular/helpers.ts rename to addons/storyshots/storyshots-core/src/frameworks/angular/helpers.ts diff --git a/addons/storyshots/storyshots-core/src/angular/loader.js b/addons/storyshots/storyshots-core/src/frameworks/angular/loader.js similarity index 100% rename from addons/storyshots/storyshots-core/src/angular/loader.js rename to addons/storyshots/storyshots-core/src/frameworks/angular/loader.js diff --git a/addons/storyshots/storyshots-core/src/angular/renderTree.js b/addons/storyshots/storyshots-core/src/frameworks/angular/renderTree.js similarity index 100% rename from addons/storyshots/storyshots-core/src/angular/renderTree.js rename to addons/storyshots/storyshots-core/src/frameworks/angular/renderTree.js diff --git a/addons/storyshots/storyshots-core/src/angular/types.ts b/addons/storyshots/storyshots-core/src/frameworks/angular/types.ts similarity index 100% rename from addons/storyshots/storyshots-core/src/angular/types.ts rename to addons/storyshots/storyshots-core/src/frameworks/angular/types.ts diff --git a/addons/storyshots/storyshots-core/src/config-loader.js b/addons/storyshots/storyshots-core/src/frameworks/config-loader.js similarity index 100% rename from addons/storyshots/storyshots-core/src/config-loader.js rename to addons/storyshots/storyshots-core/src/frameworks/config-loader.js diff --git a/addons/storyshots/storyshots-core/src/frameworkLoader.js b/addons/storyshots/storyshots-core/src/frameworks/frameworkLoader.js similarity index 100% rename from addons/storyshots/storyshots-core/src/frameworkLoader.js rename to addons/storyshots/storyshots-core/src/frameworks/frameworkLoader.js diff --git a/addons/storyshots/storyshots-core/src/hasDependency.js b/addons/storyshots/storyshots-core/src/frameworks/hasDependency.js similarity index 100% rename from addons/storyshots/storyshots-core/src/hasDependency.js rename to addons/storyshots/storyshots-core/src/frameworks/hasDependency.js diff --git a/addons/storyshots/storyshots-core/src/html/loader.js b/addons/storyshots/storyshots-core/src/frameworks/html/loader.js similarity index 100% rename from addons/storyshots/storyshots-core/src/html/loader.js rename to addons/storyshots/storyshots-core/src/frameworks/html/loader.js diff --git a/addons/storyshots/storyshots-core/src/html/renderTree.js b/addons/storyshots/storyshots-core/src/frameworks/html/renderTree.js similarity index 100% rename from addons/storyshots/storyshots-core/src/html/renderTree.js rename to addons/storyshots/storyshots-core/src/frameworks/html/renderTree.js diff --git a/addons/storyshots/storyshots-core/src/react/loader.js b/addons/storyshots/storyshots-core/src/frameworks/react/loader.js similarity index 100% rename from addons/storyshots/storyshots-core/src/react/loader.js rename to addons/storyshots/storyshots-core/src/frameworks/react/loader.js diff --git a/addons/storyshots/storyshots-core/src/react/renderShallowTree.js b/addons/storyshots/storyshots-core/src/frameworks/react/renderShallowTree.js similarity index 100% rename from addons/storyshots/storyshots-core/src/react/renderShallowTree.js rename to addons/storyshots/storyshots-core/src/frameworks/react/renderShallowTree.js diff --git a/addons/storyshots/storyshots-core/src/react/renderTree.js b/addons/storyshots/storyshots-core/src/frameworks/react/renderTree.js similarity index 100% rename from addons/storyshots/storyshots-core/src/react/renderTree.js rename to addons/storyshots/storyshots-core/src/frameworks/react/renderTree.js diff --git a/addons/storyshots/storyshots-core/src/require_context.js b/addons/storyshots/storyshots-core/src/frameworks/require_context.js similarity index 100% rename from addons/storyshots/storyshots-core/src/require_context.js rename to addons/storyshots/storyshots-core/src/frameworks/require_context.js diff --git a/addons/storyshots/storyshots-core/src/rn/loader.js b/addons/storyshots/storyshots-core/src/frameworks/rn/loader.js similarity index 100% rename from addons/storyshots/storyshots-core/src/rn/loader.js rename to addons/storyshots/storyshots-core/src/frameworks/rn/loader.js diff --git a/addons/storyshots/storyshots-core/src/vue/loader.js b/addons/storyshots/storyshots-core/src/frameworks/vue/loader.js similarity index 100% rename from addons/storyshots/storyshots-core/src/vue/loader.js rename to addons/storyshots/storyshots-core/src/frameworks/vue/loader.js diff --git a/addons/storyshots/storyshots-core/src/vue/renderTree.js b/addons/storyshots/storyshots-core/src/frameworks/vue/renderTree.js similarity index 100% rename from addons/storyshots/storyshots-core/src/vue/renderTree.js rename to addons/storyshots/storyshots-core/src/frameworks/vue/renderTree.js From 120af7cedb6d171bca5a1b3dd066f381196d8ead Mon Sep 17 00:00:00 2001 From: igor-dv Date: Sun, 10 Jun 2018 19:31:37 +0300 Subject: [PATCH 03/10] Few minor cleanups --- .../storyshots-core/src/api/integrityTestTemplate.js | 3 ++- .../storyshots-core/src/api/snapshotsTestsTemplate.js | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/addons/storyshots/storyshots-core/src/api/integrityTestTemplate.js b/addons/storyshots/storyshots-core/src/api/integrityTestTemplate.js index a1262924d504..7a47470a81eb 100644 --- a/addons/storyshots/storyshots-core/src/api/integrityTestTemplate.js +++ b/addons/storyshots/storyshots-core/src/api/integrityTestTemplate.js @@ -1,5 +1,6 @@ import fs from 'fs'; import glob from 'glob'; +import { describe, it } from 'global'; function integrityTest(integrityOptions, stories2snapsConverter) { if (integrityOptions === false) { @@ -7,7 +8,7 @@ function integrityTest(integrityOptions, stories2snapsConverter) { } describe('Storyshots Integrity', () => { - test('Abandoned Storyshots', () => { + it('Abandoned Storyshots', () => { const snapshotExtension = stories2snapsConverter.getSnapshotExtension(); const storyshots = glob.sync(`**/*${snapshotExtension}`, integrityOptions); diff --git a/addons/storyshots/storyshots-core/src/api/snapshotsTestsTemplate.js b/addons/storyshots/storyshots-core/src/api/snapshotsTestsTemplate.js index 298b6608d037..7c8757f7cd03 100644 --- a/addons/storyshots/storyshots-core/src/api/snapshotsTestsTemplate.js +++ b/addons/storyshots/storyshots-core/src/api/snapshotsTestsTemplate.js @@ -1,8 +1,10 @@ import { describe, it } from 'global'; function snapshotTest({ story, kind, fileName, framework, testMethod, testMethodParams }) { - it(story.name, () => { - const context = { fileName, kind, story: story.name, framework }; + const { name } = story; + + it(name, () => { + const context = { fileName, kind, story: name, framework }; return testMethod({ story, From 36d6a9c69feb8b13289c8f15ab517a0667b60fdb Mon Sep 17 00:00:00 2001 From: igor-dv Date: Sun, 10 Jun 2018 19:59:11 +0300 Subject: [PATCH 04/10] Change "export * from x" to manually importing and exporting named exports. --- .../storyshots/storyshots-core/src/index.js | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/addons/storyshots/storyshots-core/src/index.js b/addons/storyshots/storyshots-core/src/index.js index eaf733ce4347..83867027f1e1 100644 --- a/addons/storyshots/storyshots-core/src/index.js +++ b/addons/storyshots/storyshots-core/src/index.js @@ -1,7 +1,22 @@ import Stories2SnapsConverter from './Stories2SnapsConverter'; import api from './api'; +import { + snapshotWithOptions, + multiSnapshotWithOptions, + renderOnly, + renderWithOptions, + shallowSnapshot, + snapshot, +} from './test-bodies'; -export * from './test-bodies'; -export { Stories2SnapsConverter }; +export { + Stories2SnapsConverter, + snapshotWithOptions, + multiSnapshotWithOptions, + renderOnly, + renderWithOptions, + shallowSnapshot, + snapshot, +}; export default api; From 52ef4c4fd302ab6db62995e41d8a043f4f76def2 Mon Sep 17 00:00:00 2001 From: igor-dv Date: Sun, 10 Jun 2018 20:05:52 +0300 Subject: [PATCH 05/10] Import framework loaders dynamically --- .../src/frameworks/frameworkLoader.js | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/addons/storyshots/storyshots-core/src/frameworks/frameworkLoader.js b/addons/storyshots/storyshots-core/src/frameworks/frameworkLoader.js index 0a8cdd51df07..c818c2cea6a0 100644 --- a/addons/storyshots/storyshots-core/src/frameworks/frameworkLoader.js +++ b/addons/storyshots/storyshots-core/src/frameworks/frameworkLoader.js @@ -1,12 +1,24 @@ -import loaderReact from './react/loader'; -import loaderRn from './rn/loader'; -import loaderAngular from './angular/loader'; -import loaderVue from './vue/loader'; -import loaderHTML from './html/loader'; +/* eslint-disable global-require,import/no-dynamic-require */ +import fs from 'fs'; +import path from 'path'; -const loaders = [loaderReact, loaderAngular, loaderRn, loaderVue, loaderHTML]; +const loaderScriptName = 'loader.js'; + +const isDirectory = source => fs.lstatSync(source).isDirectory(); + +function getLoaders() { + return fs + .readdirSync(__dirname) + .map(name => path.join(__dirname, name)) + .filter(isDirectory) + .map(framework => path.join(framework, loaderScriptName)) + .filter(fs.existsSync) + .map(loader => require(loader).default); +} function loadFramework(options) { + const loaders = getLoaders(); + const loader = loaders.find(frameworkLoader => frameworkLoader.test(options)); if (!loader) { From 0fbb935ee1f4aeded7a60d3975ce9c1faa68331d Mon Sep 17 00:00:00 2001 From: igor-dv Date: Sun, 10 Jun 2018 22:52:35 +0300 Subject: [PATCH 06/10] Move appOptions imports to the load methods --- .../storyshots-core/src/frameworks/angular/loader.js | 4 ++-- .../storyshots-core/src/frameworks/config-loader.js | 4 ++-- .../storyshots/storyshots-core/src/frameworks/html/loader.js | 4 ++-- .../storyshots/storyshots-core/src/frameworks/react/loader.js | 4 ++-- .../storyshots/storyshots-core/src/frameworks/vue/loader.js | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/addons/storyshots/storyshots-core/src/frameworks/angular/loader.js b/addons/storyshots/storyshots-core/src/frameworks/angular/loader.js index 728c7c060127..b5031a448a63 100644 --- a/addons/storyshots/storyshots-core/src/frameworks/angular/loader.js +++ b/addons/storyshots/storyshots-core/src/frameworks/angular/loader.js @@ -1,5 +1,3 @@ -import appOptions from '@storybook/angular/options'; - import runWithRequireContext from '../require_context'; import hasDependency from '../hasDependency'; import loadConfig from '../config-loader'; @@ -22,6 +20,8 @@ function test(options) { function load(options) { setupAngularJestPreset(); + const appOptions = require.requireActual('@storybook/angular/options').default; + const { content, contextOpts } = loadConfig({ configDirPath: options.configPath, appOptions, diff --git a/addons/storyshots/storyshots-core/src/frameworks/config-loader.js b/addons/storyshots/storyshots-core/src/frameworks/config-loader.js index f3e0707b94f2..2ec25a1cd872 100644 --- a/addons/storyshots/storyshots-core/src/frameworks/config-loader.js +++ b/addons/storyshots/storyshots-core/src/frameworks/config-loader.js @@ -11,11 +11,11 @@ function getConfigContent({ resolvedConfigDirPath, configPath, appOptions }) { return babel.transformFileSync(configPath, babelConfig).code; } -function load({ configDirPath, babelConfigPath }) { +function load({ configDirPath, appOptions }) { const resolvedConfigDirPath = path.resolve(configDirPath || '.storybook'); const configPath = path.join(resolvedConfigDirPath, 'config.js'); - const content = getConfigContent({ resolvedConfigDirPath, configPath, babelConfigPath }); + const content = getConfigContent({ resolvedConfigDirPath, configPath, appOptions }); const contextOpts = { filename: configPath, dirname: resolvedConfigDirPath }; return { diff --git a/addons/storyshots/storyshots-core/src/frameworks/html/loader.js b/addons/storyshots/storyshots-core/src/frameworks/html/loader.js index 4f6a75b7e386..bfd7f007774e 100644 --- a/addons/storyshots/storyshots-core/src/frameworks/html/loader.js +++ b/addons/storyshots/storyshots-core/src/frameworks/html/loader.js @@ -1,5 +1,3 @@ -import appOptions from '@storybook/angular/options'; - import global from 'global'; import runWithRequireContext from '../require_context'; import loadConfig from '../config-loader'; @@ -11,6 +9,8 @@ function test(options) { function load(options) { global.STORYBOOK_ENV = 'html'; + const appOptions = require.requireActual('@storybook/html/options').default; + const { content, contextOpts } = loadConfig({ configDirPath: options.configPath, appOptions, diff --git a/addons/storyshots/storyshots-core/src/frameworks/react/loader.js b/addons/storyshots/storyshots-core/src/frameworks/react/loader.js index b0dc7b963ded..d240a4680720 100644 --- a/addons/storyshots/storyshots-core/src/frameworks/react/loader.js +++ b/addons/storyshots/storyshots-core/src/frameworks/react/loader.js @@ -1,5 +1,3 @@ -import appOptions from '@storybook/react/options'; - import runWithRequireContext from '../require_context'; import hasDependency from '../hasDependency'; import loadConfig from '../config-loader'; @@ -9,6 +7,8 @@ function test(options) { } function load(options) { + const appOptions = require.requireActual('@storybook/react/options').default; + const { content, contextOpts } = loadConfig({ configDirPath: options.configPath, appOptions, diff --git a/addons/storyshots/storyshots-core/src/frameworks/vue/loader.js b/addons/storyshots/storyshots-core/src/frameworks/vue/loader.js index 48627ce10a32..79b2119bc214 100644 --- a/addons/storyshots/storyshots-core/src/frameworks/vue/loader.js +++ b/addons/storyshots/storyshots-core/src/frameworks/vue/loader.js @@ -1,5 +1,3 @@ -import appOptions from '@storybook/angular/options'; - import global from 'global'; import runWithRequireContext from '../require_context'; import hasDependency from '../hasDependency'; @@ -17,6 +15,8 @@ function load(options) { global.STORYBOOK_ENV = 'vue'; mockVueToIncludeCompiler(); + const appOptions = require.requireActual('@storybook/vue/options').default; + const { content, contextOpts } = loadConfig({ configDirPath: options.configPath, appOptions, From fce815a371943914e177e940c51d02cf2aa90be0 Mon Sep 17 00:00:00 2001 From: igor-dv Date: Sun, 10 Jun 2018 23:57:10 +0300 Subject: [PATCH 07/10] Add docs for the stories2snapsConverters --- addons/storyshots/storyshots-core/README.md | 27 +++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/addons/storyshots/storyshots-core/README.md b/addons/storyshots/storyshots-core/README.md index 1304e1376f03..1c9e5c1ce67c 100644 --- a/addons/storyshots/storyshots-core/README.md +++ b/addons/storyshots/storyshots-core/README.md @@ -307,9 +307,32 @@ initStoryshots({ Take a snapshot of a shallow-rendered version of the component. Note that this option will be overriden if you pass a `renderer` option. -### `getSnapshotFileName` +### `stories2snapsConverter` +This parameter should be an instance of the [`Stories2SnapsConverter`](src/Stories2SnapsConverter) (or a derived from it) Class that is used to convert story-file name to snapshot-file name and vice versa. -Utility function used in `multiSnapshotWithOptions`. This is made available for users who implement custom test functions that also want to take advantage of multi-file storyshots. +By default, the instance of this class is created with these default options: + +```js +{ + snapshotsDirName: '__snapshots__', + snapshotExtension: '.storyshot', + storiesExtensions: ['.js', '.jsx', '.ts', '.tsx'], +} +``` + +This class might be overridden to extend the existing conversion functionality or instantiated to provide different options: + +```js +import initStoryshots, { Stories2SnapsConverter } from '@storybook/addon-storyshots'; + +initStoryshots({ + stories2snapsConverter: new Stories2SnapsConverter({ + snapshotExtension: '.storypuke', + storiesExtensions: ['.foo'], + }), +}); + +``` ###### Example: From 6644743ce35b36b91b5f331280e0776f6451bd64 Mon Sep 17 00:00:00 2001 From: igor-dv Date: Sun, 10 Jun 2018 23:59:25 +0300 Subject: [PATCH 08/10] Fix README.md --- addons/storyshots/storyshots-core/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/storyshots/storyshots-core/README.md b/addons/storyshots/storyshots-core/README.md index 1c9e5c1ce67c..787ac58563c3 100644 --- a/addons/storyshots/storyshots-core/README.md +++ b/addons/storyshots/storyshots-core/README.md @@ -308,7 +308,7 @@ initStoryshots({ Take a snapshot of a shallow-rendered version of the component. Note that this option will be overriden if you pass a `renderer` option. ### `stories2snapsConverter` -This parameter should be an instance of the [`Stories2SnapsConverter`](src/Stories2SnapsConverter) (or a derived from it) Class that is used to convert story-file name to snapshot-file name and vice versa. +This parameter should be an instance of the [`Stories2SnapsConverter`](src/Stories2SnapsConverter.js) (or a derived from it) Class that is used to convert story-file name to snapshot-file name and vice versa. By default, the instance of this class is created with these default options: From e43a73314ee459f9f34b5071bd73aebe7862141c Mon Sep 17 00:00:00 2001 From: igor-dv Date: Mon, 11 Jun 2018 02:12:25 +0300 Subject: [PATCH 09/10] Mention about imageSnapshot in the MIGRATION.md --- MIGRATION.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MIGRATION.md b/MIGRATION.md index e514b5714e1b..40c0fbe37d21 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -6,6 +6,7 @@ - [Keyboard shortcuts moved](#keyboard-shortcuts-moved) - [Removed addWithInfo](#removed-add-with-info) - [Removed RN addons](#removed-rn-addons) + - [Storyshots imageSnapshot test function moved to a separate package](#storyshots-imagesnapshot-moved) - [From version 3.3.x to 3.4.x](#from-version-33x-to-34x) - [From version 3.2.x to 3.3.x](#from-version-32x-to-33x) - [Refactored Knobs](#refactored-knobs) @@ -40,6 +41,11 @@ With 4.0 as our first major release in over a year, we've collected a lot of cle The `@storybook/react-native` had built-in addons (`addon-actions` and `addon-links`) that have been marked as deprecated since 3.x. They have been fully removed in 4.x. If your project still uses the built-ins, you'll need to add explicit dependencies on `@storybook/addon-actions` and/or `@storybook/addon-links` and import directly from those packages. +### Storyshots imageSnapshot moved + +`imageSnapshot` test function was extracted from `addon-storyshots` +and moved to a new package - `addon-storyshots-puppeteer` + ## From version 3.3.x to 3.4.x There are no expected breaking changes in the 3.4.x release, but 3.4 contains a major refactor to make it easier to support new frameworks, and we will document any breaking changes here if they arise. From 3063a776d73bf2955fe727319493f09952d35a49 Mon Sep 17 00:00:00 2001 From: igor-dv Date: Tue, 12 Jun 2018 17:03:22 +0300 Subject: [PATCH 10/10] MIGRATION.md and README.md improvements --- MIGRATION.md | 7 +-- addons/storyshots/storyshots-core/README.md | 54 +++++++++++---------- 2 files changed, 33 insertions(+), 28 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 40c0fbe37d21..08e126ac8561 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -7,6 +7,7 @@ - [Removed addWithInfo](#removed-add-with-info) - [Removed RN addons](#removed-rn-addons) - [Storyshots imageSnapshot test function moved to a separate package](#storyshots-imagesnapshot-moved) + - [Storyshots changes](#storyshots-changes) - [From version 3.3.x to 3.4.x](#from-version-33x-to-34x) - [From version 3.2.x to 3.3.x](#from-version-32x-to-33x) - [Refactored Knobs](#refactored-knobs) @@ -41,10 +42,10 @@ With 4.0 as our first major release in over a year, we've collected a lot of cle The `@storybook/react-native` had built-in addons (`addon-actions` and `addon-links`) that have been marked as deprecated since 3.x. They have been fully removed in 4.x. If your project still uses the built-ins, you'll need to add explicit dependencies on `@storybook/addon-actions` and/or `@storybook/addon-links` and import directly from those packages. -### Storyshots imageSnapshot moved +### Storyshots Changes -`imageSnapshot` test function was extracted from `addon-storyshots` -and moved to a new package - `addon-storyshots-puppeteer` +1. `imageSnapshot` test function was extracted from `addon-storyshots` and moved to a new package - `addon-storyshots-puppeteer` that now will be dependant on puppeteer +2. `getSnapshotFileName` export was replaced with the `Stories2SnapsConverter` class that now can be overridden for a custom implementation of the snapshot-name generation ## From version 3.3.x to 3.4.x diff --git a/addons/storyshots/storyshots-core/README.md b/addons/storyshots/storyshots-core/README.md index 787ac58563c3..a993e3a9322b 100644 --- a/addons/storyshots/storyshots-core/README.md +++ b/addons/storyshots/storyshots-core/README.md @@ -268,6 +268,33 @@ initStoryshots({ This option only needs to be set if the default `snapshotSerializers` is not set in your jest config. +### `stories2snapsConverter` +This parameter should be an instance of the [`Stories2SnapsConverter`](src/Stories2SnapsConverter.js) (or a derived from it) Class that is used to convert story-file name to snapshot-file name and vice versa. + +By default, the instance of this class is created with these default options: + +```js +{ + snapshotsDirName: '__snapshots__', + snapshotExtension: '.storyshot', + storiesExtensions: ['.js', '.jsx', '.ts', '.tsx'], +} +``` + +This class might be overridden to extend the existing conversion functionality or instantiated to provide different options: + +```js +import initStoryshots, { Stories2SnapsConverter } from '@storybook/addon-storyshots'; + +initStoryshots({ + stories2snapsConverter: new Stories2SnapsConverter({ + snapshotExtension: '.storypuke', + storiesExtensions: ['.foo'], + }), +}); + +``` + ## Exports Apart from the default export (`initStoryshots`), Storyshots also exports some named test functions (see the `test` option above): @@ -307,32 +334,9 @@ initStoryshots({ Take a snapshot of a shallow-rendered version of the component. Note that this option will be overriden if you pass a `renderer` option. -### `stories2snapsConverter` -This parameter should be an instance of the [`Stories2SnapsConverter`](src/Stories2SnapsConverter.js) (or a derived from it) Class that is used to convert story-file name to snapshot-file name and vice versa. - -By default, the instance of this class is created with these default options: - -```js -{ - snapshotsDirName: '__snapshots__', - snapshotExtension: '.storyshot', - storiesExtensions: ['.js', '.jsx', '.ts', '.tsx'], -} -``` - -This class might be overridden to extend the existing conversion functionality or instantiated to provide different options: +### `Stories2SnapsConverter` -```js -import initStoryshots, { Stories2SnapsConverter } from '@storybook/addon-storyshots'; - -initStoryshots({ - stories2snapsConverter: new Stories2SnapsConverter({ - snapshotExtension: '.storypuke', - storiesExtensions: ['.foo'], - }), -}); - -``` +This is a class that generates snapshot's name based on the story (kind, story & filename) and vice versa. ###### Example: