├── .prettierrc ├── .gitignore ├── logo.png ├── .flowconfig ├── .travis.yml ├── .babelrc ├── tsconfig.json ├── rollup.config.js ├── example ├── index.html ├── simple.js ├── shared.js ├── complex.js └── single.js ├── LICENSE ├── src ├── unstated.d.ts └── unstated.js ├── logo.svg ├── README.md ├── package.json ├── __tests__ ├── unstated.js └── unstated.tsx └── flow-typed └── npm └── jest_v22.x.x.js /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | lib 4 | .cache 5 | dist 6 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-phan/unstated-x/HEAD/logo.png -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | 3 | [include] 4 | 5 | [libs] 6 | 7 | [lints] 8 | 9 | [options] 10 | 11 | [strict] 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | git: 2 | depth: 1 3 | sudo: false 4 | language: node_js 5 | node_js: 6 | - '8' 7 | cache: 8 | yarn: true 9 | directories: 10 | - node_modules 11 | script: 12 | - yarn test && yarn flow 13 | - yarn typescript 14 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "modules": false, 7 | "loose": true 8 | } 9 | ], 10 | "@babel/preset-react", 11 | "@babel/preset-flow" 12 | ], 13 | "plugins": [ 14 | "@babel/plugin-proposal-class-properties" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es5", 5 | "lib": ["es6", "dom"], 6 | "sourceMap": true, 7 | "jsx": "react", 8 | "moduleResolution": "node", 9 | "forceConsistentCasingInFileNames": true, 10 | "strict": true, 11 | "noEmit": true 12 | }, 13 | "include": ["src/**/*", "__test__/*.tsx"] 14 | } 15 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel'; 2 | import pkg from './package.json'; 3 | 4 | export default { 5 | input: 'src/unstated.js', 6 | output: [ 7 | { 8 | file: pkg.main, 9 | format: 'cjs' 10 | }, 11 | { 12 | file: pkg.module, 13 | format: 'es' 14 | } 15 | ], 16 | external: [ 17 | ...Object.keys(pkg.dependencies || {}), 18 | ...Object.keys(pkg.peerDependencies || {}) 19 | ], 20 | plugins: [babel()] 21 | }; 22 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Unstated - Examples 6 | 7 | 8 |

Simple

9 |
10 | 11 | 12 |

Complex

13 |
14 | 15 | 16 |

Shared

17 |
18 | 19 | 20 |

Single Subscribe

21 |
22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /example/simple.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import React from 'react'; 3 | import { render } from 'react-dom'; 4 | import { Provider, Subscribe, Container } from '../src/unstated'; 5 | 6 | type CounterState = { 7 | count: number 8 | }; 9 | 10 | class CounterContainer extends Container { 11 | state = { count: 0 }; 12 | 13 | increment() { 14 | this.setState({ count: this.state.count + 1 }); 15 | } 16 | 17 | decrement() { 18 | this.setState({ count: this.state.count - 1 }); 19 | } 20 | } 21 | 22 | function Counter() { 23 | return ( 24 | 25 | {counter => ( 26 |
27 | 28 | {counter.state.count} 29 | 30 |
31 | )} 32 |
33 | ); 34 | } 35 | 36 | render( 37 | 38 | 39 | , 40 | window.simple 41 | ); 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy 2 | of this software and associated documentation files (the "Software"), to deal 3 | in the Software without restriction, including without limitation the rights 4 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 5 | copies of the Software, and to permit persons to whom the Software is 6 | furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all 9 | copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 17 | SOFTWARE. 18 | -------------------------------------------------------------------------------- /example/shared.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import React from 'react'; 3 | import { render } from 'react-dom'; 4 | import { Provider, Subscribe, Container } from '../src/unstated'; 5 | 6 | type CounterState = { 7 | count: number 8 | }; 9 | 10 | class CounterContainer extends Container { 11 | state = { count: 0 }; 12 | 13 | increment() { 14 | this.setState({ count: this.state.count + 1 }); 15 | } 16 | 17 | decrement() { 18 | this.setState({ count: this.state.count - 1 }); 19 | } 20 | } 21 | 22 | const sharedCounterContainer = new CounterContainer(); 23 | 24 | function Counter() { 25 | return ( 26 | 27 | {counter => ( 28 |
29 | 30 | {counter.state.count} 31 | 32 |
33 | )} 34 |
35 | ); 36 | } 37 | 38 | render( 39 | 40 | 41 | , 42 | window.shared 43 | ); 44 | -------------------------------------------------------------------------------- /src/unstated.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export class Container { 4 | constructor(state?: object); 5 | state: State; 6 | _listeners: [Function]; 7 | setStateSync( 8 | state: 9 | | ((prevState: Readonly) => Partial | State | null) 10 | | (Partial | State | null), 11 | callback?: () => void 12 | ): void; 13 | setState( 14 | state: 15 | | ((prevState: Readonly) => Partial | State | null) 16 | | (Partial | State | null), 17 | callback?: () => void 18 | ): Promise; 19 | subscribe(fn: (changes: {}) => any): void; 20 | unsubscribe(fn: (changes: {}) => any): void; 21 | } 22 | 23 | export interface ContainerType { 24 | new (...args: any[]): Container; 25 | } 26 | 27 | interface SubscribeProps { 28 | to: (ContainerType | Container)[]; 29 | children(...instances: any[]): React.ReactNode; 30 | } 31 | interface SubscribeOneProps { 32 | to: (ContainerType | Container); 33 | bind: string[]; 34 | children(...instances: any[]): React.ReactNode; 35 | } 36 | 37 | export class Subscribe extends React.Component {} 38 | export class SubscribeOne extends React.Component {} 39 | 40 | export interface ProviderProps { 41 | inject?: Container[]; 42 | children: React.ReactNode; 43 | } 44 | 45 | export const Provider: React.SFC; 46 | -------------------------------------------------------------------------------- /logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This project will not be maintained anymore in favor of Hooks: https://github.com/paul-phan/global-state-hook 2 | 3 | 4 | # Unstated-x 5 | 6 | > State so simple, it goes without saying 7 | 8 | ## Installation 9 | 10 | ```sh 11 | yarn add unstated-x 12 | ``` 13 | 14 | ## About Unstated 15 | 16 | The original documentation can be found in: https://github.com/jamiebuilds/unstated 17 | 18 | ## Additional Functions 19 | 20 | > Fix broken Controlled Component when using Asynchronous Unstated `setState` by using `setStateSync` 21 | 22 | ```js 23 | function App() { 24 | return ( 25 | 26 | {app => ( 27 | app.setStateSync({ value: e.target.value })} 29 | value={app.state.value} 30 | /> 31 | )} 32 | 33 | ); 34 | } 35 | ``` 36 | 37 | > Add Subscribe to only one Container and only state you want to: 38 | 39 | ```js 40 | function App() { 41 | return ( 42 | 43 | {app => ( 44 |
45 | This will update only if the "value" state is updated, others will not 46 | app.setStateSync({ value: e.target.value })} 48 | value={app.state.value} 49 | /> 50 |
51 | )} 52 |
53 | ); 54 | } 55 | ``` 56 | 57 | You can checkout the example to see more. 58 | 59 | > In this version, if you use React version 16.3 or higher, it will use the native React.createContext rather than the `create-react-context` library. I also removed support for multiple `` it clearly unnecessary. 60 | 61 | ## Contribution 62 | 63 | Feel free to create issues/pull request, I will eager to apply it to unstated-x if it good enough :) 64 | -------------------------------------------------------------------------------- /example/complex.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import React from 'react'; 3 | import { render } from 'react-dom'; 4 | import { Provider, Subscribe, Container } from '../src/unstated'; 5 | 6 | type AppState = { 7 | amount: number 8 | }; 9 | 10 | class AppContainer extends Container { 11 | state = { 12 | amount: 1 13 | }; 14 | 15 | setAmount(amount: number) { 16 | this.setState({ amount }); 17 | } 18 | } 19 | 20 | type CounterState = { 21 | count: number 22 | }; 23 | 24 | class CounterContainer extends Container { 25 | state = { 26 | count: 0 27 | }; 28 | 29 | increment(amount: number) { 30 | this.setState({ count: this.state.count + amount }); 31 | } 32 | 33 | decrement(amount: number) { 34 | this.setState({ count: this.state.count - amount }); 35 | } 36 | } 37 | 38 | function Counter() { 39 | return ( 40 | 41 | {(app, counter) => ( 42 |
43 | Count: {counter.state.count} 44 | 45 | 46 |
47 | )} 48 |
49 | ); 50 | } 51 | 52 | function App() { 53 | return ( 54 | 55 | {app => ( 56 |
57 | 58 | 59 | { 63 | app.setAmount(parseInt(event.currentTarget.value, 10)); 64 | }} 65 | /> 66 |
67 | )} 68 |
69 | ); 70 | } 71 | 72 | render( 73 | 74 | 75 | , 76 | window.complex 77 | ); 78 | -------------------------------------------------------------------------------- /example/single.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import React from 'react'; 3 | import { render } from 'react-dom'; 4 | import { Container, Provider, SubscribeOne } from '../src/unstated'; 5 | 6 | type CounterState = { 7 | count: number 8 | }; 9 | 10 | class AppContainer extends Container { 11 | state = {}; 12 | } 13 | 14 | function App() { 15 | return ( 16 |
17 | Open React DevTools => Highlight updates to check result 18 | 19 | {app => ( 20 |
21 | Subscribe to only one state property:: {app.state.test} 22 |
23 | app.setStateSync({ test: e.target.value })} 25 | value={app.state.test || ''} 26 | /> 27 |
28 |
29 | )} 30 |
31 | 32 | {app => ( 33 |
34 | Subscribe to only one state property:: {app.state.test1} 35 |
36 | app.setStateSync({ test1: e.target.value })} 38 | value={app.state.test1 || ''} 39 | /> 40 |
41 |
42 | )} 43 |
44 | 45 | {app => ( 46 |
47 | Subscribe to only one state property:: {app.state.test2} 48 |
49 | app.setStateSync({ test2: e.target.value })} 51 | value={app.state.test2 || ''} 52 | /> 53 |
54 |
55 | )} 56 |
57 |
58 | ); 59 | } 60 | 61 | render( 62 | 63 | 64 | , 65 | window.single 66 | ); 67 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "unstated-x", 3 | "version": "1.0.1", 4 | "description": "Add more powers to unstated", 5 | "main": "lib/unstated.js", 6 | "module": "lib/unstated.es.js", 7 | "types": "lib/unstated.d.ts", 8 | "repository": "https://github.com/paul-phan/unstated-x.git", 9 | "license": "MIT", 10 | "files": [ 11 | "lib" 12 | ], 13 | "scripts": { 14 | "clean": "rm -rf lib", 15 | "build": "rollup -c && flow-copy-source src lib && cp src/unstated.d.ts lib/unstated.d.ts", 16 | "typecheck": "flow", 17 | "test": "jest", 18 | "format": "prettier --write **/*.{js,json,md}", 19 | "prepublish": "yarn clean && yarn build", 20 | "precommit": "lint-staged", 21 | "example": "parcel example/index.html", 22 | "typescript": "tsc -p tsconfig.json" 23 | }, 24 | "dependencies": {}, 25 | "peerDependencies": { 26 | "react": "^16.14.0" 27 | }, 28 | "resolutions": { 29 | "babel-core": "7.0.0-bridge.0" 30 | }, 31 | "devDependencies": { 32 | "@babel/core": "^7.15.0", 33 | "@babel/plugin-proposal-class-properties": "^7.14.5", 34 | "@babel/preset-env": "^7.15.0", 35 | "@babel/preset-flow": "^7.14.5", 36 | "@babel/preset-react": "^7.14.5", 37 | "@babel/register": "^7.15.3", 38 | "@types/react": "^17.0.17", 39 | "babel-core": "^7.0.0-bridge.0", 40 | "babel-loader": "^8.2.2", 41 | "flow-bin": "^0.157.0", 42 | "flow-copy-source": "^2.0.9", 43 | "husky": "^7.0.1", 44 | "jest": "^27.0.6", 45 | "jsdom": "^16.7.0", 46 | "lint-staged": "^11.1.2", 47 | "parcel-bundler": "^1.12.5", 48 | "prettier": "^2.3.2", 49 | "prop-types": "^15.7.2", 50 | "react": "^17.0.2", 51 | "react-dom": "^17.0.2", 52 | "react-test-renderer": "^17.0.2", 53 | "rollup": "^2.56.2", 54 | "rollup-plugin-babel": "^4.4.0", 55 | "typescript": "^4.3.5" 56 | }, 57 | "lint-staged": { 58 | "*.{js,json,md}": [ 59 | "prettier --write", 60 | "git add" 61 | ] 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /__tests__/unstated.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import React from 'react'; 3 | import renderer from 'react-test-renderer'; 4 | import { Provider, Subscribe, Container } from '../src/unstated'; 5 | 6 | function render(element) { 7 | return renderer.create(element).toJSON(); 8 | } 9 | 10 | class CounterContainer extends Container<{ count: number }> { 11 | state = { count: 0 }; 12 | increment(amount = 1) { 13 | this.setState({ count: this.state.count + amount }); 14 | } 15 | decrement(amount = 1) { 16 | this.setState({ count: this.state.count - amount }); 17 | } 18 | } 19 | 20 | class AmounterContainer extends Container<{ amount: number }> { 21 | state = { amount: 1 }; 22 | setAmount(amount) { 23 | this.setState({ amount }); 24 | } 25 | } 26 | 27 | function Counter() { 28 | return ( 29 | 30 | {counter => ( 31 |
32 | {counter.state.count} 33 | 34 | 35 |
36 | )} 37 |
38 | ); 39 | } 40 | 41 | function CounterWithAmount() { 42 | return ( 43 | 44 | {(counter, amounter) => ( 45 |
46 | {counter.state.count} 47 | 50 | 53 |
54 | )} 55 |
56 | ); 57 | } 58 | 59 | function CounterWithAmountApp() { 60 | return ( 61 | 62 | {amounter => ( 63 |
64 | 65 | { 69 | amounter.setAmount(parseInt(event.currentTarget.value, 10)); 70 | }} 71 | /> 72 |
73 | )} 74 |
75 | ); 76 | } 77 | 78 | test('basic', () => { 79 | // still too lazy 80 | }); 81 | -------------------------------------------------------------------------------- /__tests__/unstated.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Provider, Subscribe, Container } from '../src/unstated'; 3 | 4 | class CounterContainer extends Container<{ count: number }> { 5 | state = { count: 0 }; 6 | increment(amount = 1) { 7 | this.setState({ count: this.state.count + amount }); 8 | } 9 | decrement(amount = 1) { 10 | this.setState({ count: this.state.count - amount }); 11 | } 12 | } 13 | 14 | class AmounterContainer extends Container<{ amount: number }> { 15 | state = { amount: 1 }; 16 | setAmount(amount: number) { 17 | this.setState({ amount }); 18 | } 19 | } 20 | 21 | function Counter() { 22 | return ( 23 | 24 | {(counter: CounterContainer) => ( 25 |
26 | {counter.state.count} 27 | 28 | 29 |
30 | )} 31 |
32 | ); 33 | } 34 | 35 | function CounterWithAmount() { 36 | return ( 37 | 38 | {(counter: CounterContainer, amounter: AmounterContainer) => ( 39 |
40 | {counter.state.count} 41 | 44 | 47 |
48 | )} 49 |
50 | ); 51 | } 52 | 53 | function CounterWithAmountApp() { 54 | return ( 55 | 56 | {(amounter: AmounterContainer) => ( 57 |
58 | 59 | { 63 | amounter.setAmount(parseInt(event.currentTarget.value, 10)); 64 | }} 65 | /> 66 |
67 | )} 68 |
69 | ); 70 | } 71 | 72 | const sharedAmountContainer = new AmounterContainer(); 73 | 74 | function CounterWithSharedAmountApp() { 75 | return ( 76 | 77 | {(amounter: AmounterContainer) => ( 78 |
79 | 80 | { 84 | amounter.setAmount(parseInt(event.currentTarget.value, 10)); 85 | }} 86 | /> 87 |
88 | )} 89 |
90 | ); 91 | } 92 | 93 | let counter = new CounterContainer(); 94 | let render = () => ( 95 | 96 | 97 | 98 | ); 99 | -------------------------------------------------------------------------------- /src/unstated.js: -------------------------------------------------------------------------------- 1 | import React, { createContext } from 'react'; 2 | 3 | export const StateContext = createContext(null); 4 | 5 | export class Container { 6 | state = {}; 7 | _listeners = []; 8 | 9 | constructor(state = {}) { 10 | this.state = state; 11 | } 12 | 13 | _setState(updater, callback) { 14 | let nextState; 15 | 16 | if (typeof updater === 'function') { 17 | nextState = updater(this.state); 18 | } else { 19 | nextState = updater; 20 | } 21 | 22 | if (nextState == null) { 23 | if (callback) callback(); 24 | } 25 | return nextState; 26 | } 27 | 28 | setStateSync(updater, callback) { 29 | const nextState = this._setState(updater, callback); 30 | 31 | this.state = Object.assign({}, this.state, nextState); 32 | 33 | this._listeners.forEach(fn => fn(nextState)); 34 | } 35 | 36 | setState(updater, callback) { 37 | return Promise.resolve().then(() => { 38 | const nextState = this._setState(updater, callback); 39 | 40 | this.state = Object.assign({}, this.state, nextState); 41 | 42 | let promises = this._listeners.map(listener => listener(nextState)); 43 | 44 | return Promise.all(promises).then(() => { 45 | if (callback) { 46 | return callback(); 47 | } 48 | }); 49 | }); 50 | } 51 | 52 | subscribe(fn) { 53 | this._listeners.push(fn); 54 | } 55 | 56 | unsubscribe(fn) { 57 | this._listeners = this._listeners.filter(f => f !== fn); 58 | } 59 | } 60 | 61 | export class Subscribe extends React.Component { 62 | state = {}; 63 | instances = []; 64 | unmounted = false; 65 | 66 | componentWillUnmount() { 67 | this.unmounted = true; 68 | this._unsubscribe(); 69 | } 70 | 71 | _unsubscribe() { 72 | this.instances.forEach(container => { 73 | container.unsubscribe(this.onUpdate); 74 | }); 75 | } 76 | 77 | onUpdate = () => { 78 | return new Promise(resolve => { 79 | if (!this.unmounted) { 80 | this.setState({}, resolve); 81 | } else { 82 | resolve(); 83 | } 84 | }); 85 | }; 86 | 87 | _createInstances(map, containers) { 88 | this._unsubscribe(); 89 | 90 | if (map === null) { 91 | throw new Error( 92 | 'You must wrap your components with a ' 93 | ); 94 | } 95 | 96 | let safeMap = map; 97 | let instances = containers.map(ContainerItem => { 98 | let instance; 99 | 100 | if ( 101 | typeof ContainerItem === 'object' && 102 | ContainerItem instanceof Container 103 | ) { 104 | instance = ContainerItem; 105 | } else { 106 | instance = safeMap.get(ContainerItem); 107 | 108 | if (!instance) { 109 | instance = new ContainerItem(); 110 | safeMap.set(ContainerItem, instance); 111 | } 112 | } 113 | 114 | instance.unsubscribe(this.onUpdate); 115 | instance.subscribe(this.onUpdate); 116 | 117 | return instance; 118 | }); 119 | 120 | this.instances = instances; 121 | return instances; 122 | } 123 | 124 | render() { 125 | return ( 126 | 127 | {map => 128 | Reflect.apply( 129 | this.props.children, 130 | null, 131 | this._createInstances(map, this.props.to) 132 | ) 133 | } 134 | 135 | ); 136 | } 137 | } 138 | 139 | export class SubscribeOne extends React.Component { 140 | state = {}; 141 | instance = null; 142 | unmounted = false; 143 | 144 | componentWillUnmount() { 145 | this.unmounted = true; 146 | this._unsubscribe(); 147 | } 148 | 149 | _unsubscribe() { 150 | this.instance && this.instance.unsubscribe(this.onUpdate); 151 | } 152 | 153 | onUpdate = changedState => { 154 | return new Promise(resolve => { 155 | if ( 156 | !this.unmounted && 157 | Array.isArray(this.props.bind) && 158 | Object.keys(changedState).filter(key => this.props.bind.includes(key)) 159 | .length > 0 160 | ) { 161 | this.setState({}, resolve); 162 | } else { 163 | resolve(); 164 | } 165 | }); 166 | }; 167 | 168 | _createInstance(map, container) { 169 | this._unsubscribe(); 170 | if (map === null) { 171 | throw new Error( 172 | 'You must wrap your components with a ' 173 | ); 174 | } 175 | let safeMap = map; 176 | if (typeof container === 'object' && container instanceof Container) { 177 | this.instance = container; 178 | } else { 179 | this.instance = safeMap.get(container); 180 | 181 | if (!this.instance) { 182 | this.instance = new container(); 183 | safeMap.set(container, this.instance); 184 | } 185 | } 186 | 187 | this.instance.unsubscribe(this.onUpdate); 188 | this.instance.subscribe(this.onUpdate); 189 | 190 | return this.instance; 191 | } 192 | 193 | render() { 194 | return ( 195 | 196 | {map => this.props.children(this._createInstance(map, this.props.to))} 197 | 198 | ); 199 | } 200 | } 201 | 202 | export function Provider(props) { 203 | return ( 204 | 205 | {props.children} 206 | 207 | ); 208 | } 209 | -------------------------------------------------------------------------------- /flow-typed/npm/jest_v22.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 6e1fc0a644aa956f79029fec0709e597 2 | // flow-typed version: 07ebad4796/jest_v22.x.x/flow_>=v0.39.x 3 | 4 | type JestMockFn, TReturn> = { 5 | (...args: TArguments): TReturn, 6 | /** 7 | * An object for introspecting mock calls 8 | */ 9 | mock: { 10 | /** 11 | * An array that represents all calls that have been made into this mock 12 | * function. Each call is represented by an array of arguments that were 13 | * passed during the call. 14 | */ 15 | calls: Array, 16 | /** 17 | * An array that contains all the object instances that have been 18 | * instantiated from this mock function. 19 | */ 20 | instances: Array 21 | }, 22 | /** 23 | * Resets all information stored in the mockFn.mock.calls and 24 | * mockFn.mock.instances arrays. Often this is useful when you want to clean 25 | * up a mock's usage data between two assertions. 26 | */ 27 | mockClear(): void, 28 | /** 29 | * Resets all information stored in the mock. This is useful when you want to 30 | * completely restore a mock back to its initial state. 31 | */ 32 | mockReset(): void, 33 | /** 34 | * Removes the mock and restores the initial implementation. This is useful 35 | * when you want to mock functions in certain test cases and restore the 36 | * original implementation in others. Beware that mockFn.mockRestore only 37 | * works when mock was created with jest.spyOn. Thus you have to take care of 38 | * restoration yourself when manually assigning jest.fn(). 39 | */ 40 | mockRestore(): void, 41 | /** 42 | * Accepts a function that should be used as the implementation of the mock. 43 | * The mock itself will still record all calls that go into and instances 44 | * that come from itself -- the only difference is that the implementation 45 | * will also be executed when the mock is called. 46 | */ 47 | mockImplementation( 48 | fn: (...args: TArguments) => TReturn 49 | ): JestMockFn, 50 | /** 51 | * Accepts a function that will be used as an implementation of the mock for 52 | * one call to the mocked function. Can be chained so that multiple function 53 | * calls produce different results. 54 | */ 55 | mockImplementationOnce( 56 | fn: (...args: TArguments) => TReturn 57 | ): JestMockFn, 58 | /** 59 | * Just a simple sugar function for returning `this` 60 | */ 61 | mockReturnThis(): void, 62 | /** 63 | * Deprecated: use jest.fn(() => value) instead 64 | */ 65 | mockReturnValue(value: TReturn): JestMockFn, 66 | /** 67 | * Sugar for only returning a value once inside your mock 68 | */ 69 | mockReturnValueOnce(value: TReturn): JestMockFn 70 | }; 71 | 72 | type JestAsymmetricEqualityType = { 73 | /** 74 | * A custom Jasmine equality tester 75 | */ 76 | asymmetricMatch(value: mixed): boolean 77 | }; 78 | 79 | type JestCallsType = { 80 | allArgs(): mixed, 81 | all(): mixed, 82 | any(): boolean, 83 | count(): number, 84 | first(): mixed, 85 | mostRecent(): mixed, 86 | reset(): void 87 | }; 88 | 89 | type JestClockType = { 90 | install(): void, 91 | mockDate(date: Date): void, 92 | tick(milliseconds?: number): void, 93 | uninstall(): void 94 | }; 95 | 96 | type JestMatcherResult = { 97 | message?: string | (() => string), 98 | pass: boolean 99 | }; 100 | 101 | type JestMatcher = (actual: any, expected: any) => JestMatcherResult; 102 | 103 | type JestPromiseType = { 104 | /** 105 | * Use rejects to unwrap the reason of a rejected promise so any other 106 | * matcher can be chained. If the promise is fulfilled the assertion fails. 107 | */ 108 | rejects: JestExpectType, 109 | /** 110 | * Use resolves to unwrap the value of a fulfilled promise so any other 111 | * matcher can be chained. If the promise is rejected the assertion fails. 112 | */ 113 | resolves: JestExpectType 114 | }; 115 | 116 | /** 117 | * Plugin: jest-enzyme 118 | */ 119 | type EnzymeMatchersType = { 120 | toBeChecked(): void, 121 | toBeDisabled(): void, 122 | toBeEmpty(): void, 123 | toBePresent(): void, 124 | toContainReact(element: React$Element): void, 125 | toHaveClassName(className: string): void, 126 | toHaveHTML(html: string): void, 127 | toHaveProp(propKey: string, propValue?: any): void, 128 | toHaveRef(refName: string): void, 129 | toHaveState(stateKey: string, stateValue?: any): void, 130 | toHaveStyle(styleKey: string, styleValue?: any): void, 131 | toHaveTagName(tagName: string): void, 132 | toHaveText(text: string): void, 133 | toIncludeText(text: string): void, 134 | toHaveValue(value: any): void, 135 | toMatchElement(element: React$Element): void, 136 | toMatchSelector(selector: string): void 137 | }; 138 | 139 | type JestExpectType = { 140 | not: JestExpectType & EnzymeMatchersType, 141 | /** 142 | * If you have a mock function, you can use .lastCalledWith to test what 143 | * arguments it was last called with. 144 | */ 145 | lastCalledWith(...args: Array): void, 146 | /** 147 | * toBe just checks that a value is what you expect. It uses === to check 148 | * strict equality. 149 | */ 150 | toBe(value: any): void, 151 | /** 152 | * Use .toHaveBeenCalled to ensure that a mock function got called. 153 | */ 154 | toBeCalled(): void, 155 | /** 156 | * Use .toBeCalledWith to ensure that a mock function was called with 157 | * specific arguments. 158 | */ 159 | toBeCalledWith(...args: Array): void, 160 | /** 161 | * Using exact equality with floating point numbers is a bad idea. Rounding 162 | * means that intuitive things fail. 163 | */ 164 | toBeCloseTo(num: number, delta: any): void, 165 | /** 166 | * Use .toBeDefined to check that a variable is not undefined. 167 | */ 168 | toBeDefined(): void, 169 | /** 170 | * Use .toBeFalsy when you don't care what a value is, you just want to 171 | * ensure a value is false in a boolean context. 172 | */ 173 | toBeFalsy(): void, 174 | /** 175 | * To compare floating point numbers, you can use toBeGreaterThan. 176 | */ 177 | toBeGreaterThan(number: number): void, 178 | /** 179 | * To compare floating point numbers, you can use toBeGreaterThanOrEqual. 180 | */ 181 | toBeGreaterThanOrEqual(number: number): void, 182 | /** 183 | * To compare floating point numbers, you can use toBeLessThan. 184 | */ 185 | toBeLessThan(number: number): void, 186 | /** 187 | * To compare floating point numbers, you can use toBeLessThanOrEqual. 188 | */ 189 | toBeLessThanOrEqual(number: number): void, 190 | /** 191 | * Use .toBeInstanceOf(Class) to check that an object is an instance of a 192 | * class. 193 | */ 194 | toBeInstanceOf(cls: Class<*>): void, 195 | /** 196 | * .toBeNull() is the same as .toBe(null) but the error messages are a bit 197 | * nicer. 198 | */ 199 | toBeNull(): void, 200 | /** 201 | * Use .toBeTruthy when you don't care what a value is, you just want to 202 | * ensure a value is true in a boolean context. 203 | */ 204 | toBeTruthy(): void, 205 | /** 206 | * Use .toBeUndefined to check that a variable is undefined. 207 | */ 208 | toBeUndefined(): void, 209 | /** 210 | * Use .toContain when you want to check that an item is in a list. For 211 | * testing the items in the list, this uses ===, a strict equality check. 212 | */ 213 | toContain(item: any): void, 214 | /** 215 | * Use .toContainEqual when you want to check that an item is in a list. For 216 | * 217 | * 218 | * 219 | * ing the items in the list, this matcher recursively checks the 220 | * equality of all fields, rather than checking for object identity. 221 | */ 222 | toContainEqual(item: any): void, 223 | /** 224 | * Use .toEqual when you want to check that two objects have the same value. 225 | * This matcher recursively checks the equality of all fields, rather than 226 | * checking for object identity. 227 | */ 228 | toEqual(value: any): void, 229 | /** 230 | * Use .toHaveBeenCalled to ensure that a mock function got called. 231 | */ 232 | toHaveBeenCalled(): void, 233 | /** 234 | * Use .toHaveBeenCalledTimes to ensure that a mock function got called exact 235 | * number of times. 236 | */ 237 | toHaveBeenCalledTimes(number: number): void, 238 | /** 239 | * Use .toHaveBeenCalledWith to ensure that a mock function was called with 240 | * specific arguments. 241 | */ 242 | toHaveBeenCalledWith(...args: Array): void, 243 | /** 244 | * Use .toHaveBeenLastCalledWith to ensure that a mock function was last called 245 | * with specific arguments. 246 | */ 247 | toHaveBeenLastCalledWith(...args: Array): void, 248 | /** 249 | * Check that an object has a .length property and it is set to a certain 250 | * numeric value. 251 | */ 252 | toHaveLength(number: number): void, 253 | /** 254 | * 255 | */ 256 | toHaveProperty(propPath: string, value?: any): void, 257 | /** 258 | * Use .toMatch to check that a string matches a regular expression or string. 259 | */ 260 | toMatch(regexpOrString: RegExp | string): void, 261 | /** 262 | * Use .toMatchObject to check that a javascript object matches a subset of the properties of an object. 263 | */ 264 | toMatchObject(object: Object | Array): void, 265 | /** 266 | * This ensures that a React component matches the most recent snapshot. 267 | */ 268 | toMatchSnapshot(name?: string): void, 269 | /** 270 | * Use .toThrow to test that a function throws when it is called. 271 | * If you want to test that a specific error gets thrown, you can provide an 272 | * argument to toThrow. The argument can be a string for the error message, 273 | * a class for the error, or a regex that should match the error. 274 | * 275 | * Alias: .toThrowError 276 | */ 277 | toThrow(message?: string | Error | Class | RegExp): void, 278 | toThrowError(message?: string | Error | Class | RegExp): void, 279 | /** 280 | * Use .toThrowErrorMatchingSnapshot to test that a function throws a error 281 | * matching the most recent snapshot when it is called. 282 | */ 283 | toThrowErrorMatchingSnapshot(): void 284 | }; 285 | 286 | type JestObjectType = { 287 | /** 288 | * Disables automatic mocking in the module loader. 289 | * 290 | * After this method is called, all `require()`s will return the real 291 | * versions of each module (rather than a mocked version). 292 | */ 293 | disableAutomock(): JestObjectType, 294 | /** 295 | * An un-hoisted version of disableAutomock 296 | */ 297 | autoMockOff(): JestObjectType, 298 | /** 299 | * Enables automatic mocking in the module loader. 300 | */ 301 | enableAutomock(): JestObjectType, 302 | /** 303 | * An un-hoisted version of enableAutomock 304 | */ 305 | autoMockOn(): JestObjectType, 306 | /** 307 | * Clears the mock.calls and mock.instances properties of all mocks. 308 | * Equivalent to calling .mockClear() on every mocked function. 309 | */ 310 | clearAllMocks(): JestObjectType, 311 | /** 312 | * Resets the state of all mocks. Equivalent to calling .mockReset() on every 313 | * mocked function. 314 | */ 315 | resetAllMocks(): JestObjectType, 316 | /** 317 | * Restores all mocks back to their original value. 318 | */ 319 | restoreAllMocks(): JestObjectType, 320 | /** 321 | * Removes any pending timers from the timer system. 322 | */ 323 | clearAllTimers(): void, 324 | /** 325 | * The same as `mock` but not moved to the top of the expectation by 326 | * babel-jest. 327 | */ 328 | doMock(moduleName: string, moduleFactory?: any): JestObjectType, 329 | /** 330 | * The same as `unmock` but not moved to the top of the expectation by 331 | * babel-jest. 332 | */ 333 | dontMock(moduleName: string): JestObjectType, 334 | /** 335 | * Returns a new, unused mock function. Optionally takes a mock 336 | * implementation. 337 | */ 338 | fn, TReturn>( 339 | implementation?: (...args: TArguments) => TReturn 340 | ): JestMockFn, 341 | /** 342 | * Determines if the given function is a mocked function. 343 | */ 344 | isMockFunction(fn: Function): boolean, 345 | /** 346 | * Given the name of a module, use the automatic mocking system to generate a 347 | * mocked version of the module for you. 348 | */ 349 | genMockFromModule(moduleName: string): any, 350 | /** 351 | * Mocks a module with an auto-mocked version when it is being required. 352 | * 353 | * The second argument can be used to specify an explicit module factory that 354 | * is being run instead of using Jest's automocking feature. 355 | * 356 | * The third argument can be used to create virtual mocks -- mocks of modules 357 | * that don't exist anywhere in the system. 358 | */ 359 | mock( 360 | moduleName: string, 361 | moduleFactory?: any, 362 | options?: Object 363 | ): JestObjectType, 364 | /** 365 | * Returns the actual module instead of a mock, bypassing all checks on 366 | * whether the module should receive a mock implementation or not. 367 | */ 368 | requireActual(moduleName: string): any, 369 | /** 370 | * Returns a mock module instead of the actual module, bypassing all checks 371 | * on whether the module should be required normally or not. 372 | */ 373 | requireMock(moduleName: string): any, 374 | /** 375 | * Resets the module registry - the cache of all required modules. This is 376 | * useful to isolate modules where local state might conflict between tests. 377 | */ 378 | resetModules(): JestObjectType, 379 | /** 380 | * Exhausts the micro-task queue (usually interfaced in node via 381 | * process.nextTick). 382 | */ 383 | runAllTicks(): void, 384 | /** 385 | * Exhausts the macro-task queue (i.e., all tasks queued by setTimeout(), 386 | * setInterval(), and setImmediate()). 387 | */ 388 | runAllTimers(): void, 389 | /** 390 | * Exhausts all tasks queued by setImmediate(). 391 | */ 392 | runAllImmediates(): void, 393 | /** 394 | * Executes only the macro task queue (i.e. all tasks queued by setTimeout() 395 | * or setInterval() and setImmediate()). 396 | */ 397 | runTimersToTime(msToRun: number): void, 398 | /** 399 | * Executes only the macro-tasks that are currently pending (i.e., only the 400 | * tasks that have been queued by setTimeout() or setInterval() up to this 401 | * point) 402 | */ 403 | runOnlyPendingTimers(): void, 404 | /** 405 | * Explicitly supplies the mock object that the module system should return 406 | * for the specified module. Note: It is recommended to use jest.mock() 407 | * instead. 408 | */ 409 | setMock(moduleName: string, moduleExports: any): JestObjectType, 410 | /** 411 | * Indicates that the module system should never return a mocked version of 412 | * the specified module from require() (e.g. that it should always return the 413 | * real module). 414 | */ 415 | unmock(moduleName: string): JestObjectType, 416 | /** 417 | * Instructs Jest to use fake versions of the standard timer functions 418 | * (setTimeout, setInterval, clearTimeout, clearInterval, nextTick, 419 | * setImmediate and clearImmediate). 420 | */ 421 | useFakeTimers(): JestObjectType, 422 | /** 423 | * Instructs Jest to use the real versions of the standard timer functions. 424 | */ 425 | useRealTimers(): JestObjectType, 426 | /** 427 | * Creates a mock function similar to jest.fn but also tracks calls to 428 | * object[methodName]. 429 | */ 430 | spyOn(object: Object, methodName: string): JestMockFn, 431 | /** 432 | * Set the default timeout interval for tests and before/after hooks in milliseconds. 433 | * Note: The default timeout interval is 5 seconds if this method is not called. 434 | */ 435 | setTimeout(timeout: number): JestObjectType 436 | }; 437 | 438 | type JestSpyType = { 439 | calls: JestCallsType 440 | }; 441 | 442 | /** Runs this function after every test inside this context */ 443 | declare function afterEach( 444 | fn: (done: () => void) => ?Promise, 445 | timeout?: number 446 | ): void; 447 | /** Runs this function before every test inside this context */ 448 | declare function beforeEach( 449 | fn: (done: () => void) => ?Promise, 450 | timeout?: number 451 | ): void; 452 | /** Runs this function after all tests have finished inside this context */ 453 | declare function afterAll( 454 | fn: (done: () => void) => ?Promise, 455 | timeout?: number 456 | ): void; 457 | /** Runs this function before any tests have started inside this context */ 458 | declare function beforeAll( 459 | fn: (done: () => void) => ?Promise, 460 | timeout?: number 461 | ): void; 462 | 463 | /** A context for grouping tests together */ 464 | declare var describe: { 465 | /** 466 | * Creates a block that groups together several related tests in one "test suite" 467 | */ 468 | (name: string, fn: () => void): void, 469 | 470 | /** 471 | * Only run this describe block 472 | */ 473 | only(name: string, fn: () => void): void, 474 | 475 | /** 476 | * Skip running this describe block 477 | */ 478 | skip(name: string, fn: () => void): void 479 | }; 480 | 481 | /** An individual test unit */ 482 | declare var it: { 483 | /** 484 | * An individual test unit 485 | * 486 | * @param {string} Name of Test 487 | * @param {Function} Test 488 | * @param {number} Timeout for the test, in milliseconds. 489 | */ 490 | ( 491 | name: string, 492 | fn?: (done: () => void) => ?Promise, 493 | timeout?: number 494 | ): void, 495 | /** 496 | * Only run this test 497 | * 498 | * @param {string} Name of Test 499 | * @param {Function} Test 500 | * @param {number} Timeout for the test, in milliseconds. 501 | */ 502 | only( 503 | name: string, 504 | fn?: (done: () => void) => ?Promise, 505 | timeout?: number 506 | ): void, 507 | /** 508 | * Skip running this test 509 | * 510 | * @param {string} Name of Test 511 | * @param {Function} Test 512 | * @param {number} Timeout for the test, in milliseconds. 513 | */ 514 | skip( 515 | name: string, 516 | fn?: (done: () => void) => ?Promise, 517 | timeout?: number 518 | ): void, 519 | /** 520 | * Run the test concurrently 521 | * 522 | * @param {string} Name of Test 523 | * @param {Function} Test 524 | * @param {number} Timeout for the test, in milliseconds. 525 | */ 526 | concurrent( 527 | name: string, 528 | fn?: (done: () => void) => ?Promise, 529 | timeout?: number 530 | ): void 531 | }; 532 | declare function fit( 533 | name: string, 534 | fn: (done: () => void) => ?Promise, 535 | timeout?: number 536 | ): void; 537 | /** An individual test unit */ 538 | declare var test: typeof it; 539 | /** A disabled group of tests */ 540 | declare var xdescribe: typeof describe; 541 | /** A focused group of tests */ 542 | declare var fdescribe: typeof describe; 543 | /** A disabled individual test */ 544 | declare var xit: typeof it; 545 | /** A disabled individual test */ 546 | declare var xtest: typeof it; 547 | 548 | /** The expect function is used every time you want to test a value */ 549 | declare var expect: { 550 | /** The object that you want to make assertions against */ 551 | (value: any): JestExpectType & JestPromiseType & EnzymeMatchersType, 552 | /** Add additional Jasmine matchers to Jest's roster */ 553 | extend(matchers: { [name: string]: JestMatcher }): void, 554 | /** Add a module that formats application-specific data structures. */ 555 | addSnapshotSerializer(serializer: (input: Object) => string): void, 556 | assertions(expectedAssertions: number): void, 557 | hasAssertions(): void, 558 | any(value: mixed): JestAsymmetricEqualityType, 559 | anything(): void, 560 | arrayContaining(value: Array): void, 561 | objectContaining(value: Object): void, 562 | /** Matches any received string that contains the exact expected string. */ 563 | stringContaining(value: string): void, 564 | stringMatching(value: string | RegExp): void 565 | }; 566 | 567 | // TODO handle return type 568 | // http://jasmine.github.io/2.4/introduction.html#section-Spies 569 | declare function spyOn(value: mixed, method: string): Object; 570 | 571 | /** Holds all functions related to manipulating test runner */ 572 | declare var jest: JestObjectType; 573 | 574 | /** 575 | * The global Jasmine object, this is generally not exposed as the public API, 576 | * using features inside here could break in later versions of Jest. 577 | */ 578 | declare var jasmine: { 579 | DEFAULT_TIMEOUT_INTERVAL: number, 580 | any(value: mixed): JestAsymmetricEqualityType, 581 | anything(): void, 582 | arrayContaining(value: Array): void, 583 | clock(): JestClockType, 584 | createSpy(name: string): JestSpyType, 585 | createSpyObj( 586 | baseName: string, 587 | methodNames: Array 588 | ): { [methodName: string]: JestSpyType }, 589 | objectContaining(value: Object): void, 590 | stringMatching(value: string): void 591 | }; 592 | --------------------------------------------------------------------------------