Skip to content

Commit

Permalink
Merge pull request #7674 from storybookjs/ts-migration/addon-storyshots
Browse files Browse the repository at this point in the history
Migrate addon storyshots to TS
  • Loading branch information
gaetanmaisse authored Oct 8, 2019
2 parents fe0be1f + aad29ed commit 38d8c4f
Show file tree
Hide file tree
Showing 44 changed files with 393 additions and 155 deletions.
5 changes: 5 additions & 0 deletions addons/storyshots/storyshots-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"README.md"
],
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build-storybook": "build-storybook",
"example": "jest storyshot.test",
Expand All @@ -31,6 +32,10 @@
"dependencies": {
"@jest/transform": "^24.9.0",
"@storybook/addons": "5.3.0-alpha.13",
"@storybook/client-api": "5.3.0-alpha.13",
"@types/glob": "^7.1.1",
"@types/jest": "^24.0.16",
"@types/jest-specific-snapshot": "^0.5.3",
"core-js": "^3.0.1",
"glob": "^7.1.3",
"global": "^4.3.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Stories2SnapsConverter from './Stories2SnapsConverter';
import { Stories2SnapsConverter } from './Stories2SnapsConverter';

const target = new Stories2SnapsConverter();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
import path from 'path';
import dedent from 'ts-dedent';

const defaultOptions = {
const defaultOptions: Stories2SnapsConverterOptions = {
snapshotsDirName: '__snapshots__',
snapshotExtension: '.storyshot',
storiesExtensions: ['.js', '.jsx', '.ts', '.tsx'],
};

class DefaultStories2SnapsConverter {
constructor(options = {}) {
export interface Stories2SnapsConverterOptions {
storiesExtensions: string[];
snapshotExtension: string;
snapshotsDirName: string;
}

export class Stories2SnapsConverter {
options: Stories2SnapsConverterOptions;

constructor(options: Partial<Stories2SnapsConverterOptions> = {}) {
this.options = {
...defaultOptions,
...options,
Expand All @@ -17,14 +25,14 @@ class DefaultStories2SnapsConverter {

getSnapshotExtension = () => this.options.snapshotExtension;

getStoryshotFile(fileName) {
getStoryshotFile(fileName: string) {
const { dir, name } = path.parse(fileName);
const { snapshotsDirName, snapshotExtension } = this.options;

return path.format({ dir: path.join(dir, snapshotsDirName), name, ext: snapshotExtension });
}

getSnapshotFileName(context) {
getSnapshotFileName(context: { fileName?: string, kind: any }) {
const { fileName, kind } = context;

if (!fileName) {
Expand All @@ -45,7 +53,7 @@ class DefaultStories2SnapsConverter {
return this.getStoryshotFile(fileName);
}

getPossibleStoriesFiles(storyshotFile) {
getPossibleStoriesFiles(storyshotFile: string) {
const { dir, name } = path.parse(storyshotFile);
const { storiesExtensions } = this.options;

Expand All @@ -58,5 +66,3 @@ class DefaultStories2SnapsConverter {
);
}
}

export default DefaultStories2SnapsConverter;
23 changes: 23 additions & 0 deletions addons/storyshots/storyshots-core/src/api/StoryshotsOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { IOptions } from 'glob';
import { Stories2SnapsConverter } from '../Stories2SnapsConverter';
import { SupportedFramework } from '../frameworks';
import { RenderTree } from '../frameworks/Loader';

export interface StoryshotsOptions {
asyncJest?: boolean;
config?: (options: any) => void;
integrityOptions?: IOptions | false;
configPath?: string;
suite?: string;
storyKindRegex?: RegExp | string;
storyNameRegex?: RegExp | string;
framework?: SupportedFramework;
test?: (story: any, context: any, renderTree: RenderTree, options?: any) => any;
renderer?: Function;
snapshotSerializers?: jest.SnapshotSerializerPlugin[];
/**
* @Deprecated The functionality of this option is completely covered by snapshotSerializers which should be used instead.
*/
serializer?: any;
stories2snapsConverter?: Stories2SnapsConverter;
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { snapshotWithOptions } from '../test-bodies';
import Stories2SnapsConverter from '../Stories2SnapsConverter';
import { Stories2SnapsConverter } from '../Stories2SnapsConverter';
import { StoryshotsOptions } from './StoryshotsOptions';

const ignore = ['**/node_modules/**'];
const defaultStories2SnapsConverter = new Stories2SnapsConverter();

function getIntegrityOptions({ integrityOptions }) {
function getIntegrityOptions({ integrityOptions }: StoryshotsOptions) {
if (integrityOptions === false) {
return false;
}
Expand All @@ -13,14 +14,18 @@ function getIntegrityOptions({ integrityOptions }) {
return false;
}

const ignoreOption: string[] = Array.isArray(integrityOptions.ignore)
? integrityOptions.ignore
: [];

return {
...integrityOptions,
ignore: [...ignore, ...(integrityOptions.ignore || [])],
ignore: [...ignore, ...ignoreOption],
absolute: true,
};
}

function ensureOptionsDefaults(options) {
function ensureOptionsDefaults(options: StoryshotsOptions) {
const {
suite = 'Storyshots',
asyncJest,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,27 @@ import ensureOptionsDefaults from './ensureOptionsDefaults';
import snapshotsTests from './snapshotsTestsTemplate';
import integrityTest from './integrityTestTemplate';
import loadFramework from '../frameworks/frameworkLoader';
import { StoryshotsOptions } from './StoryshotsOptions';

global.STORYBOOK_REACT_CLASSES = global.STORYBOOK_REACT_CLASSES || {};

const methods = ['beforeAll', 'beforeEach', 'afterEach', 'afterAll'];
type TestMethod = 'beforeAll' | 'beforeEach' | 'afterEach' | 'afterAll';
const methods: TestMethod[] = ['beforeAll', 'beforeEach', 'afterEach', 'afterAll'];

function callTestMethodGlobals(testMethod) {
function callTestMethodGlobals(
testMethod: { [key in TestMethod]?: Function } & { [key in string]: any }
) {
methods.forEach(method => {
if (typeof testMethod[method] === 'function') {
global[method](testMethod[method]);
}
});
}

const isDisabled = parameter => parameter === false || (parameter && parameter.disable === true);
const isDisabled = (parameter: any) =>
parameter === false || (parameter && parameter.disable === true);

function testStorySnapshots(options = {}) {
function testStorySnapshots(options: StoryshotsOptions = {}) {
if (typeof describe !== 'function') {
throw new Error('testStorySnapshots is intended only to be used inside jest');
}
Expand Down Expand Up @@ -47,23 +52,29 @@ function testStorySnapshots(options = {}) {
.raw()
.filter(({ name }) => (storyNameRegex ? name.match(storyNameRegex) : true))
.filter(({ kind }) => (storyKindRegex ? kind.match(storyKindRegex) : true))
.reduce((acc, item) => {
const { kind, storyFn: render, parameters } = item;
const existing = acc.find(i => i.kind === kind);
const { fileName } = item.parameters;
.reduce(
(acc, item) => {
const { kind, storyFn: render, parameters } = item;
const existing = acc.find((i: any) => i.kind === kind);
const { fileName } = item.parameters;

if (!isDisabled(parameters.storyshots)) {
if (existing) {
existing.children.push({ ...item, render, fileName });
} else {
acc.push({
kind,
children: [{ ...item, render, fileName }],
});
if (!isDisabled(parameters.storyshots)) {
if (existing) {
existing.children.push({ ...item, render, fileName });
} else {
acc.push({
kind,
children: [{ ...item, render, fileName }],
});
}
}
}
return acc;
}, []);
return acc;
},
[] as Array<{
kind: string;
children: any[];
}>
);

if (data.length) {
callTestMethodGlobals(testMethod);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import fs from 'fs';
import glob from 'glob';
import { describe, it } from 'global';

function integrityTest(integrityOptions, stories2snapsConverter) {
function integrityTest(integrityOptions: any, stories2snapsConverter: any) {
if (integrityOptions === false) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { describe, it } from 'global';
import { addSerializer } from 'jest-specific-snapshot';

function snapshotTest({ item, asyncJest, framework, testMethod, testMethodParams }) {
function snapshotTest({ item, asyncJest, framework, testMethod, testMethodParams }: any) {
const { name } = item;
const context = { ...item, framework };

if (asyncJest === true) {
it(name, done =>
it(name, (done: jest.DoneCallback) =>
testMethod({
done,
story: item,
Expand All @@ -25,28 +25,28 @@ function snapshotTest({ item, asyncJest, framework, testMethod, testMethodParams
}
}

function snapshotTestSuite({ item, suite, ...restParams }) {
function snapshotTestSuite({ item, suite, ...restParams }: any) {
const { kind, children } = item;
// eslint-disable-next-line jest/valid-describe
describe(suite, () => {
// eslint-disable-next-line jest/valid-describe
describe(kind, () => {
children.forEach(c => {
children.forEach((c: any) => {
snapshotTest({ item: c, ...restParams });
});
});
});
}

function snapshotsTests({ data, snapshotSerializers, ...restParams }) {
function snapshotsTests({ data, snapshotSerializers, ...restParams }: any) {
if (snapshotSerializers) {
snapshotSerializers.forEach(serializer => {
snapshotSerializers.forEach((serializer: any) => {
addSerializer(serializer);
expect.addSnapshotSerializer(serializer);
});
}

data.forEach(item => {
data.forEach((item: any) => {
snapshotTestSuite({ item, ...restParams });
});
}
Expand Down
17 changes: 17 additions & 0 deletions addons/storyshots/storyshots-core/src/frameworks/Loader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ClientApi } from '@storybook/client-api';
import { StoryshotsOptions } from '../api/StoryshotsOptions';
import { SupportedFramework } from './SupportedFramework';

export type RenderTree = (story: any, context?: any, options?: any) => any;

export interface Loader {
load: (
options: StoryshotsOptions
) => {
framework: SupportedFramework;
renderTree: RenderTree;
renderShallowTree: any;
storybook: ClientApi;
};
test: (options: StoryshotsOptions) => boolean;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export type SupportedFramework =
| 'angular'
| 'html'
| 'preact'
| 'react'
| 'riot'
| 'react-native'
| 'svelte'
| 'vue';
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import 'core-js';
import 'core-js/es/reflect';
import hasDependency from '../hasDependency';
import configure from '../configure';
import { Loader } from '../Loader';
import { StoryshotsOptions } from '../../api/StoryshotsOptions';

function setupAngularJestPreset() {
// Needed to prevent "Zone.js has detected that ZoneAwarePromise `(window|global).Promise` has been overwritten."
Expand All @@ -18,13 +20,13 @@ function setupAngularJestPreset() {
require.requireActual('jest-preset-angular/setupJest');
}

function test(options) {
function test(options: StoryshotsOptions): boolean {
return (
options.framework === 'angular' || (!options.framework && hasDependency('@storybook/angular'))
);
}

function load(options) {
function load(options: StoryshotsOptions) {
setupAngularJestPreset();

const { configPath, config } = options;
Expand All @@ -33,7 +35,7 @@ function load(options) {
configure({ configPath, config, storybook });

return {
framework: 'angular',
framework: 'angular' as const,
renderTree: require.requireActual('./renderTree').default,
renderShallowTree: () => {
throw new Error('Shallow renderer is not supported for angular');
Expand All @@ -42,7 +44,9 @@ function load(options) {
};
}

export default {
const angularLoader: Loader = {
load,
test,
};

export default angularLoader;
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,21 @@ import { initModuleData } from './helpers';
addSerializer(HTMLCommentSerializer);
addSerializer(AngularSnapshotSerializer);

function getRenderedTree(story) {
function getRenderedTree(story: any) {
const currentStory = story.render();

const { moduleMeta, AppComponent } = initModuleData(currentStory);

TestBed.configureTestingModule({
imports: [...moduleMeta.imports],
declarations: [...moduleMeta.declarations],
providers: [...moduleMeta.providers],
schemas: [NO_ERRORS_SCHEMA, ...moduleMeta.schemas],
bootstrap: [...moduleMeta.bootstrap],
});
TestBed.configureTestingModule(
// TODO: take a look at `bootstrap` because it looks it does not exists in TestModuleMetadata
{
imports: [...moduleMeta.imports],
declarations: [...moduleMeta.declarations],
providers: [...moduleMeta.providers],
schemas: [NO_ERRORS_SCHEMA, ...moduleMeta.schemas],
bootstrap: [...moduleMeta.bootstrap],
} as any
);

TestBed.overrideModule(BrowserDynamicTestingModule, {
set: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import fs from 'fs';
import path from 'path';

function getConfigPathParts(configPath) {
function getConfigPathParts(configPath: string): string {
const resolvedConfigPath = path.resolve(configPath);

if (fs.lstatSync(resolvedConfigPath).isDirectory()) {
Expand All @@ -11,7 +11,7 @@ function getConfigPathParts(configPath) {
return resolvedConfigPath;
}

function configure(options) {
function configure(options: { configPath?: string; config: any; storybook: any }): void {
const { configPath = '.storybook', config, storybook } = options;

if (config && typeof config === 'function') {
Expand Down
Loading

1 comment on commit 38d8c4f

@vercel
Copy link

@vercel vercel bot commented on 38d8c4f Oct 8, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.