-
-
Notifications
You must be signed in to change notification settings - Fork 9.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #11 from kadirahq/refactor
Refactor
- Loading branch information
Showing
4 changed files
with
242 additions
and
12 deletions.
There are no files selected for viewing
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
import chalk from 'chalk'; | ||
import SnapshotRunner from './snapshot_runner'; | ||
|
||
export default class Runner { | ||
constructor(options) { | ||
const { | ||
configDir = './.storybook', | ||
update, | ||
updateInteractive: interactive, | ||
} = options; | ||
|
||
this.configDir = configDir; | ||
this.update = update; | ||
this.interactive = interactive; | ||
|
||
this.testState = { | ||
added: 0, | ||
matched: 0, | ||
unmatched: 0, | ||
updated: 0, | ||
obsolete: 0, | ||
errored: 0, | ||
}; | ||
|
||
this.runner = new SnapshotRunner(configDir); | ||
} | ||
|
||
updateState(result) { | ||
this.testState[result.state]++; | ||
logState(result); | ||
} | ||
|
||
completed() { | ||
logSummary(this.testState); | ||
} | ||
|
||
async run(storybook) { | ||
const options = { | ||
update: this.update, | ||
interactive: this.interactive, | ||
}; | ||
|
||
for (const group of storybook) { | ||
try { | ||
this.runner.startKind(group.kind); | ||
this.updateState({state: 'started-kind', name: group.kind}); | ||
for (const story of group.stories) { | ||
try { | ||
const result = await this.runner.runStory(story, options); | ||
this.updateState({...result, name: story.name}); | ||
} catch (err) { | ||
// Error on story | ||
this.updateState({state: 'errored', message: err, name: story.name}); | ||
} | ||
} | ||
this.runner.endKind(options); | ||
} catch (err) { | ||
// Error on kind | ||
this.updateState({state: 'errored-kind', message: err}); | ||
} | ||
} | ||
|
||
this.completed(); | ||
} | ||
} | ||
|
||
function logState({state, name, message}) { | ||
switch (state) { | ||
case 'added': | ||
process.stdout.write(chalk.cyan(`+ ${name}: Added`)); | ||
break; | ||
case 'updated': | ||
process.stdout.write(chalk.cyan(`● ${name}: Updated`)); | ||
break; | ||
case 'matched': | ||
process.stdout.write(chalk.green(`✓ ${name}`)); | ||
break; | ||
case 'unmatched': | ||
process.stdout.write('\n'); | ||
process.stdout.write(chalk.red(`✗ ${name}\n`)); | ||
process.stdout.write(' ' + message.split('\n').join('\n ')); | ||
process.stdout.write('\n'); | ||
break; | ||
case 'errored': | ||
case 'errored-kind': | ||
process.stdout.write('\n'); | ||
process.stdout.write(chalk.red(`✗ ${name}: ERROR\n`)); | ||
const output = message.stack || message; | ||
process.stdout.write(chalk.dim(' ' + output.split('\n').join('\n '))); | ||
process.stdout.write('\n'); | ||
break; | ||
case 'started-kind': | ||
process.stdout.write('\n'); | ||
process.stdout.write(chalk.underline(name)); | ||
break; | ||
default: | ||
process.stdout.write(`Error occured when testing ${state}`); | ||
} | ||
process.stdout.write('\n'); | ||
} | ||
|
||
function logSummary(state) { | ||
const { added, matched, unmatched, updated, errored, obsolete } = state; | ||
const total = added + matched + unmatched + updated + errored; | ||
process.stdout.write(chalk.bold('Test summary\n')); | ||
process.stdout.write(`> ${total} stories tested.\n`); | ||
if (matched > 0) { | ||
process.stdout.write(chalk.green(`> ${matched}/${total} stories matched with snapshots.\n`)); | ||
} | ||
if (unmatched > 0) { | ||
process.stdout.write(chalk.red(`> ${unmatched}/${total} differ from snapshots.\n`)); | ||
} | ||
if (updated > 0) { | ||
process.stdout.write(chalk.cyan(`> ${updated} snapshots updated to match current stories.\n`)); | ||
} | ||
if (added > 0) { | ||
process.stdout.write(chalk.cyan(`> ${added} snapshots newly added.\n`)); | ||
} | ||
if (errored > 0) { | ||
process.stdout.write(chalk.red(`> ${errored} tests errored.\n`)); | ||
} | ||
if (obsolete > 0) { | ||
process.stdout.write(chalk.cyan(`> ${obsolete} unused snapshots remaining. Run with -u to remove them.\n`)); | ||
} | ||
} | ||
|
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import path from 'path'; | ||
import jestSnapshot from 'jest-snapshot'; | ||
import ReactTestRenderer from 'react-test-renderer'; | ||
import diff from 'jest-diff'; | ||
import promptly from 'promptly'; | ||
|
||
export default class SnapshotRunner { | ||
constructor(configDir) { | ||
this.configDir = configDir; | ||
this.kind = ''; | ||
} | ||
|
||
startKind(kind) { | ||
const filePath = path.resolve(this.configDir, kind); | ||
|
||
const fakeJasmine = { | ||
Spec: () => {} | ||
}; | ||
this.state = jestSnapshot.getSnapshotState(fakeJasmine, filePath); | ||
this.kind = kind; | ||
} | ||
|
||
async runStory(story, {update, interactive}) { | ||
this.state.setSpecName(story.name); | ||
this.state.setCounter(0); | ||
const snapshot = this.state.snapshot; | ||
|
||
const key = story.name; | ||
const hasSnapshot = snapshot.has(key); | ||
const context = { kind: this.kind, story }; | ||
const tree = story.render(context); | ||
const renderer = ReactTestRenderer.create(tree); | ||
const actual = renderer.toJSON(); | ||
|
||
if (!snapshot.fileExists() || !hasSnapshot) { | ||
// If the file does not exist of snapshot of this name is not present | ||
// add it. | ||
snapshot.add(key, actual); | ||
return {state: 'added'}; | ||
} | ||
|
||
const matches = snapshot.matches(key, actual); | ||
const pass = matches.pass; | ||
if (pass) { | ||
// Snapshot matches with the story | ||
return {state: 'matched'}; | ||
} | ||
|
||
// Snapshot does not match story | ||
if (update) { | ||
snapshot.add(key, actual); | ||
return {state: 'updated'}; | ||
} | ||
|
||
const diffMessage = diff( | ||
matches.expected.trim(), | ||
matches.actual.trim(), | ||
{ | ||
aAnnotation: 'Snapshot', | ||
bAnnotation: 'Current story', | ||
}, | ||
); | ||
|
||
if (interactive) { | ||
const shouldUpdate = await this.confirmUpate(diffMessage); | ||
if (shouldUpdate) { | ||
snapshot.add(key, actual); | ||
return {state: 'updated'}; | ||
} | ||
} | ||
|
||
return {state: 'unmatched', message: diffMessage}; | ||
} | ||
|
||
endKind({update}) { | ||
const snapshot = this.state.snapshot; | ||
if (update) { | ||
snapshot.removeUncheckedKeys(); | ||
} | ||
snapshot.save(update); | ||
} | ||
|
||
async confirmUpate(diffMessage) { | ||
process.stdout.write('\nReceived story is different from stored snapshot.\n'); | ||
process.stdout.write(' ' + diffMessage.split('\n').join('\n ')); | ||
let ans = await promptly.prompt('Should this snapshot be updated?(y/n)'); | ||
while (ans !== 'y' && ans !== 'n') { | ||
process.stdout.write('Enter only y (yes) or n (no)\n'); | ||
ans = await promptly.prompt('Should this snapshot be updated?(y/n)'); | ||
} | ||
process.stdout.write('\n'); | ||
|
||
return ans === 'y'; | ||
} | ||
} |