├── .eslintignore ├── .flowconfig ├── .babelrc ├── .editorconfig ├── .travis.yml ├── src ├── index.js └── index.test.js ├── dist ├── index.js.flow └── index.js ├── .gitignore ├── LICENSE ├── package.json ├── .eslintrc ├── README.md └── flow-typed └── npm └── jest_v17.x.x.js /.eslintignore: -------------------------------------------------------------------------------- 1 | **/coverage/** 2 | **/node_modules/** 3 | dist/** 4 | flow-typed/** 5 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | /node_modules/.* 3 | 4 | [include] 5 | 6 | [libs] 7 | 8 | [options] 9 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "latest" 4 | ], 5 | "plugins": [ 6 | "transform-flow-strip-types" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | cache: 3 | directories: 4 | - node_modules 5 | notifications: 6 | email: false 7 | node_js: 8 | - '8' 9 | - '10' 10 | script: 11 | - yarn test 12 | - yarn build 13 | after_success: 14 | - yarn run report-coverage 15 | - yarn run semantic-release 16 | branches: 17 | only: 18 | - master 19 | - /^greenkeeper/.*$/ 20 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default (theme: Object, mapper?: string => string) => 4 | (props: Object, defaultClass?: string): Array => { 5 | if (Object.keys(props).length) { 6 | return Object.keys(props) 7 | .filter(prop => !!props[prop]) 8 | .map(className => mapper ? 9 | theme[mapper(className)] : 10 | theme[className]); 11 | } else { 12 | return defaultClass ? [theme[defaultClass]] : []; 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /dist/index.js.flow: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default (theme: Object, mapper?: string => string) => 4 | (props: Object, defaultClass?: string): Array => { 5 | if (Object.keys(props).length) { 6 | return Object.keys(props) 7 | .filter(prop => !!props[prop]) 8 | .map(className => mapper ? 9 | theme[mapper(className)] : 10 | theme[className]); 11 | } else { 12 | return defaultClass ? [theme[defaultClass]] : []; 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | exports.default = function (theme, mapper) { 8 | return function (props, defaultClass) { 9 | if (Object.keys(props).length) { 10 | return Object.keys(props).filter(function (prop) { 11 | return !!props[prop]; 12 | }).map(function (className) { 13 | return mapper ? theme[mapper(className)] : theme[className]; 14 | }); 15 | } else { 16 | return defaultClass ? [theme[defaultClass]] : []; 17 | } 18 | }; 19 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | # IDEs and editors 40 | .idea 41 | .vscode 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright © 2016 Flavio Corpa 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/index.test.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import cssProps from './index'; 4 | 5 | // Mock imported theme from CSS Modules 6 | const theme = { 7 | 'icon': 'Icon__icon___66GYG', 8 | 'button': 'Button__button___3_Ozh', 9 | 'primary': 'Button__primary___zYyzg', 10 | 'icon-save': 'Icon__icon-save___e4uoa' 11 | }; 12 | 13 | describe('react-css-props', () => { 14 | 15 | test('should always return a function', () => { 16 | expect(typeof cssProps({})).toBe('function'); 17 | expect(typeof cssProps(theme)).toBe('function'); 18 | }); 19 | 20 | test('generated function should always return an array', () => { 21 | expect(cssProps(theme)({})).toEqual([]); 22 | }); 23 | 24 | test('should return an array with the specified default class', () => { 25 | expect(cssProps(theme)({}, 'button')).toEqual([theme.button]); 26 | }); 27 | 28 | test('should have `undefined` when not finding the default class', () => { 29 | expect(cssProps(theme)({}, 'unexisting')).toEqual([undefined]); 30 | }); 31 | 32 | test('should turn props into existing classNames', () => { 33 | expect(cssProps(theme)({ icon: true })).toEqual([theme.icon]); 34 | }); 35 | 36 | test('should only turn positive props into classNames', () => { 37 | expect( 38 | cssProps(theme)({ button: true, icon: false }) 39 | ).toEqual([theme.button]); 40 | }); 41 | 42 | test('should use the mapper if provided', () => { 43 | expect( 44 | cssProps(theme, type => `icon-${type}`)({ save: true }) 45 | ).toEqual([theme['icon-save']]); 46 | }); 47 | 48 | }); 49 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-css-props", 3 | "version": "0.0.0-semantic-release", 4 | "description": "Dynamically turn your props into CSS clases!", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "flow": "flow", 8 | "commit": "git-cz", 9 | "test:w": "jest --watch", 10 | "test": "jest --coverage", 11 | "prebuild": "rimraf dist", 12 | "build": "yarn run build:lib && yarn run build:flow", 13 | "build:lib": "babel --out-dir dist --ignore *.test.js src", 14 | "build:flow": "flow-copy-source -v -i *.test.js src dist", 15 | "report-coverage": "cat ./coverage/lcov.info | coveralls", 16 | "semantic-release": "semantic-release" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/kutyel/react-css-props.git" 21 | }, 22 | "keywords": [ 23 | "react", 24 | "css", 25 | "props", 26 | "flow", 27 | "jest", 28 | "yarn", 29 | "flowtype", 30 | "css modules" 31 | ], 32 | "files": [ 33 | "dist", 34 | "README.md" 35 | ], 36 | "author": "Flavio Corpa Ríos (https://github.com/kutyel)", 37 | "license": "MIT", 38 | "bugs": { 39 | "url": "https://github.com/kutyel/react-css-props/issues" 40 | }, 41 | "homepage": "https://github.com/kutyel/react-css-props#readme", 42 | "devDependencies": { 43 | "babel-cli": "^6.18.0", 44 | "babel-eslint": "^9.0.0", 45 | "babel-plugin-transform-flow-strip-types": "^6.18.0", 46 | "babel-preset-latest": "^6.16.0", 47 | "commitizen": "^2.8.6", 48 | "coveralls": "^3.0.0", 49 | "cz-conventional-changelog": "^2.0.0", 50 | "eslint": "^5.0.0", 51 | "eslint-plugin-flowtype": "^2.29.1", 52 | "eslint-plugin-import": "^2.2.0", 53 | "flow-bin": "^0.81.0", 54 | "flow-copy-source": "^2.0.0", 55 | "ghooks": "^2.0.0", 56 | "jest-cli": "^23.0.0", 57 | "rimraf": "^2.5.4", 58 | "semantic-release": "^15.0.0" 59 | }, 60 | "jest": { 61 | "testEnvironment": "node", 62 | "coverageThreshold": { 63 | "global": { 64 | "branches": 100, 65 | "functions": 100, 66 | "lines": 100, 67 | "statements": 100 68 | } 69 | } 70 | }, 71 | "config": { 72 | "ghooks": { 73 | "pre-commit": "yarn test && yarn flow" 74 | }, 75 | "commitizen": { 76 | "path": "node_modules/cz-conventional-changelog" 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "env": { 4 | "es6": true, 5 | "jest": true, 6 | "node": true 7 | }, 8 | "parserOptions": { 9 | "ecmaVersion": 2016, 10 | "sourceType": "module", 11 | "ecmaFeatures": { 12 | "experimentalObjectRestSpread": true 13 | } 14 | }, 15 | "rules": { 16 | "block-scoped-var": 2, 17 | "camelcase": [ 18 | 2, 19 | { 20 | "properties": "never" 21 | } 22 | ], 23 | "comma-dangle": [ 24 | 2, 25 | "never" 26 | ], 27 | "comma-style": [ 28 | 2, 29 | "last" 30 | ], 31 | "consistent-return": 0, 32 | "curly": [ 33 | 2, 34 | "multi-line" 35 | ], 36 | "dot-location": [ 37 | 2, 38 | "property" 39 | ], 40 | "dot-notation": [ 41 | 2, 42 | { 43 | "allowKeywords": true 44 | } 45 | ], 46 | "eqeqeq": [ 47 | 2, 48 | "allow-null" 49 | ], 50 | "guard-for-in": 2, 51 | "indent": [ 52 | 2, 53 | 4, 54 | { 55 | "SwitchCase": 1 56 | } 57 | ], 58 | "key-spacing": [ 59 | 0, 60 | { 61 | "align": "value" 62 | } 63 | ], 64 | "max-len": [ 65 | 1, 66 | 80, 67 | 2, 68 | { 69 | "ignoreComments": true, 70 | "ignoreUrls": true 71 | } 72 | ], 73 | "new-cap": [ 74 | 2, 75 | { 76 | "capIsNewExceptions": [ 77 | "Private" 78 | ] 79 | } 80 | ], 81 | "no-bitwise": 0, 82 | "no-caller": 2, 83 | "no-cond-assign": 0, 84 | "no-debugger": 2, 85 | "no-empty": [ 86 | 2, 87 | { 88 | "allowEmptyCatch": true 89 | } 90 | ], 91 | "no-eval": 2, 92 | "no-extend-native": 2, 93 | "no-extra-parens": 0, 94 | "no-irregular-whitespace": 2, 95 | "no-iterator": 2, 96 | "no-loop-func": 2, 97 | "no-multi-spaces": 0, 98 | "no-multi-str": 2, 99 | "no-multiple-empty-lines": [ 100 | 2, 101 | { 102 | "max": 2, 103 | "maxEOF": 1 104 | } 105 | ], 106 | "no-nested-ternary": 2, 107 | "no-new": 0, 108 | "no-path-concat": 0, 109 | "no-proto": 2, 110 | "no-return-assign": 0, 111 | "no-script-url": 2, 112 | "no-sequences": 0, 113 | "no-shadow": 0, 114 | "no-trailing-spaces": 2, 115 | "no-undef": 2, 116 | "no-underscore-dangle": 0, 117 | "no-unused-expressions": 0, 118 | "no-unused-vars": 1, 119 | "no-use-before-define": [ 120 | 2, 121 | "nofunc" 122 | ], 123 | "no-with": 2, 124 | "one-var": [ 125 | 2, 126 | "never" 127 | ], 128 | "quotes": [ 129 | 2, 130 | "single" 131 | ], 132 | "semi-spacing": [ 133 | 2, 134 | { 135 | "before": false, 136 | "after": true 137 | } 138 | ], 139 | "semi": [ 140 | 2, 141 | "always" 142 | ], 143 | "keyword-spacing": 2, 144 | "space-before-blocks": [ 145 | 2, 146 | "always" 147 | ], 148 | "space-before-function-paren": [ 149 | 2, 150 | { 151 | "anonymous": "always", 152 | "named": "never" 153 | } 154 | ], 155 | "space-in-parens": [ 156 | 2, 157 | "never" 158 | ], 159 | "space-infix-ops": [ 160 | 2, 161 | { 162 | "int32Hint": false 163 | } 164 | ], 165 | "space-unary-ops": [ 166 | 2 167 | ], 168 | "strict": [ 169 | 2, 170 | "never" 171 | ], 172 | "valid-typeof": 2, 173 | "wrap-iife": [ 174 | 2, 175 | "outside" 176 | ], 177 | "yoda": 0, 178 | "eol-last": "error" 179 | }, 180 | "plugins": [ 181 | "import", 182 | "flowtype" 183 | ] 184 | } 185 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React CSS Props 2 | 3 | [![Greenkeeper badge](https://badges.greenkeeper.io/kutyel/react-css-props.svg)](https://greenkeeper.io/) 4 | 5 | [![Build](https://img.shields.io/travis/kutyel/react-css-props/master.svg?style=flat-square)](https://travis-ci.org/kutyel/react-css-props) 6 | [![Dependencies](https://img.shields.io/david/kutyel/react-css-props.svg?style=flat-square)](https://david-dm.org/kutyel/react-css-props) 7 | [![Dev Dependencies](https://img.shields.io/david/dev/kutyel/react-css-props.svg?style=flat-square)](https://david-dm.org/kutyel/react-css-props#info=devDependencies) 8 | [![Coverage Status](https://img.shields.io/coveralls/kutyel/react-css-props/master.svg?style=flat-square)](https://coveralls.io/github/kutyel/react-css-props?branch=master) 9 | [![Downloads](https://img.shields.io/npm/dm/react-css-props.svg?style=flat-square)](https://npmjs.com/packages/react-css-props) 10 | [![Version](https://img.shields.io/npm/v/react-css-props.svg?style=flat-square)](https://npmjs.com/packages/react-css-props) 11 | [![Donate](https://img.shields.io/badge/donate-paypal-blue.svg?style=flat-square)](https://paypal.me/flaviocorpa) 12 | 13 | Dynamically turn your props into CSS clases! 14 | 15 | ## Install 16 | 17 | ```sh 18 | $ yarn add react-css-props 19 | ``` 20 | 21 | **Note: *any* feedback, contributions or other possible use cases are highly appreciated.** 22 | 23 | ## Examples 24 | 25 | Imagine that you are working in just a regular React project, and you decide to use [**CSS Modules**](https://github.com/css-modules/css-modules) for your styling. At some point, you would like to have the following components: 26 | 27 | ```jsx 28 | 29 | 30 | 33 | 34 | ``` 35 | 36 | OK. Everything looks normal with the `Button` component but, wait, what the hell is that custom property besides `Block` and `Icon`!? 37 | You guessed, those are custom properties already built-in with each component, they *match a specific class in the CSS* for that component, in a way that **you don't have to contemplate every possible prop** that will eventually get rendered into a *hashed, CSS Modules class*. 38 | But first, let's get into each component one at a time. 39 | 40 | ### Block component 41 | 42 | Say you are implementing your own grid system, something like what Bootstrap does with their `col-md-6 col-xs-12` global classes, but you want a wrapper component to do that and, of course, *you don't want to rely on `classNames` to do all the job*. 43 | 44 | ```scss 45 | .block { 46 | padding: $padding-layout-block; 47 | box-sizing: border-box; 48 | float: left; 49 | width: 100%; 50 | } 51 | 52 | @mixin blockSize($n) { 53 | width: $column-width * $n * 1%; 54 | .block & { 55 | padding-top: 0; 56 | } 57 | } 58 | 59 | @media (#{$breakpoint-phablet-up}) { 60 | .width1of12 { 61 | @include blockSize(1); 62 | } 63 | .width2of12, .width1of6 { 64 | @include blockSize(2); 65 | } 66 | .width3of12, .width1of4 { 67 | @include blockSize(3); 68 | } 69 | .width4of12, .width2of6, .width1of3 { 70 | @include blockSize(4); 71 | } 72 | } 73 | ``` 74 | 75 | As you might have noticed, this code us using [SASS](http://sass-lang.com/) to show an use case with a preprocessor, but you *don't need one at all* as long as you are using **CSS Modules**. 76 | This is where **react-css-props** takes action: 77 | 78 | ```jsx 79 | import React from 'react'; 80 | import cn from 'classnames'; 81 | import cssProps from 'react-css-props'; 82 | 83 | import theme from './Block.scss'; 84 | 85 | // First customize it with your theme, then use it! 86 | const toCSS = cssProps(theme); 87 | 88 | const Block = ({ children, ...props }) => ( 89 |
90 | {children} 91 |
92 | ); 93 | 94 | export default Block; 95 | ``` 96 | 97 | Obviously, the *magic* is that `cssProps` is a HOF ([Higher Order Function](http://eloquentjavascript.net/05_higher_order.html)), that gets ready to use the props ass CSS clases using the imported theme from CSS Modules. 98 | As you can imagine, the code before *react-css-props* was as weird as ``. 99 | 100 | ### Icon component 101 | 102 | Now let's move into a more complex example. We have the following CSS and we don't have control over it cause it's auto-generated (maybe, by our *Designer*): 103 | 104 | ```css 105 | @font-face { 106 | font-family: Custom Icons; 107 | src: url("./Icon.woff"); 108 | } 109 | 110 | .icon { 111 | font-family: Custom Icons, Helvetica, sans-serif; 112 | font-style: normal !important; 113 | } 114 | 115 | .icon-arrow-down:before { 116 | content: "\e900"; 117 | } 118 | 119 | .icon-arrow-left:before { 120 | content: "\e901"; 121 | } 122 | 123 | .icon-arrow-right:before { 124 | content: "\e902"; 125 | } 126 | 127 | .icon-arrow-up:before { 128 | content: "\e903"; 129 | } 130 | ``` 131 | 132 | Of course, you **really** want to avoid as much `className` duplication as possible, and you notice the pattern of every icon: `icon-${type}`. 133 | Well, luckily, you have a second *optional* parameter to the **react-css-props** module to specify a **lambda with your given pattern**, as follows: 134 | 135 | ```jsx 136 | import React from 'react'; 137 | import cn from 'classnames'; 138 | import cssProps from 'react-css-props'; 139 | 140 | import theme from './Icon.css'; 141 | 142 | // Prepare it with your custom mapper function 143 | const toCSS = cssProps(theme, type => `icon-${type}`); 144 | 145 | const Icon = (props) => ( 146 | 147 | ); 148 | 149 | export default Icon; 150 | ``` 151 | 152 | Isn't that *neat*? No longer need for code like this: ``, where you had to type **three times** the word "icon" to finnally render it! 153 | 154 | ## API 155 | 156 | ### `cssProps(theme: Object, mapper?: (string) => string)` 157 | 158 | The first argument is the `theme` imported from CSS Modules which, as [confirmed](https://twitter.com/markdalgleish/status/804033901161156608) by **Mark Dalgleish** (the creator of CSS Modules), it's "just a regular JavaScript *object* :)". 159 | The second argument is **optional** and corresponds to a mapper *function* in case your CSS clases follow a defined pattern. 160 | This method **returns** the next function (you can name any of these two literally *whatever* you like): 161 | 162 | ### `toCSS(props: Object, defaultClass?: string): Array` 163 | 164 | The first argument are the `props` of your component, likely to have been delegated to only your plausible CSS classes with the **spread operator (`...props`)**. 165 | The second argument is also **optional** and consists on a default CSS class, if you have any. 166 | This method will **always return a `string` array** of matching CSS classes between your *theme* and your *props*. 167 | This plays along very well with [**classnames**](https://github.com/JedWatson/classnames) to customize your components, as shown in the examples. 168 | 169 | ## Stack 170 | 171 | - [Flow](https://flowtype.org/) *- a static type checker for JavaScript*. 172 | - [Jest](https://facebook.github.io/jest/) *- painless JavaScript testing*. 173 | - [Yarn](https://yarnpkg.com/) *- fast, reliable, and secure dependency management*. 174 | 175 | ## License 176 | 177 | MIT © [Flavio Corpa](http://flaviocorpa.com) 178 | -------------------------------------------------------------------------------- /flow-typed/npm/jest_v17.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: e70b796308162f4f3eb388f11a0f8cb5 2 | // flow-typed version: c3a5268faf/jest_v17.x.x/flow_>=v0.33.x 3 | 4 | 5 | type JestMockFn = { 6 | (...args: Array): any; 7 | /** An object for introspecting mock calls */ 8 | mock: { 9 | /** An array that represents all calls that have been made into this mock function. Each call is represented by an array of arguments that were passed during the call. */ 10 | calls: Array>; 11 | /** An array that contains all the object instances that have been instantiated from this mock function. */ 12 | instances: mixed; 13 | }; 14 | /** Resets all information stored in the mockFn.mock.calls and mockFn.mock.instances arrays. 15 | * Often this is useful when you want to clean up a mock's usage data between two assertions. 16 | */ 17 | mockClear(): Function; 18 | /** Resets all information stored in the mock 19 | * This is useful when you want to completely restore a mock back to its initial state. 20 | */ 21 | mockReset(): Function; 22 | /** Accepts a function that should be used as the implementation of the mock. The mock itself will still record all calls that go into and instances that come from itself – the only difference is that the implementation will also be executed when the mock is called. */ 23 | mockImplementation(fn: Function): JestMockFn; 24 | /** Accepts a function that will be used as an implementation of the mock for one call to the mocked function. Can be chained so that multiple function calls produce different results. */ 25 | mockImplementationOnce(fn: Function): JestMockFn; 26 | /** Just a simple sugar function for returning `this` */ 27 | mockReturnThis(): void; 28 | /** Deprecated: use jest.fn(() => value) instead */ 29 | mockReturnValue(value: any): JestMockFn; 30 | /** Sugar for only returning a value once inside your mock */ 31 | mockReturnValueOnce(value: any): JestMockFn; 32 | } 33 | 34 | type JestAsymmetricEqualityType = { 35 | /** A custom Jasmine equality tester */ 36 | asymmetricMatch(value: mixed): boolean; 37 | } 38 | 39 | type JestCallsType = { 40 | allArgs(): mixed; 41 | all(): mixed; 42 | any(): boolean; 43 | count(): number; 44 | first(): mixed; 45 | mostRecent(): mixed; 46 | reset(): void; 47 | } 48 | 49 | type JestClockType = { 50 | install(): void; 51 | mockDate(date: Date): void; 52 | tick(): void; 53 | uninstall(): void; 54 | } 55 | 56 | type JestMatcherResult = { 57 | message?: string | ()=>string; 58 | pass: boolean; 59 | }; 60 | 61 | type JestMatcher = (actual: any, expected: any) => JestMatcherResult; 62 | 63 | type JestExpectType = { 64 | not: JestExpectType; 65 | /** If you have a mock function, you can use .lastCalledWith to test what arguments it was last called with. */ 66 | lastCalledWith(...args: Array): void; 67 | /** toBe just checks that a value is what you expect. It uses === to check strict equality. */ 68 | toBe(value: any): void; 69 | /** Use .toHaveBeenCalled to ensure that a mock function got called. */ 70 | toBeCalled(): void; 71 | /** Use .toBeCalledWith to ensure that a mock function was called with specific arguments. */ 72 | toBeCalledWith(...args: Array): void; 73 | /** Using exact equality with floating point numbers is a bad idea. Rounding means that intuitive things fail. */ 74 | toBeCloseTo(num: number, delta: any): void; 75 | /** Use .toBeDefined to check that a variable is not undefined. */ 76 | toBeDefined(): void; 77 | /** Use .toBeFalsy when you don't care what a value is, you just want to ensure a value is false in a boolean context. */ 78 | toBeFalsy(): void; 79 | /** To compare floating point numbers, you can use toBeGreaterThan. */ 80 | toBeGreaterThan(number: number): void; 81 | /** To compare floating point numbers, you can use toBeGreaterThanOrEqual. */ 82 | toBeGreaterThanOrEqual(number: number): void; 83 | /** To compare floating point numbers, you can use toBeLessThan. */ 84 | toBeLessThan(number: number): void; 85 | /** To compare floating point numbers, you can use toBeLessThanOrEqual. */ 86 | toBeLessThanOrEqual(number: number): void; 87 | /** Use .toBeInstanceOf(Class) to check that an object is an instance of a class. */ 88 | toBeInstanceOf(cls: Class<*>): void; 89 | /** .toBeNull() is the same as .toBe(null) but the error messages are a bit nicer. */ 90 | toBeNull(): void; 91 | /** Use .toBeTruthy when you don't care what a value is, you just want to ensure a value is true in a boolean context. */ 92 | toBeTruthy(): void; 93 | /** Use .toBeUndefined to check that a variable is undefined. */ 94 | toBeUndefined(): void; 95 | /** Use .toContain when you want to check that an item is in a list. For testing the items in the list, this uses ===, a strict equality check. */ 96 | toContain(item: any): void; 97 | /** Use .toContainEqual when you want to check that an item is in a list. For testing the items in the list, this matcher recursively checks the equality of all fields, rather than checking for object identity. 98 | */ 99 | toContainEqual(item: any): void; 100 | /** Use .toEqual when you want to check that two objects have the same value. This matcher recursively checks the equality of all fields, rather than checking for object identity. */ 101 | toEqual(value: any): void; 102 | /** Use .toHaveBeenCalled to ensure that a mock function got called. */ 103 | toHaveBeenCalled(): void; 104 | /** Use .toHaveBeenCalledTimes to ensure that a mock function got called exact number of times. */ 105 | toHaveBeenCalledTimes(number: number): void; 106 | /** Use .toHaveBeenCalledWith to ensure that a mock function was called with specific arguments. */ 107 | toHaveBeenCalledWith(...args: Array): void; 108 | /** Use .toMatch to check that a string matches a regular expression. */ 109 | toMatch(regexp: RegExp): void; 110 | /** This ensures that a React component matches the most recent snapshot. */ 111 | toMatchSnapshot(): void; 112 | /** Use .toThrow to test that a function throws when it is called. */ 113 | toThrow(message?: string | Error): void; 114 | /** Use .toThrowError to test that a function throws a specific error when it is called. The argument can be a string for the error message, a class for the error, or a regex that should match the error. */ 115 | toThrowError(message?: string | Error | RegExp): void; 116 | /** Use .toThrowErrorMatchingSnapshot to test that a function throws a error matching the most recent snapshot when it is called. */ 117 | toThrowErrorMatchingSnapshot(): void; 118 | } 119 | 120 | type JestObjectType = { 121 | /** 122 | * Disables automatic mocking in the module loader. 123 | * 124 | * After this method is called, all `require()`s will return the real versions of each module (rather than a mocked version). 125 | */ 126 | disableAutomock(): JestObjectType; 127 | /** An un-hoisted version of disableAutomock */ 128 | autoMockOff(): JestObjectType; 129 | /** Enables automatic mocking in the module loader. */ 130 | enableAutomock(): JestObjectType; 131 | /** An un-hoisted version of enableAutomock */ 132 | autoMockOn(): JestObjectType; 133 | /** Resets the state of all mocks. Equivalent to calling .mockReset() on every mocked function. */ 134 | resetAllMocks(): JestObjectType; 135 | /** Removes any pending timers from the timer system. */ 136 | clearAllTimers(): void; 137 | /** The same as `mock` but not moved to the top of the expectation by babel-jest. */ 138 | doMock(moduleName: string, moduleFactory?: any): void; 139 | /** The same as `unmock` but not moved to the top of the expectation by babel-jest. */ 140 | dontMock(moduleName: string): void; 141 | /** Returns a new, unused mock function. Optionally takes a mock implementation. */ 142 | fn(implementation?: Function): JestMockFn; 143 | /** Determines if the given function is a mocked function. */ 144 | isMockFunction(fn: Function): boolean; 145 | /** Given the name of a module, use the automatic mocking system to generate a mocked version of the module for you. */ 146 | genMockFromModule(moduleName: string): any; 147 | /** 148 | * Mocks a module with an auto-mocked version when it is being required. 149 | * 150 | * The second argument can be used to specify an explicit module factory that is being run instead of using Jest's automocking feature. 151 | * 152 | * The third argument can be used to create virtual mocks – mocks of modules that don't exist anywhere in the system. 153 | */ 154 | mock(moduleName: string, moduleFactory?: any): JestMockFn; 155 | /** Resets the module registry - the cache of all required modules. This is useful to isolate modules where local state might conflict between tests. */ 156 | resetModules(): void; 157 | /** Exhausts the micro-task queue (usually interfaced in node via process.nextTick). */ 158 | runAllTicks(): void; 159 | /** Exhausts the macro-task queue (i.e., all tasks queued by setTimeout(), setInterval(), and setImmediate()). */ 160 | runAllTimers(): void; 161 | /** Exhausts all tasks queued by setImmediate(). */ 162 | runAllImmediates(): void; 163 | /** Executes only the macro task queue (i.e. all tasks queued by setTimeout() or setInterval() and setImmediate()). */ 164 | runTimersToTime(msToRun: number): void; 165 | /** Executes only the macro-tasks that are currently pending (i.e., only the tasks that have been queued by setTimeout() or setInterval() up to this point) */ 166 | runOnlyPendingTimers(): void; 167 | /** Explicitly supplies the mock object that the module system should return for the specified module. Note: It is recommended to use jest.mock() instead. */ 168 | setMock(moduleName: string, moduleExports: any): JestObjectType; 169 | /** Indicates that the module system should never return a mocked version of the specified module from require() (e.g. that it should always return the real module). */ 170 | unmock(moduleName: string): JestObjectType; 171 | /** Instructs Jest to use fake versions of the standard timer functions (setTimeout, setInterval, clearTimeout, clearInterval, nextTick, setImmediate and clearImmediate). */ 172 | useFakeTimers(): JestObjectType; 173 | /** Instructs Jest to use the real versions of the standard timer functions. */ 174 | useRealTimers(): JestObjectType; 175 | } 176 | 177 | type JestSpyType = { 178 | calls: JestCallsType; 179 | } 180 | 181 | /** Runs this function after every test inside this context */ 182 | declare function afterEach(fn: Function): void; 183 | /** Runs this function before every test inside this context */ 184 | declare function beforeEach(fn: Function): void; 185 | /** Runs this function after all tests have finished inside this context */ 186 | declare function afterAll(fn: Function): void; 187 | /** Runs this function before any tests have started inside this context */ 188 | declare function beforeAll(fn: Function): void; 189 | /** A context for grouping tests together */ 190 | declare function describe(name: string, fn: Function): void; 191 | 192 | /** An individual test unit */ 193 | declare var it: { 194 | /** 195 | * An individual test unit 196 | * 197 | * @param {string} Name of Test 198 | * @param {Function} Test 199 | */ 200 | (name: string, fn?: Function): ?Promise; 201 | /** 202 | * Only run this test 203 | * 204 | * @param {string} Name of Test 205 | * @param {Function} Test 206 | */ 207 | only(name: string, fn?: Function): ?Promise; 208 | /** 209 | * Skip running this test 210 | * 211 | * @param {string} Name of Test 212 | * @param {Function} Test 213 | */ 214 | skip(name: string, fn?: Function): ?Promise; 215 | }; 216 | declare function fit(name: string, fn: Function): ?Promise; 217 | /** An individual test unit */ 218 | declare var test: typeof it; 219 | /** A disabled group of tests */ 220 | declare var xdescribe: typeof describe; 221 | /** A focused group of tests */ 222 | declare var fdescribe: typeof describe; 223 | /** A disabled individual test */ 224 | declare var xit: typeof it; 225 | /** A disabled individual test */ 226 | declare var xtest: typeof it; 227 | 228 | /** The expect function is used every time you want to test a value */ 229 | declare var expect: { 230 | /** The object that you want to make assertions against */ 231 | (value: any): JestExpectType; 232 | /** Add additional Jasmine matchers to Jest's roster */ 233 | extend(matchers: {[name:string]: JestMatcher}): void; 234 | }; 235 | 236 | // TODO handle return type 237 | // http://jasmine.github.io/2.4/introduction.html#section-Spies 238 | declare function spyOn(value: mixed, method: string): Object; 239 | 240 | /** Holds all functions related to manipulating test runner */ 241 | declare var jest: JestObjectType 242 | 243 | /** 244 | * The global Jamine object, this is generally not exposed as the public API, 245 | * using features inside here could break in later versions of Jest. 246 | */ 247 | declare var jasmine: { 248 | DEFAULT_TIMEOUT_INTERVAL: number; 249 | any(value: mixed): JestAsymmetricEqualityType; 250 | anything(): void; 251 | arrayContaining(value: mixed[]): void; 252 | clock(): JestClockType; 253 | createSpy(name: string): JestSpyType; 254 | objectContaining(value: Object): void; 255 | stringMatching(value: string): void; 256 | } 257 | --------------------------------------------------------------------------------