diff --git a/.circleci/config.yml b/.circleci/config.yml index 9d0f412db379..0620b6b23b7f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -28,7 +28,7 @@ jobs: - run: name: "Install root dependencies" command: | - yarn + yarn install - save_cache: key: root-dependencies-{{ checksum "package.json" }} paths: @@ -59,7 +59,7 @@ jobs: - run: name: "Install root dependencies" command: | - yarn + yarn install - run: name: "Bootstrapping" command: | @@ -83,7 +83,7 @@ jobs: - run: name: "Install root dependencies" command: | - yarn + yarn install - run: name: "Bootstrapping packages" command: | @@ -103,7 +103,7 @@ jobs: - run: name: "Install root dependencies" command: | - yarn + yarn install - run: name: "Bootstrapping" command: | @@ -123,7 +123,7 @@ jobs: - run: name: "Install root dependencies" command: | - yarn + yarn install - run: name: "Linting" command: | @@ -139,7 +139,7 @@ jobs: - run: name: "Install root dependencies" command: | - yarn + yarn install - run: name: "Bootstrapping" command: | @@ -147,7 +147,7 @@ jobs: - run: name: "Unit testing" command: | - yarn test -- --coverage -i + yarn test -- --all --coverage --runInBand yarn coverage deploy: <<: *defaults diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 917758ac319d..9541039df612 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,17 +22,44 @@ No software is bug free. So, if you got an issue, follow these steps: To test your project against the current latest version of storybook, you can clone the repository and link it with `yarn`. Try following these steps: -1. Download the latest version of this project, and build it: +#### 1. Download the latest version of this project, and build it: + +```sh +git clone https://github.com/storybooks/storybook.git +cd storybook +yarn install +yarn bootstrap +``` + +The bootstrap command will ask which sections of the codebase you want to bootstrap. Unless you're going to work with ReactNative or the Documentation, you can keep the default. + +You can also pick directly from CLI: - ```sh - git clone https://github.com/storybooks/storybook.git - cd storybook - yarn yarn bootstrap -- --core - ``` +#### 2a. Run unit tests + +You can use one of the example projects in `examples/` to develop on. + +This command will list all the suites and options for running tests. -2. Link `storybook` and any other required dependencies: +```sh +yarn test +``` + +_Note that in order to run the tests fro ReactNative, you must have bootstrapped with ReactNative enabled_ + +You can also pick suites from CLI: + +```sh +yarn test -- --core +``` + +In order to run ALL unit tests, you must have bootstrapped the react-native + +#### 2b. Link `storybook` and any other required dependencies: + +If you want to test your own existing project using the github version of storybook, you need to `link` the packages you use in your project. ```sh cd app/react @@ -54,11 +81,12 @@ A good way to do that is using the example `cra-kitchen-sink` app embedded in th # Download and build this repository: git clone https://github.com/storybooks/storybook.git cd storybook -yarn -yarn bootstrap -- --core +yarn install +yarn bootstrap # make changes to try and reproduce the problem, such as adding components + stories -yarn start +cd examples/cra-kitchen-sink +yarn storybook # see if you can see the problem, if so, commit it: git checkout "branch-describing-issue" @@ -194,12 +222,9 @@ First, build the release: git checkout master git status -# clean out extra files +# clean out extra files & build all the packages # WARNING: destructive if you have extra files lying around! -git clean -fdx && yarn - -# build all the packages -yarn bootstrap -- --all +yarn bootstrap -- --reset --all ``` From here there are different procedures for prerelease (e.g. alpha/beta/rc) and proper release. @@ -210,7 +235,7 @@ From here there are different procedures for prerelease (e.g. alpha/beta/rc) and ```sh # publish and tag the release -yarn publish -- --concurrency 1 --npm-tag=alpha +yarn run publish -- --concurrency 1 --npm-tag=alpha # push the tags git push --tags diff --git a/package.json b/package.json index 824a05d649e6..728390921afa 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "lint:js": "eslint --cache --cache-location=.cache/eslint --ext .js,.jsx,.json", "lint:md": "remark", "publish": "lerna publish", - "test": "jest --projects ./ ./examples/react-native-vanilla" + "test": "./scripts/test.js" }, "devDependencies": { "babel-cli": "^6.24.1", diff --git a/scripts/test.js b/scripts/test.js new file mode 100755 index 000000000000..8d75b58515c6 --- /dev/null +++ b/scripts/test.js @@ -0,0 +1,145 @@ +#!/usr/bin/env node +const inquirer = require('inquirer'); +const program = require('commander'); +const childProcess = require('child_process'); +const chalk = require('chalk'); +const log = require('npmlog'); + +log.heading = 'storybook'; +const prefix = 'test'; +log.addLevel('aborted', 3001, { fg: 'red', bold: true }); + +const spawn = command => + childProcess.spawnSync(`${command}`, { + shell: true, + stdio: 'inherit', + }); + +const main = program.version('3.0.0').option('--all', `Test everything ${chalk.gray('(all)')}`); + +const createProject = ({ defaultValue, option, name, projectLocation }) => ({ + value: false, + defaultValue: defaultValue || false, + option: option || undefined, + name: name || 'unnamed task', + projectLocation, +}); +const createOption = ({ defaultValue, option, name, extraParam }) => ({ + value: false, + defaultValue: defaultValue || false, + option: option || undefined, + name: name || 'unnamed task', + extraParam, +}); + +const tasks = { + core: createProject({ + name: `Core & React & Vue ${chalk.gray('(core)')}`, + defaultValue: true, + option: '--core', + projectLocation: './', + }), + 'react-native-vanilla': createProject({ + name: `React-Native example ${chalk.gray('(react-native-vanilla)')}`, + defaultValue: true, + option: '--reactnative', + projectLocation: './examples/react-native-vanilla', + }), + // 'crna-kitchen-sink': createProject({ + // name: `React-Native-App example ${chalk.gray('(crna-kitchen-sink)')} ${chalk.red( + // '[not implemented yet]' + // )}`, + // defaultValue: false, + // option: '--reactnativeapp', + // projectLocation: './examples/crna-kitchen-sink', + // }), + watchmode: createOption({ + name: `Run in watch-mode ${chalk.gray('(watchmode)')}`, + defaultValue: false, + option: '--watch', + extraParam: '--watch', + }), + coverage: createOption({ + name: `Output coverage reports ${chalk.gray('(coverage)')}`, + defaultValue: false, + option: '--coverage', + extraParam: '--coverage', + }), + runInBand: createOption({ + name: `Run all tests serially in the current process ${chalk.gray('(runInBand)')}`, + defaultValue: false, + option: '--runInBand', + extraParam: '--runInBand', + }), +}; + +const getProjects = list => { + const filtered = list.filter(key => key.projectLocation); + if (filtered.length > 0) { + return filtered.map(key => key.projectLocation); + } + + // if list would have been empty, we run with default projects + return Object.keys(tasks) + .map(key => tasks[key]) + .filter(key => key.projectLocation && key.defaultValue) + .map(key => key.projectLocation); +}; + +const getExtraParams = list => list.filter(key => key.extraParam).map(key => key.extraParam); + +Object.keys(tasks) + .reduce((acc, key) => acc.option(tasks[key].option, tasks[key].name), main) + .parse(process.argv); + +Object.keys(tasks).forEach(key => { + tasks[key].value = + program[tasks[key].option.replace('--', '')] || (program.all && tasks[key].projectLocation); +}); + +let selection; +if (!Object.keys(tasks).map(key => tasks[key].value).filter(Boolean).length) { + selection = inquirer + .prompt([ + { + type: 'checkbox', + message: 'Select which tests to run', + name: 'todo', + choices: Object.keys(tasks) + .map(key => tasks[key]) + .filter(key => key.projectLocation) + .map(key => ({ + name: key.name, + checked: key.defaultValue, + })) + .concat(new inquirer.Separator()) + .concat( + Object.keys(tasks).map(key => tasks[key]).filter(key => key.extraParam).map(key => ({ + name: key.name, + checked: key.defaultValue, + })) + ), + }, + ]) + .then(({ todo }) => + todo.map(name => tasks[Object.keys(tasks).find(i => tasks[i].name === name)]) + ); +} else { + selection = Promise.resolve( + Object.keys(tasks).map(key => tasks[key]).filter(item => item.value === true) + ); +} + +selection + .then(list => { + if (list.length === 0) { + log.warn(prefix, 'Nothing to test'); + } else { + spawn(`jest --projects ${getProjects(list).join(' ')} ${getExtraParams(list).join(' ')}`); + process.stdout.write('\x07'); + } + }) + .catch(e => { + log.aborted(prefix, chalk.red(e.message)); + log.silly(prefix, e); + });