Last active
July 30, 2018 10:52
-
-
Save axelnormand/4ee5786020c161ad8540988261e0221b to your computer and use it in GitHub Desktop.
React Native Storyshot Generation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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