Skip to content

Instantly share code, notes, and snippets.

@axelnormand
Last active July 30, 2018 10:52
Show Gist options
  • Save axelnormand/4ee5786020c161ad8540988261e0221b to your computer and use it in GitHub Desktop.
Save axelnormand/4ee5786020c161ad8540988261e0221b to your computer and use it in GitHub Desktop.
React Native Storyshot Generation
import chalk from 'chalk';
import * as fs from 'graceful-fs';
import * as path from 'path';
const SRC_DIR = path.join(__dirname, '../../../../src');
const ALL_STORIES_DIR = path.join(__dirname, 'allStories');
const ALL_STORIES_FILE = path.join(ALL_STORIES_DIR, 'index.ts'); // commit this file to git
const STORY_EXTENSION = '.story.tsx';
const STORY_TEST_OUTPUT_EXTENSION = '.story.test.ts';
const STORYSHOT_TEMPLATE = path.join(__dirname, 'testTemplate.test.ts');
/**
* Find all story files and create a test.ts for the shallow storyshot in the same dir (should be committed to git)
*/
const createTests = () => {
log(`--- STARTING storyshots createTests ---`);
const files = findFilesInDir(SRC_DIR, STORY_EXTENSION);
if (!files.length) {
throw new Error(
`No Story files found in '${SRC_DIR}' with extension '${STORY_EXTENSION}'`,
);
}
log(`\nGenerating ${files.length} '${STORY_TEST_OUTPUT_EXTENSION}' files`);
writeStoryShotTestFiles(files);
log(`\nGenerating All Stories File: ${ALL_STORIES_FILE}`);
writeAllStoriesFile(files);
log(`\n--- FINISHED storyshots createTests ---\n`);
};
const log = (arg: string) => {
console.log(chalk.green(arg));
};
/** write the allStories file to disk to this dir */
const writeAllStoriesFile = (files: string[]) => {
// produce relative path for import in template
const filesWithRelativePath: string[] = [];
for (const file of files) {
const relativeFilename = makeRelative(file, ALL_STORIES_DIR, true);
filesWithRelativePath.push(relativeFilename);
}
// write all stories file
const content = generateAllStoriesJS(filesWithRelativePath);
fs.writeFileSync(ALL_STORIES_FILE, content);
};
/** create each test file in same dir as story */
const writeStoryShotTestFiles = (files: string[]) => {
const existingTestFiles = findFilesInDir(
SRC_DIR,
STORY_TEST_OUTPUT_EXTENSION,
);
log(`Found ${existingTestFiles.length} existing story test files`);
const templateFile = fs.readFileSync(STORYSHOT_TEMPLATE).toString();
const testFilesWritten: string[] = [];
for (const file of files) {
const storyFilename = path.basename(file);
const storyPath = path.dirname(file);
const testFilename = storyFilename.replace(
STORY_EXTENSION,
STORY_TEST_OUTPUT_EXTENSION,
);
const testFile = path.join(storyPath, testFilename);
const content = templateFile.replace(
"require('./testStory')",
`require('./${removeFileExtension(storyFilename)}')`,
);
console.log(`Generating test file: ${testFile}`);
testFilesWritten.push(testFile);
fs.writeFileSync(testFile, content);
}
// delete old test files
const filenamesToRemove = existingTestFiles.filter(
file => !testFilesWritten.includes(file),
);
log(`Deleting ${filenamesToRemove.length} old story test files`);
for (const file of filenamesToRemove) {
console.log(`Deleting redundant test file: ${file}`);
fs.unlinkSync(file);
}
};
/**
* Return array of absolute file names in directory given (with forward slashes).
*
* Only includes fileTypes given
*
* @param {string} extension file type you want to search for eg `'.story.test.tsx'`
*/
export const findFilesInDir = (dir: string, extension: string) => {
let results: string[] = [];
if (!fs.existsSync(dir)) {
throw new Error(`No such directory: ${dir}`);
}
const files = fs.readdirSync(dir);
for (const file of files) {
const filename = path.join(dir, file);
const stat = fs.lstatSync(filename);
if (stat.isDirectory()) {
// recurse
results = results.concat(findFilesInDir(filename, extension));
} else if (filename.endsWith(extension)) {
const absFile = path.resolve(filename);
results.push(absFile);
}
}
return results.sort();
};
/** turns into relative filename with forward slashes and removes extension */
const makeRelative = (
file: string,
toDir: string,
removeExtension: boolean,
) => {
const filePath = path.dirname(file);
const fileName = path.basename(file);
const relativePath = path.relative(toDir, filePath);
const relativeFilename = path
.join(relativePath, fileName)
.replace(/\\/g, '/');
return removeExtension
? removeFileExtension(relativeFilename)
: relativeFilename;
};
/** remove last file extension */
const removeFileExtension = (filename: string) => {
const lastDotPosition = filename.lastIndexOf('.');
if (lastDotPosition > -1) {
return filename.substr(0, lastDotPosition);
}
return filename;
};
const generateAllStoriesJS = (files: string[]) => `///////////////////
// AUTO GENERATED by createTests
// Loads all stories for storybook
///////////////////
/** import this to load all stories in your storybook init file */
export const loadStories = () => {
${files.map(file => " require('" + file + "');").join('\n')}
};
/** useful to loop through all stories */
export const allStories = [
${files.map(file => " '" + file + "',").join('\n')}
];
`;
/** Run now as prestorybook task in package.json */
createTests();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment