From 4922d9cbc3955ee79a7c0f94be232f951c3e8047 Mon Sep 17 00:00:00 2001 From: Erik Rasmussen Date: Mon, 26 May 2025 09:49:26 +0200 Subject: [PATCH 1/2] Convert to TypeScript --- .babelrc | 3 +- eslint.config.mjs | 44 ++++ package-scripts.js | 26 +-- package.json | 104 +++++---- rollup.config.js => rollup.config.mjs | 26 ++- src/{concat.test.js => concat.test.ts} | 24 +- src/{concat.js => concat.ts} | 9 +- src/{copyField.js => copyField.ts} | 11 +- src/index.d.test.ts | 4 +- src/index.js | 28 --- src/index.js.flow | 21 -- src/{index.d.ts => index.ts} | 57 +++-- src/{insert.test.js => insert.test.ts} | 66 +++--- src/{insert.js => insert.ts} | 11 +- src/{move.test.js => move.test.ts} | 158 ++++++------- src/{move.js => move.ts} | 19 +- src/{pop.test.js => pop.test.ts} | 102 ++++----- src/{pop.js => pop.ts} | 9 +- src/{push.test.js => push.test.ts} | 12 +- src/{push.js => push.ts} | 9 +- src/{remove.test.js => remove.test.ts} | 192 ++++++++-------- src/{remove.js => remove.ts} | 15 +- ...emoveBatch.test.js => removeBatch.test.ts} | 212 +++++++++--------- src/{removeBatch.js => removeBatch.ts} | 20 +- src/shift.js | 11 - src/{shift.test.js => shift.test.ts} | 44 ++-- src/shift.ts | 10 + src/{swap.test.js => swap.test.ts} | 42 ++-- src/{swap.js => swap.ts} | 11 +- src/testUtils.test.ts | 37 +++ src/testUtils.ts | 19 ++ src/unshift.js | 11 - src/{unshift.test.js => unshift.test.ts} | 48 ++-- src/unshift.ts | 10 + src/{update.test.js => update.test.ts} | 24 +- src/{update.js => update.ts} | 9 +- src/{utils.js => utils.ts} | 4 +- tsconfig.json | 26 ++- 38 files changed, 812 insertions(+), 676 deletions(-) create mode 100644 eslint.config.mjs rename rollup.config.js => rollup.config.mjs (83%) rename src/{concat.test.js => concat.test.ts} (68%) rename src/{concat.js => concat.ts} (50%) rename src/{copyField.js => copyField.ts} (80%) delete mode 100644 src/index.js delete mode 100644 src/index.js.flow rename src/{index.d.ts => index.ts} (62%) rename src/{insert.test.js => insert.test.ts} (85%) rename src/{insert.js => insert.ts} (83%) rename src/{move.test.js => move.test.ts} (86%) rename src/{move.js => move.ts} (83%) rename src/{pop.test.js => pop.test.ts} (80%) rename src/{pop.js => pop.ts} (69%) rename src/{push.test.js => push.test.ts} (74%) rename src/{push.js => push.ts} (50%) rename src/{remove.test.js => remove.test.ts} (76%) rename src/{remove.js => remove.ts} (85%) rename src/{removeBatch.test.js => removeBatch.test.ts} (77%) rename src/{removeBatch.js => removeBatch.ts} (86%) delete mode 100644 src/shift.js rename src/{shift.test.js => shift.test.ts} (83%) create mode 100644 src/shift.ts rename src/{swap.test.js => swap.test.ts} (89%) rename src/{swap.js => swap.ts} (85%) create mode 100644 src/testUtils.test.ts create mode 100644 src/testUtils.ts delete mode 100644 src/unshift.js rename src/{unshift.test.js => unshift.test.ts} (83%) create mode 100644 src/unshift.ts rename src/{update.test.js => update.test.ts} (68%) rename src/{update.js => update.ts} (57%) rename src/{utils.js => utils.ts} (88%) diff --git a/.babelrc b/.babelrc index cf2fb46..b1e238d 100644 --- a/.babelrc +++ b/.babelrc @@ -9,10 +9,9 @@ } } ], - "@babel/preset-flow" + "@babel/preset-typescript" ], "plugins": [ - "@babel/plugin-transform-flow-strip-types", "@babel/plugin-syntax-dynamic-import", "@babel/plugin-syntax-import-meta", "@babel/plugin-proposal-class-properties", diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..6745f1e --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,44 @@ +import js from '@eslint/js' +import tsPlugin from '@typescript-eslint/eslint-plugin' +import tsParser from '@typescript-eslint/parser' +import globals from 'globals' + +export default [ + js.configs.recommended, + { + files: ['**/*.{js,jsx,ts,tsx,mjs}'], + languageOptions: { + parser: tsParser, + ecmaVersion: 2020, + sourceType: 'module', + globals: { + ...globals.browser, + ...globals.node, + ...globals.jest + } + }, + plugins: { + '@typescript-eslint': tsPlugin + }, + rules: { + ...tsPlugin.configs.recommended.rules, + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-unused-vars': [ + 'error', + { argsIgnorePattern: '^_' } + ], + 'no-unused-vars': 'off' + } + }, + { + ignores: [ + 'node_modules/**', + 'dist/**', + 'coverage/**', + '.nyc_output/**', + '*.config.js', + 'package-scripts.js', + 'src/index.d.test.ts' + ] + } +] diff --git a/package-scripts.js b/package-scripts.js index 0dd4285..92b74c2 100644 --- a/package-scripts.js +++ b/package-scripts.js @@ -34,7 +34,7 @@ module.exports = { ) ), es: { - description: 'run the build with rollup (uses rollup.config.js)', + description: 'run the build with rollup (uses rollup.config.mjs)', script: 'rollup --config --environment FORMAT:es' }, cjs: { @@ -57,37 +57,19 @@ module.exports = { description: 'Generates table of contents in README', script: 'doctoc README.md' }, - copyTypes: series( - npsUtils.copy('src/*.js.flow src/*.d.ts dist'), - npsUtils.copy( - 'dist/index.js.flow dist --rename="final-form-arrays.cjs.js.flow"' - ), - npsUtils.copy( - 'dist/index.js.flow dist --rename="final-form-arrays.es.js.flow"' - ) - ), + copyTypes: series('tsc --declaration --emitDeclarationOnly --outDir dist'), lint: { description: 'lint the entire project', script: 'eslint .' }, - flow: { - description: 'flow check the entire project', - script: 'flow check' - }, typescript: { description: 'typescript check the entire project', - script: 'tsc' + script: 'tsc --noEmit' }, validate: { description: 'This runs several scripts to make sure things look good before committing or on clean install', - default: concurrent.nps( - 'lint', - 'flow', - 'typescript', - 'build.andTest', - 'test' - ) + default: concurrent.nps('lint', 'typescript', 'build.andTest', 'test') } }, options: { diff --git a/package.json b/package.json index 3fe5ff7..54738cf 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,9 @@ "scripts": { "start": "nps", "test": "nps test", - "precommit": "lint-staged && npm start validate" + "precommit": "lint-staged && npm start validate", + "build:types": "tsc --declaration --emitDeclarationOnly --outDir dist", + "prebuild": "yarn build:types" }, "author": "Erik Rasmussen (http://github.com/erikras)", "license": "MIT", @@ -25,52 +27,55 @@ }, "homepage": "https://github.com/final-form/final-form-arrays#readme", "devDependencies": { - "@babel/core": "^7.5.4", - "@babel/plugin-external-helpers": "^7.0.0", - "@babel/plugin-proposal-class-properties": "^7.5.0", - "@babel/plugin-proposal-decorators": "^7.4.4", - "@babel/plugin-proposal-export-namespace-from": "^7.5.2", - "@babel/plugin-proposal-function-sent": "^7.5.0", - "@babel/plugin-proposal-json-strings": "^7.0.0", - "@babel/plugin-proposal-numeric-separator": "^7.0.0", - "@babel/plugin-proposal-throw-expressions": "^7.0.0", - "@babel/plugin-syntax-dynamic-import": "^7.0.0", - "@babel/plugin-syntax-import-meta": "^7.0.0", - "@babel/plugin-transform-flow-strip-types": "^7.4.4", - "@babel/plugin-transform-runtime": "^7.5.0", - "@babel/preset-env": "^7.5.4", - "@babel/preset-flow": "^7.0.0", + "@types/jest": "^29.5.14", + "@babel/core": "^7.27.1", + "@babel/plugin-external-helpers": "^7.27.1", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-decorators": "^7.27.1", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", + "@babel/plugin-proposal-function-sent": "^7.27.1", + "@babel/plugin-proposal-json-strings": "^7.18.6", + "@babel/plugin-proposal-numeric-separator": "^7.18.6", + "@babel/plugin-proposal-throw-expressions": "^7.27.1", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-transform-flow-strip-types": "^7.27.1", + "@babel/plugin-transform-runtime": "^7.27.1", + "@babel/preset-env": "^7.27.2", + "@babel/preset-typescript": "^7.27.1", "babel-core": "^7.0.0-bridge.0", - "babel-eslint": "^10.0.2", - "babel-jest": "^24.8.0", - "bundlesize": "^0.18.0", - "doctoc": "^1.3.0", - "eslint": "^6.0.1", - "eslint-config-react-app": "^3.0.6", - "eslint-plugin-babel": "^5.3.0", - "eslint-plugin-flowtype": "^3.2.1", - "eslint-plugin-import": "^2.16.0", - "eslint-plugin-jsx-a11y": "^6.2.1", - "eslint-plugin-react": "^7.13.0", - "final-form": "^4.20.8", - "flow-bin": "^0.102.0", + "babel-eslint": "^10.1.0", + "babel-jest": "^29.7.0", + "bundlesize": "^0.18.2", + "doctoc": "^2.2.1", + "@eslint/js": "^9.27.0", + "@typescript-eslint/eslint-plugin": "^8.32.1", + "@typescript-eslint/parser": "^8.32.1", + "eslint": "^9.27.0", + "globals": "^16.2.0", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jsx-a11y": "^6.10.2", + "eslint-plugin-react": "^7.37.5", + "final-form": "^5.0.0-3", "glow": "^1.2.2", - "husky": "^3.0.0", - "jest": "^24.8.0", - "lint-staged": "^9.2.0", - "nps": "^5.9.5", - "nps-utils": "^1.5.0", - "prettier": "^1.18.2", - "prettier-eslint-cli": "^5.0.0", - "react": "^16.8.6", - "rollup": "^1.16.7", - "rollup-plugin-babel": "^4.3.3", - "rollup-plugin-commonjs": "^10.0.1", + "husky": "^9.1.7", + "jest": "^29.7.0", + "lint-staged": "^16.0.0", + "nps": "^5.10.0", + "nps-utils": "^1.7.0", + "prettier": "^3.5.3", + "prettier-eslint-cli": "^8.0.1", + "react": "^19.1.0", + "rollup": "^4.41.1", + "rollup-plugin-babel": "^4.4.0", + "rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-flow": "^1.1.1", "rollup-plugin-node-resolve": "^5.2.0", "rollup-plugin-replace": "^2.2.0", - "rollup-plugin-uglify": "^6.0.2", - "typescript": "^3.5.3" + "rollup-plugin-uglify": "^6.0.4", + "rollup-plugin-typescript2": "^0.36.0", + "typescript": "^5.8.3", + "ts-jest": "^29.2.5" }, "peerDependencies": { "final-form": "^4.20.8" @@ -82,10 +87,21 @@ ] }, "jest": { + "preset": "ts-jest", "testEnvironment": "node", "testPathIgnorePatterns": [ - ".*\\.ts" - ] + "/node_modules/", + "src/index.d.test.ts" + ], + "moduleFileExtensions": [ + "ts", + "tsx", + "js", + "jsx" + ], + "transform": { + "^.+\\.(ts|tsx)$": "ts-jest" + } }, "bundlesize": [ { diff --git a/rollup.config.js b/rollup.config.mjs similarity index 83% rename from rollup.config.js rename to rollup.config.mjs index 41ccabc..d4502eb 100644 --- a/rollup.config.js +++ b/rollup.config.mjs @@ -1,9 +1,10 @@ import resolve from 'rollup-plugin-node-resolve' import babel from 'rollup-plugin-babel' -import flow from 'rollup-plugin-flow' +import typescript from 'rollup-plugin-typescript2' import commonjs from 'rollup-plugin-commonjs' import { uglify } from 'rollup-plugin-uglify' import replace from 'rollup-plugin-replace' +import ts from 'typescript' const minify = process.env.MINIFY const format = process.env.FORMAT @@ -33,7 +34,7 @@ if (es) { } export default { - input: 'src/index.js', + input: 'src/index.ts', output: Object.assign( { name: 'final-form-arrays', @@ -43,8 +44,21 @@ export default { ), external: [], plugins: [ - resolve({ jsnext: true, main: true }), - flow(), + resolve({ + mainFields: ['module', 'jsnext:main', 'main'], + browser: true, + preferBuiltins: false + }), + typescript({ + typescript: ts, + clean: true, + tsconfigOverride: { + compilerOptions: { + declaration: false, + declarationMap: false + } + } + }), commonjs({ include: 'node_modules/**' }), babel({ exclude: 'node_modules/**', @@ -57,12 +71,10 @@ export default { modules: false, loose: true } - ], - '@babel/preset-flow' + ] ], plugins: [ ['@babel/plugin-transform-runtime', { useESModules: !cjs }], - '@babel/plugin-transform-flow-strip-types', '@babel/plugin-syntax-dynamic-import', '@babel/plugin-syntax-import-meta', '@babel/plugin-proposal-class-properties', diff --git a/src/concat.test.js b/src/concat.test.ts similarity index 68% rename from src/concat.test.js rename to src/concat.test.ts index 1705d06..9a20349 100644 --- a/src/concat.test.js +++ b/src/concat.test.ts @@ -1,16 +1,30 @@ import concat from './concat' +import { MutableState, Tools } from 'final-form' describe('concat', () => { - const getOp = value => { + const getOp = (value: any) => { const changeValue = jest.fn() - concat(['foo', value], {}, { changeValue }) + const mockState: MutableState = { + fieldSubscribers: {}, + fields: {}, + formState: { + values: {} + } + } as any + concat(['foo', value], mockState, { changeValue } as unknown as Tools) return changeValue.mock.calls[0][2] } it('should call changeValue once', () => { const changeValue = jest.fn() - const state = {} - const result = concat(['foo', ['bar', 'baz']], state, { changeValue }) + const state: MutableState = { + fieldSubscribers: {}, + fields: {}, + formState: { + values: {} + } + } as any + const result = concat(['foo', ['bar', 'baz']], state, { changeValue } as unknown as Tools) expect(result).toBeUndefined() expect(changeValue).toHaveBeenCalled() expect(changeValue).toHaveBeenCalledTimes(1) @@ -32,4 +46,4 @@ describe('concat', () => { expect(Array.isArray(result)).toBe(true) expect(result).toEqual(['a', 'b', 'c', 'd', 'e']) }) -}) +}) \ No newline at end of file diff --git a/src/concat.js b/src/concat.ts similarity index 50% rename from src/concat.js rename to src/concat.ts index f303feb..e9a0c42 100644 --- a/src/concat.js +++ b/src/concat.ts @@ -1,14 +1,13 @@ -// @flow -import type { MutableState, Mutator, Tools } from 'final-form' +import { MutableState, Mutator, Tools } from 'final-form' const concat: Mutator = ( [name, value]: any[], state: MutableState, { changeValue }: Tools -) => { - changeValue(state, name, (array: ?(any[])): any[] => +): void => { + changeValue(state, name, (array?: any[]): any[] => array ? [...array, ...value] : value ) } -export default concat +export default concat \ No newline at end of file diff --git a/src/copyField.js b/src/copyField.ts similarity index 80% rename from src/copyField.js rename to src/copyField.ts index 33d3bb1..d20a490 100644 --- a/src/copyField.js +++ b/src/copyField.ts @@ -1,12 +1,9 @@ -// @flow -import type { InternalFieldState } from 'final-form/dist/types' - function copyField( - oldFields: { [string]: InternalFieldState }, + oldFields: { [key: string]: any }, oldKey: string, - newFields: { [string]: InternalFieldState }, + newFields: { [key: string]: any }, newKey: string -) { +): void { newFields[newKey] = { ...oldFields[oldKey], name: newKey, @@ -32,4 +29,4 @@ function copyField( } } -export default copyField +export default copyField \ No newline at end of file diff --git a/src/index.d.test.ts b/src/index.d.test.ts index ea48168..8b2c810 100644 --- a/src/index.d.test.ts +++ b/src/index.d.test.ts @@ -1,10 +1,10 @@ // tslint:disable no-console -import { Config, createForm, AnyObject } from 'final-form' +import { Config, createForm } from 'final-form' import arrayMutators from './index' import { Mutators } from './index' -const onSubmit: Config['onSubmit'] = (values, callback) => {} +const onSubmit: Config['onSubmit'] = (_values, _callback) => { } const form = createForm({ mutators: { ...arrayMutators }, diff --git a/src/index.js b/src/index.js deleted file mode 100644 index bb419a5..0000000 --- a/src/index.js +++ /dev/null @@ -1,28 +0,0 @@ -// @flow -import type { Mutator } from 'final-form' -import insert from './insert' -import concat from './concat' -import move from './move' -import pop from './pop' -import push from './push' -import remove from './remove' -import removeBatch from './removeBatch' -import shift from './shift' -import swap from './swap' -import unshift from './unshift' -import update from './update' - -const mutators: { [string]: Mutator } = { - insert, - concat, - move, - pop, - push, - remove, - removeBatch, - shift, - swap, - unshift, - update -} -export default mutators diff --git a/src/index.js.flow b/src/index.js.flow deleted file mode 100644 index 285bcf6..0000000 --- a/src/index.js.flow +++ /dev/null @@ -1,21 +0,0 @@ -// @flow -import type { Mutator } from 'final-form' - -type DefaultType = { [string]: Mutator } - -declare export default DefaultType - -/** The shape of the mutators once final-form has bound them to state */ -export type Mutators = { - insert: (name: string, index: number, value: any) => void, - concat: (name: string, value: Array) => void, - move: (name: string, from: number, to: number) => void, - pop: (name: string) => any, - push: (name: string, value: any) => void, - remove: (name: string, index: number) => any, - removeBatch: (name: string, indexes: Array) => any, - shift: (name: string) => any, - swap: (name: string, indexA: number, indexB: number) => void, - update: (name: string, index: number, value: any) => void, - unshift: (name: string, value: any) => void -} diff --git a/src/index.d.ts b/src/index.ts similarity index 62% rename from src/index.d.ts rename to src/index.ts index 2b8fd37..de99e4b 100644 --- a/src/index.d.ts +++ b/src/index.ts @@ -1,16 +1,15 @@ import { Mutator } from 'final-form' - -export const insert: Mutator -export const concat: Mutator -export const move: Mutator -export const pop: Mutator -export const push: Mutator -export const removeBatch: Mutator -export const remove: Mutator -export const shift: Mutator -export const swap: Mutator -export const update: Mutator -export const unshift: Mutator +import insert from './insert' +import concat from './concat' +import move from './move' +import pop from './pop' +import push from './push' +import remove from './remove' +import removeBatch from './removeBatch' +import shift from './shift' +import swap from './swap' +import unshift from './unshift' +import update from './update' export interface DefaultType { insert: Mutator @@ -26,8 +25,36 @@ export interface DefaultType { unshift: Mutator } -declare const d: DefaultType -export default d +const mutators: DefaultType = { + insert, + concat, + move, + pop, + push, + remove, + removeBatch, + shift, + swap, + unshift, + update +} + +export default mutators + +// Export individual mutators +export { + insert, + concat, + move, + pop, + push, + remove, + removeBatch, + shift, + swap, + unshift, + update +} /** The shape of the mutators once final-form has bound them to state */ export interface Mutators { @@ -42,4 +69,4 @@ export interface Mutators { swap: (name: string, indexA: number, indexB: number) => void update: (name: string, index: number, value: any) => void unshift: (name: string, value: any) => void -} +} \ No newline at end of file diff --git a/src/insert.test.js b/src/insert.test.ts similarity index 85% rename from src/insert.test.js rename to src/insert.test.ts index 41663fd..5bae632 100644 --- a/src/insert.test.js +++ b/src/insert.test.ts @@ -1,49 +1,49 @@ import insert from './insert' -import { getIn, setIn } from 'final-form' +import { getIn, setIn, MutableState, Tools } from 'final-form' describe('insert', () => { - const getOp = (index, value) => { + const getOp = (index, value: any) => { const changeValue = jest.fn() const resetFieldState = jest.fn() - const state = { + const state: MutableState = { formState: { values: { foo: ['one', 'two'] - } + } as any }, fields: { 'foo[0]': { name: 'foo[0]', touched: true, error: 'First Error' - }, + } as any, 'foo[1]': { name: 'foo[1]', touched: false, error: 'Second Error' - } + } as any } } - insert(['foo', index, value], state, { changeValue, resetFieldState }) + insert(['foo', index, value], state, { changeValue, resetFieldState } as unknown as Tools) return changeValue.mock.calls[0][2] } it('should call changeValue once', () => { const changeValue = jest.fn() const resetFieldState = jest.fn() - const state = { + const state: MutableState = { formState: { values: { foo: ['one', 'two'], anotherField: 42 - } + } as any }, fields: { 'foo[0]': { name: 'foo[0]', touched: true, error: 'First Error' - }, + } as any, 'foo[1]': { name: 'foo[1]', touched: false @@ -57,7 +57,7 @@ describe('insert', () => { const result = insert(['foo', 0, 'bar'], state, { changeValue, resetFieldState - }) + } as unknown as Tools) expect(result).toBeUndefined() expect(changeValue).toHaveBeenCalled() expect(changeValue).toHaveBeenCalledTimes(1) @@ -86,47 +86,47 @@ describe('insert', () => { it('should increment other field data from the specified index', () => { const array = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k'] // implementation of changeValue taken directly from Final Form - const changeValue = (state, name, mutate) => { + const changeValue = (state: any, name: string, mutate: (value: any) => any) => { const before = getIn(state.formState.values, name) const after = mutate(before) state.formState.values = setIn(state.formState.values, name, after) || {} } - const resetFieldState = name => { + const resetFieldState = (name: string) => { state.fields[name].touched = false } - const state = { + const state: MutableState = { formState: { values: { foo: array - } + } as any }, fields: { 'foo[0]': { name: 'foo[0]', touched: true, error: 'A Error' - }, + } as any, 'foo[1]': { name: 'foo[1]', touched: true, error: 'B Error' - }, + } as any, 'foo[9]': { name: 'foo[9]', touched: true, error: 'J Error' - }, + } as any, 'foo[10]': { name: 'foo[10]', touched: false, error: 'K Error' - } + } as any } } const returnValue = insert(['foo', 1, 'NEWVALUE'], state, { changeValue, resetFieldState - }) + } as unknown as Tools) expect(returnValue).toBeUndefined() expect(state.formState.values.foo).not.toBe(array) // copied expect(state).toEqual({ @@ -146,14 +146,14 @@ describe('insert', () => { 'j', 'k' ] - } + } as any }, fields: { 'foo[0]': { name: 'foo[0]', touched: true, error: 'A Error' - }, + } as any, 'foo[2]': { name: 'foo[2]', touched: true, @@ -179,61 +179,61 @@ describe('insert', () => { it('should increment other field data from the specified index (nested arrays)', () => { const array = ['a', 'b', 'c', 'd'] // implementation of changeValue taken directly from Final Form - const changeValue = (state, name, mutate) => { + const changeValue = (state: any, name: string, mutate: (value: any) => any) => { const before = getIn(state.formState.values, name) const after = mutate(before) state.formState.values = setIn(state.formState.values, name, after) || {} } - const resetFieldState = name => { + const resetFieldState = (name: string) => { state.fields[name].touched = false } - const state = { + const state: MutableState = { formState: { values: { foo: [array] - } + } as any }, fields: { 'foo[0][0]': { name: 'foo[0][0]', touched: true, error: 'A Error' - }, + } as any, 'foo[0][1]': { name: 'foo[0][1]', touched: true, error: 'B Error' - }, + } as any, 'foo[0][2]': { name: 'foo[0][2]', touched: true, error: 'C Error' - }, + } as any, 'foo[0][3]': { name: 'foo[0][3]', touched: false, error: 'D Error' - } + } as any } } const returnValue = insert(['foo[0]', 1, 'NEWVALUE'], state, { changeValue, resetFieldState - }) + } as unknown as Tools) expect(returnValue).toBeUndefined() expect(state.formState.values.foo).not.toBe(array) // copied expect(state).toEqual({ formState: { values: { foo: [['a', 'NEWVALUE', 'b', 'c', 'd']] - } + } as any }, fields: { 'foo[0][0]': { name: 'foo[0][0]', touched: true, error: 'A Error' - }, + } as any, 'foo[0][2]': { name: 'foo[0][2]', touched: true, diff --git a/src/insert.js b/src/insert.ts similarity index 83% rename from src/insert.js rename to src/insert.ts index 89a0443..46464bf 100644 --- a/src/insert.js +++ b/src/insert.ts @@ -1,5 +1,4 @@ -// @flow -import type { MutableState, Mutator, Tools } from 'final-form' +import { MutableState, Mutator, Tools } from 'final-form' import copyField from './copyField' import { escapeRegexTokens } from './utils' @@ -7,8 +6,8 @@ const insert: Mutator = ( [name, index, value]: any[], state: MutableState, { changeValue }: Tools -) => { - changeValue(state, name, (array: ?(any[])): any[] => { +): void => { + changeValue(state, name, (array?: any[]): any[] => { const copy = [...(array || [])] copy.splice(index, 0, value) return copy @@ -16,7 +15,7 @@ const insert: Mutator = ( // now we have increment any higher indexes const pattern = new RegExp(`^${escapeRegexTokens(name)}\\[(\\d+)\\](.*)`) - const newFields = {} + const newFields: { [key: string]: any } = {} Object.keys(state.fields).forEach(key => { const tokens = pattern.exec(key) if (tokens) { @@ -37,4 +36,4 @@ const insert: Mutator = ( state.fields = newFields } -export default insert +export default insert \ No newline at end of file diff --git a/src/move.test.js b/src/move.test.ts similarity index 86% rename from src/move.test.js rename to src/move.test.ts index 91044c2..51db272 100644 --- a/src/move.test.js +++ b/src/move.test.ts @@ -1,24 +1,24 @@ import move from './move' -import { getIn, setIn } from 'final-form' +import { getIn, setIn, MutableState, Tools } from 'final-form' describe('move', () => { - const getOp = (from, to) => { + const getOp = (from, to: any) => { const changeValue = jest.fn() - move(['foo', from, to], { fields: {} }, { changeValue }) + move(['foo', from, to], { fields: {} }, { changeValue } as unknown as Tools) return changeValue.mock.calls[0][2] } it('should do nothing if from and to are equal', () => { const changeValue = jest.fn() - const result = move(['foo', 1, 1], { fields: {} }, { changeValue }) + const result = move(['foo', 1, 1], { fields: {} }, { changeValue } as unknown as Tools) expect(result).toBeUndefined() expect(changeValue).not.toHaveBeenCalled() }) it('should call changeValue once', () => { const changeValue = jest.fn() - const state = { fields: {} } - const result = move(['foo', 0, 2], state, { changeValue }) + const state: MutableState = { fields: {} } + const result = move(['foo', 0, 2], state, { changeValue } as unknown as Tools) expect(result).toBeUndefined() expect(changeValue).toHaveBeenCalled() expect(changeValue).toHaveBeenCalledTimes(1) @@ -47,16 +47,16 @@ describe('move', () => { it('should move field state from low index to high index', () => { // implementation of changeValue taken directly from Final Form - const changeValue = (state, name, mutate) => { + const changeValue = (state: any, name: string, mutate: (value: any) => any) => { const before = getIn(state.formState.values, name) const after = mutate(before) - state.formState.values = setIn(state.formState.values, name, after) || {} + state.formState.values = setIn(state.formState.values, name, after) || {} as any } - const state = { + const state: MutableState = { formState: { values: { foo: ['apple', 'banana', 'carrot', 'date'] - } + } as any }, fields: { 'foo[0]': { @@ -85,12 +85,12 @@ describe('move', () => { } } } - move(['foo', 0, 2], state, { changeValue }) + move(['foo', 0, 2], state, { changeValue } as unknown as Tools) expect(state).toEqual({ formState: { values: { foo: ['banana', 'carrot', 'apple', 'date'] - } + } as any }, fields: { 'foo[0]': { @@ -123,16 +123,16 @@ describe('move', () => { it('should move field state from high index to low index', () => { // implementation of changeValue taken directly from Final Form - const changeValue = (state, name, mutate) => { + const changeValue = (state: any, name: string, mutate: (value: any) => any) => { const before = getIn(state.formState.values, name) const after = mutate(before) - state.formState.values = setIn(state.formState.values, name, after) || {} + state.formState.values = setIn(state.formState.values, name, after) || {} as any } - const state = { + const state: MutableState = { formState: { values: { foo: ['apple', 'banana', 'carrot', 'date'] - } + } as any }, fields: { 'foo[0]': { @@ -161,12 +161,12 @@ describe('move', () => { } } } - move(['foo', 2, 0], state, { changeValue }) + move(['foo', 2, 0], state, { changeValue } as unknown as Tools) expect(state).toEqual({ formState: { values: { foo: ['carrot', 'apple', 'banana', 'date'] - } + } as any }, fields: { 'foo[0]': { @@ -199,12 +199,12 @@ describe('move', () => { it('should move deep field state from low index to high index', () => { // implementation of changeValue taken directly from Final Form - const changeValue = (state, name, mutate) => { + const changeValue = (state: any, name: string, mutate: (value: any) => any) => { const before = getIn(state.formState.values, name) const after = mutate(before) - state.formState.values = setIn(state.formState.values, name, after) || {} + state.formState.values = setIn(state.formState.values, name, after) || {} as any } - const state = { + const state: MutableState = { formState: { values: { foo: [ @@ -220,45 +220,45 @@ describe('move', () => { name: 'foo[0].dog', touched: true, error: 'Error A Dog' - }, + } as any, 'foo[0].cat': { name: 'foo[0].cat', touched: false, error: 'Error A Cat' - }, + } as any, 'foo[1].dog': { name: 'foo[1].dog', touched: true, error: 'Error B Dog' - }, + } as any, 'foo[1].cat': { name: 'foo[1].cat', touched: true, error: 'Error B Cat' - }, + } as any, 'foo[2].dog': { name: 'foo[2].dog', touched: true, error: 'Error C Dog' - }, + } as any, 'foo[2].cat': { name: 'foo[2].cat', touched: false, error: 'Error C Cat' - }, + } as any, 'foo[3].dog': { name: 'foo[3].dog', touched: false, error: 'Error D Dog' - }, + } as any, 'foo[3].cat': { name: 'foo[3].cat', touched: true, error: 'Error D Cat' - } + } as any } } - move(['foo', 0, 2], state, { changeValue }) + move(['foo', 0, 2], state, { changeValue } as unknown as Tools) expect(state).toMatchObject({ formState: { values: { @@ -281,17 +281,17 @@ describe('move', () => { name: 'foo[0].cat', touched: true, error: 'Error B Cat' - }, + } as any, 'foo[1].dog': { name: 'foo[1].dog', touched: true, error: 'Error C Dog' - }, + } as any, 'foo[1].cat': { name: 'foo[1].cat', touched: false, error: 'Error C Cat' - }, + } as any, 'foo[2].dog': { name: 'foo[2].dog', touched: true, @@ -308,24 +308,24 @@ describe('move', () => { name: 'foo[3].dog', touched: false, error: 'Error D Dog' - }, + } as any, 'foo[3].cat': { name: 'foo[3].cat', touched: true, error: 'Error D Cat' - } + } as any } }) }) it('should move deep field state from high index to low index', () => { // implementation of changeValue taken directly from Final Form - const changeValue = (state, name, mutate) => { + const changeValue = (state: any, name: string, mutate: (value: any) => any) => { const before = getIn(state.formState.values, name) const after = mutate(before) - state.formState.values = setIn(state.formState.values, name, after) || {} + state.formState.values = setIn(state.formState.values, name, after) || {} as any } - const state = { + const state: MutableState = { formState: { values: { foo: [ @@ -341,45 +341,45 @@ describe('move', () => { name: 'foo[0].dog', touched: true, error: 'Error A Dog' - }, + } as any, 'foo[0].cat': { name: 'foo[0].cat', touched: false, error: 'Error A Cat' - }, + } as any, 'foo[1].dog': { name: 'foo[1].dog', touched: true, error: 'Error B Dog' - }, + } as any, 'foo[1].cat': { name: 'foo[1].cat', touched: true, error: 'Error B Cat' - }, + } as any, 'foo[2].dog': { name: 'foo[2].dog', touched: true, error: 'Error C Dog' - }, + } as any, 'foo[2].cat': { name: 'foo[2].cat', touched: false, error: 'Error C Cat' - }, + } as any, 'foo[3].dog': { name: 'foo[3].dog', touched: false, error: 'Error D Dog' - }, + } as any, 'foo[3].cat': { name: 'foo[3].cat', touched: true, error: 'Error D Cat' - } + } as any } } - move(['foo', 2, 0], state, { changeValue }) + move(['foo', 2, 0], state, { changeValue } as unknown as Tools) expect(state).toMatchObject({ formState: { values: { @@ -408,12 +408,12 @@ describe('move', () => { name: 'foo[1].dog', touched: true, error: 'Error A Dog' - }, + } as any, 'foo[1].cat': { name: 'foo[1].cat', touched: false, error: 'Error A Cat' - }, + } as any, 'foo[2].dog': { name: 'foo[2].dog', touched: true, @@ -430,53 +430,53 @@ describe('move', () => { name: 'foo[3].dog', touched: false, error: 'Error D Dog' - }, + } as any, 'foo[3].cat': { name: 'foo[3].cat', touched: true, error: 'Error D Cat' - } + } as any } }) }) it('should move fields with different shapes', () => { // implementation of changeValue taken directly from Final Form - const changeValue = (state, name, mutate) => { + const changeValue = (state: any, name: string, mutate: (value: any) => any) => { const before = getIn(state.formState.values, name) const after = mutate(before) - state.formState.values = setIn(state.formState.values, name, after) || {} + state.formState.values = setIn(state.formState.values, name, after) || {} as any } - const state = { + const state: MutableState = { formState: { values: { foo: [{ dog: 'apple dog', cat: 'apple cat' }, { dog: 'banana dog' }] - } + } as any }, fields: { 'foo[0].dog': { name: 'foo[0].dog', touched: true, error: 'Error A Dog' - }, + } as any, 'foo[0].cat': { name: 'foo[0].cat', touched: false, error: 'Error A Cat' - }, + } as any, 'foo[1].dog': { name: 'foo[1].dog', touched: true, error: 'Error B Dog' - } + } as any } } - move(['foo', 0, 1], state, { changeValue }) + move(['foo', 0, 1], state, { changeValue } as unknown as Tools) expect(state).toMatchObject({ formState: { values: { foo: [{ dog: 'banana dog' }, { dog: 'apple dog', cat: 'apple cat' }] - } + } as any }, fields: { 'foo[0].dog': { @@ -502,12 +502,12 @@ describe('move', () => { }) it('should move fields with different complex not matching shapes', () => { // implementation of changeValue taken directly from Final Form - const changeValue = (state, name, mutate) => { + const changeValue = (state: any, name: string, mutate: (value: any) => any) => { const before = getIn(state.formState.values, name) const after = mutate(before) - state.formState.values = setIn(state.formState.values, name, after) || {} + state.formState.values = setIn(state.formState.values, name, after) || {} as any } - const state = { + const state: MutableState = { formState: { values: { foo: [{ dog: 'apple dog', cat: 'apple cat', colors: [{ name: 'red'}, { name: 'blue'}], deep: { inside: { rock: 'black'}} }, @@ -519,45 +519,45 @@ describe('move', () => { name: 'foo[0].dog', touched: true, error: 'Error A Dog' - }, + } as any, 'foo[0].cat': { name: 'foo[0].cat', touched: false, error: 'Error A Cat' - }, + } as any, 'foo[0].colors[0].name': { name: 'foo[0].colors[0].name', touched: true, error: 'Error A Colors Red' - }, + } as any, 'foo[0].colors[1].name': { name: 'foo[0].colors[1].name', touched: true, error: 'Error A Colors Blue' - }, + } as any, 'foo[0].deep.inside.rock': { name: 'foo[0].deep.inside.rock', touched: true, error: 'Error A Deep Inside Rock Black' - }, + } as any, 'foo[1].dog': { name: 'foo[1].dog', touched: true, error: 'Error B Dog' - }, + } as any, 'foo[1].mouse': { name: 'foo[1].mouse', touched: true, error: 'Error B Mickey' - }, + } as any, 'foo[1].deep.inside.axe': { name: 'foo[1].deep.inside.axe', touched: true, error: 'Error B Deep Inside Axe Golden' - }, + } as any, } } - move(['foo', 0, 1], state, { changeValue }) + move(['foo', 0, 1], state, { changeValue } as unknown as Tools) expect(state).toMatchObject({ formState: { values: { @@ -582,7 +582,7 @@ describe('move', () => { name: 'foo[0].deep.inside.axe', touched: true, error: 'Error B Deep Inside Axe Golden' - }, + } as any, 'foo[1].dog': { name: 'foo[1].dog', touched: true, @@ -611,23 +611,23 @@ describe('move', () => { name: 'foo[1].deep.inside.rock', touched: true, error: 'Error A Deep Inside Rock Black' - }, + } as any, } }) }) it('should preserve functions in field state', () => { // implementation of changeValue taken directly from Final Form - const changeValue = (state, name, mutate) => { + const changeValue = (state: any, name: string, mutate: (value: any) => any) => { const before = getIn(state.formState.values, name) const after = mutate(before) - state.formState.values = setIn(state.formState.values, name, after) || {} + state.formState.values = setIn(state.formState.values, name, after) || {} as any } - const state = { + const state: MutableState = { formState: { values: { foo: ['apple', 'banana', 'carrot', 'date'] - } + } as any }, fields: { 'foo[0]': { @@ -660,7 +660,7 @@ describe('move', () => { } } } - move(['foo', 0, 2], state, { changeValue }) + move(['foo', 0, 2], state, { changeValue } as unknown as Tools) expect(state.fields['foo[0]'].change()).toBe('foo[0]') expect(state.fields['foo[1]'].change()).toBe('foo[1]') expect(state.fields['foo[2]'].change()).toBe('foo[2]') diff --git a/src/move.js b/src/move.ts similarity index 83% rename from src/move.js rename to src/move.ts index 1529569..70578f6 100644 --- a/src/move.js +++ b/src/move.ts @@ -1,5 +1,4 @@ -// @flow -import type { MutableState, Mutator, Tools } from 'final-form' +import { MutableState, Mutator, Tools } from 'final-form' import copyField from './copyField' import { escapeRegexTokens } from './utils' @@ -7,11 +6,11 @@ const move: Mutator = ( [name, from, to]: any[], state: MutableState, { changeValue }: Tools -) => { +): void => { if (from === to) { return } - changeValue(state, name, (array: ?(any[])): any[] => { + changeValue(state, name, (array?: any[]): any[] => { const copy = [...(array || [])] const value = copy[from] copy.splice(from, 1) @@ -19,11 +18,11 @@ const move: Mutator = ( return copy }) - const newFields = {} + const newFields: { [key: string]: any } = {} const pattern = new RegExp(`^${escapeRegexTokens(name)}\\[(\\d+)\\](.*)`) - let lowest - let highest - let increment + let lowest: number + let highest: number + let increment: number if (from > to) { lowest = to highest = from @@ -42,7 +41,7 @@ const move: Mutator = ( copyField(state.fields, key, newFields, newKey) return } - + if (lowest <= fieldIndex && fieldIndex <= highest) { // Shift all indices const newKey = `${name}[${fieldIndex + increment}]${tokens[2]}` @@ -59,4 +58,4 @@ const move: Mutator = ( state.fields = newFields } -export default move +export default move \ No newline at end of file diff --git a/src/pop.test.js b/src/pop.test.ts similarity index 80% rename from src/pop.test.js rename to src/pop.test.ts index adfd1da..d6659f6 100644 --- a/src/pop.test.js +++ b/src/pop.test.ts @@ -1,29 +1,29 @@ import pop from './pop' -import { getIn, setIn } from 'final-form' +import { getIn, setIn, MutableState, Tools } from 'final-form' describe('pop', () => { it('should call changeValue once', () => { const changeValue = jest.fn() - const state = { + const state: MutableState = { formState: { values: { foo: ['one', 'two'] - } + } as any }, fields: { 'foo[0]': { name: 'foo[0]', touched: true, error: 'First Error' - }, + } as any, 'foo[1]': { name: 'foo[1]', touched: false, error: 'Second Error' - } + } as any } } - const result = pop(['foo'], state, { changeValue, getIn, setIn }) + const result = pop(['foo'], state, { changeValue, getIn, setIn } as unknown as Tools) expect(result).toBeUndefined() expect(changeValue).toHaveBeenCalled() expect(changeValue).toHaveBeenCalledTimes(1) @@ -34,20 +34,20 @@ describe('pop', () => { it('should return undefined if array is undefined', () => { // implementation of changeValue taken directly from Final Form - const changeValue = (state, name, mutate) => { + const changeValue = (state: any, name: string, mutate: (value: any) => any) => { const before = getIn(state.formState.values, name) const after = mutate(before) - state.formState.values = setIn(state.formState.values, name, after) || {} + state.formState.values = setIn(state.formState.values, name, after) || {} as any } - const state = { + const state: MutableState = { formState: { values: { foo: undefined - } + } as any }, fields: {} - } - const returnValue = pop(['foo'], state, { changeValue, getIn, setIn }) + } as any + const returnValue = pop(['foo'], state, { changeValue, getIn, setIn } as unknown as Tools) expect(returnValue).toBeUndefined() const result = state.formState.foo expect(result).toBeUndefined() @@ -55,20 +55,20 @@ describe('pop', () => { it('should return empty array if array is empty', () => { // implementation of changeValue taken directly from Final Form - const changeValue = (state, name, mutate) => { + const changeValue = (state: any, name: string, mutate: (value: any) => any) => { const before = getIn(state.formState.values, name) const after = mutate(before) - state.formState.values = setIn(state.formState.values, name, after) || {} + state.formState.values = setIn(state.formState.values, name, after) || {} as any } - const state = { + const state: MutableState = { formState: { values: { foo: [] - } + } as any }, fields: {} - } - const returnValue = pop(['foo'], state, { changeValue, getIn, setIn }) + } as any + const returnValue = pop(['foo'], state, { changeValue, getIn, setIn } as unknown as Tools) expect(returnValue).toBeUndefined() const result = state.formState.values.foo expect(Array.isArray(result)).toBe(true) @@ -77,31 +77,31 @@ describe('pop', () => { it('should pop value off the end of array and return it', () => { // implementation of changeValue taken directly from Final Form - const changeValue = jest.fn((state, name, mutate) => { + const changeValue = jest.fn((state: any, name: string, mutate: (value: any) => any) => { const before = getIn(state.formState.values, name) const after = mutate(before) - state.formState.values = setIn(state.formState.values, name, after) || {} + state.formState.values = setIn(state.formState.values, name, after) || {} as any }) - const state = { + const state: MutableState = { formState: { values: { foo: ['a', 'b', 'c'] - } + } as any }, fields: { 'foo[0]': { name: 'foo[0]', touched: true, error: 'First Error' - }, + } as any, 'foo[1]': { name: 'foo[1]', touched: false, error: 'Second Error' - } + } as any } } - const returnValue = pop(['foo'], state, { changeValue, getIn, setIn }) + const returnValue = pop(['foo'], state, { changeValue, getIn, setIn } as unknown as Tools) const result = state.formState.values.foo expect(returnValue).toBe('c') expect(Array.isArray(result)).toBe(true) @@ -111,46 +111,46 @@ describe('pop', () => { it('should pop value off the end of array and return it', () => { const array = ['a', 'b', 'c', 'd'] // implementation of changeValue taken directly from Final Form - const changeValue = (state, name, mutate) => { + const changeValue = (state: any, name: string, mutate: (value: any) => any) => { const before = getIn(state.formState.values, name) const after = mutate(before) - state.formState.values = setIn(state.formState.values, name, after) || {} + state.formState.values = setIn(state.formState.values, name, after) || {} as any } - const state = { + const state: MutableState = { formState: { values: { foo: array, anotherField: 42 - } + } as any }, fields: { 'foo[0]': { name: 'foo[0]', touched: true, error: 'A Error' - }, + } as any, 'foo[1]': { name: 'foo[1]', touched: false, error: 'B Error' - }, + } as any, 'foo[2]': { name: 'foo[2]', touched: true, error: 'C Error' - }, + } as any, 'foo[3]': { name: 'foo[3]', touched: false, error: 'D Error' - }, + } as any, anotherField: { name: 'anotherField', touched: false } } } - const returnValue = pop(['foo'], state, { changeValue, getIn, setIn }) + const returnValue = pop(['foo'], state, { changeValue, getIn, setIn } as unknown as Tools) expect(returnValue).toBe('d') expect(Array.isArray(state.formState.values.foo)).toBe(true) expect(state.formState.values.foo).not.toBe(array) // copied @@ -159,24 +159,24 @@ describe('pop', () => { values: { foo: ['a', 'b', 'c'], anotherField: 42 - } + } as any }, fields: { 'foo[0]': { name: 'foo[0]', touched: true, error: 'A Error' - }, + } as any, 'foo[1]': { name: 'foo[1]', touched: false, error: 'B Error' - }, + } as any, 'foo[2]': { name: 'foo[2]', touched: true, error: 'C Error' - }, + } as any, anotherField: { name: 'anotherField', touched: false @@ -188,46 +188,46 @@ describe('pop', () => { it('should pop value off the end of array and return it (nested arrays)', () => { const array = ['a', 'b', 'c', 'd'] // implementation of changeValue taken directly from Final Form - const changeValue = (state, name, mutate) => { + const changeValue = (state: any, name: string, mutate: (value: any) => any) => { const before = getIn(state.formState.values, name) const after = mutate(before) - state.formState.values = setIn(state.formState.values, name, after) || {} + state.formState.values = setIn(state.formState.values, name, after) || {} as any } - const state = { + const state: MutableState = { formState: { values: { foo: [array], anotherField: 42 - } + } as any }, fields: { 'foo[0][0]': { name: 'foo[0][0]', touched: true, error: 'A Error' - }, + } as any, 'foo[0][1]': { name: 'foo[0][1]', touched: false, error: 'B Error' - }, + } as any, 'foo[0][2]': { name: 'foo[0][2]', touched: true, error: 'C Error' - }, + } as any, 'foo[0][3]': { name: 'foo[0][3]', touched: false, error: 'D Error' - }, + } as any, anotherField: { name: 'anotherField', touched: false } } } - const returnValue = pop(['foo[0]'], state, { changeValue, getIn, setIn }) + const returnValue = pop(['foo[0]'], state, { changeValue, getIn, setIn } as unknown as Tools) expect(returnValue).toBe('d') expect(Array.isArray(state.formState.values.foo)).toBe(true) expect(state.formState.values.foo).not.toBe(array) // copied @@ -236,24 +236,24 @@ describe('pop', () => { values: { foo: [['a', 'b', 'c']], anotherField: 42 - } + } as any }, fields: { 'foo[0][0]': { name: 'foo[0][0]', touched: true, error: 'A Error' - }, + } as any, 'foo[0][1]': { name: 'foo[0][1]', touched: false, error: 'B Error' - }, + } as any, 'foo[0][2]': { name: 'foo[0][2]', touched: true, error: 'C Error' - }, + } as any, anotherField: { name: 'anotherField', touched: false diff --git a/src/pop.js b/src/pop.ts similarity index 69% rename from src/pop.js rename to src/pop.ts index cfdb486..a4d864c 100644 --- a/src/pop.js +++ b/src/pop.ts @@ -1,17 +1,16 @@ -// @flow -import type { MutableState, Mutator, Tools } from 'final-form' +import { MutableState, Mutator, Tools } from 'final-form' import remove from './remove' const pop: Mutator = ( [name]: any[], state: MutableState, tools: Tools -) => { - const { getIn } = tools; +): any => { + const { getIn } = tools const array = getIn(state.formState.values, name) return array && array.length > 0 ? remove([name, array.length - 1], state, tools) : undefined } -export default pop +export default pop \ No newline at end of file diff --git a/src/push.test.js b/src/push.test.ts similarity index 74% rename from src/push.test.js rename to src/push.test.ts index 9e261f9..891629b 100644 --- a/src/push.test.js +++ b/src/push.test.ts @@ -1,16 +1,18 @@ import push from './push' +import { createMockState, createMockTools } from './testUtils' describe('push', () => { - const getOp = value => { + const getOp = (value: any) => { const changeValue = jest.fn() - push(['foo', value], {}, { changeValue }) + const mockState = createMockState() + push(['foo', value], mockState, createMockTools({ changeValue })) return changeValue.mock.calls[0][2] } it('should call changeValue once', () => { const changeValue = jest.fn() - const state = {} - const result = push(['foo', 'bar'], state, { changeValue }) + const state = createMockState() + const result = push(['foo', 'bar'], state, createMockTools({ changeValue })) expect(result).toBeUndefined() expect(changeValue).toHaveBeenCalled() expect(changeValue).toHaveBeenCalledTimes(1) @@ -33,4 +35,4 @@ describe('push', () => { expect(Array.isArray(result)).toBe(true) expect(result).toEqual(['a', 'b', 'c', 'd']) }) -}) +}) \ No newline at end of file diff --git a/src/push.js b/src/push.ts similarity index 50% rename from src/push.js rename to src/push.ts index 45eb8ab..c6544e9 100644 --- a/src/push.js +++ b/src/push.ts @@ -1,14 +1,13 @@ -// @flow -import type { MutableState, Mutator, Tools } from 'final-form' +import { MutableState, Mutator, Tools } from 'final-form' const push: Mutator = ( [name, value]: any[], state: MutableState, { changeValue }: Tools -) => { - changeValue(state, name, (array: ?(any[])): any[] => +): void => { + changeValue(state, name, (array?: any[]): any[] => array ? [...array, value] : [value] ) } -export default push +export default push \ No newline at end of file diff --git a/src/remove.test.js b/src/remove.test.ts similarity index 76% rename from src/remove.test.js rename to src/remove.test.ts index 245f4ff..ff3fbc4 100644 --- a/src/remove.test.js +++ b/src/remove.test.ts @@ -1,29 +1,29 @@ import remove from './remove' -import { getIn, setIn } from 'final-form' +import { getIn, setIn, MutableState, Tools } from 'final-form' describe('remove', () => { it('should call changeValue once', () => { const changeValue = jest.fn() - const state = { + const state: MutableState = { formState: { values: { foo: ['one', 'two'] - } + } as any }, fields: { 'foo[0]': { name: 'foo[0]', touched: true, error: 'First Error' - }, + } as any, 'foo[1]': { name: 'foo[1]', touched: false, error: 'Second Error' - } + } as any } } - const result = remove(['foo', 0], state, { changeValue, getIn, setIn }) + const result = remove(['foo', 0], state, { changeValue, getIn, setIn } as unknown as Tools) expect(result).toBeUndefined() expect(changeValue).toHaveBeenCalled() expect(changeValue).toHaveBeenCalledTimes(1) @@ -34,47 +34,63 @@ describe('remove', () => { it('should treat undefined like an empty array', () => { const changeValue = jest.fn() - const state = { + const state: MutableState = { formState: { values: { foo: undefined - } + } as any }, fields: {} - } - const returnValue = remove(['foo', 1], state, { changeValue, getIn, setIn }) + } as any + const returnValue = remove(['foo', 1], state, { changeValue, getIn, setIn } as unknown as Tools) expect(returnValue).toBeUndefined() const op = changeValue.mock.calls[0][2] const result = op(undefined) expect(result).toBeUndefined() }) + it('should return undefined when removing last element from array', () => { + const changeValue = jest.fn() + const state: MutableState = { + formState: { + values: { + foo: ['only'] + } as any + }, + fields: {} + } as any + remove(['foo', 0], state, { changeValue, getIn, setIn } as unknown as Tools) + const op = changeValue.mock.calls[0][2] + const result = op(['only']) + expect(result).toBeUndefined() + }) + it('should remove value from the specified index, and return it', () => { const array = ['a', 'b', 'c', 'd'] // implementation of changeValue taken directly from Final Form - const changeValue = (state, name, mutate) => { + const changeValue = (state: any, name: string, mutate: (value: any) => any) => { const before = getIn(state.formState.values, name) const after = mutate(before) state.formState.values = setIn(state.formState.values, name, after) || {} } - function blur0() {} - function change0() {} - function focus0() {} - function blur1() {} - function change1() {} - function focus1() {} - function blur2() {} - function change2() {} - function focus2() {} - function blur3() {} - function change3() {} - function focus3() {} - const state = { + function blur0() { } + function change0() { } + function focus0() { } + function blur1() { } + function change1() { } + function focus1() { } + function blur2() { } + function change2() { } + function focus2() { } + function blur3() { } + function change3() { } + function focus3() { } + const state: MutableState = { formState: { values: { foo: array, anotherField: 42 - } + } as any }, fields: { 'foo[0]': { @@ -84,7 +100,7 @@ describe('remove', () => { focus: focus0, touched: true, error: 'A Error' - }, + } as any, 'foo[1]': { name: 'foo[1]', blur: blur1, @@ -92,7 +108,7 @@ describe('remove', () => { focus: focus1, touched: false, error: 'B Error' - }, + } as any, 'foo[3]': { name: 'foo[3]', blur: blur3, @@ -100,7 +116,7 @@ describe('remove', () => { focus: focus3, touched: false, error: 'D Error' - }, + } as any, 'foo[2]': { name: 'foo[2]', blur: blur2, @@ -108,14 +124,14 @@ describe('remove', () => { focus: focus2, touched: true, error: 'C Error' - }, + } as any, anotherField: { name: 'anotherField', touched: false } } } - const returnValue = remove(['foo', 1], state, { changeValue, getIn, setIn }) + const returnValue = remove(['foo', 1], state, { changeValue, getIn, setIn } as unknown as Tools) expect(returnValue).toBe('b') expect(state.formState.values.foo).not.toBe(array) // copied expect(state).toEqual({ @@ -123,7 +139,7 @@ describe('remove', () => { values: { foo: ['a', 'c', 'd'], anotherField: 42 - } + } as any }, fields: { 'foo[0]': { @@ -133,7 +149,7 @@ describe('remove', () => { focus: focus0, touched: true, error: 'A Error' - }, + } as any, 'foo[2]': { name: 'foo[2]', blur: blur2, @@ -163,29 +179,29 @@ describe('remove', () => { it('should remove value from the specified index, and return it (nested arrays)', () => { const array = ['a', 'b', 'c', 'd'] // implementation of changeValue taken directly from Final Form - const changeValue = (state, name, mutate) => { + const changeValue = (state: any, name: string, mutate: (value: any) => any) => { const before = getIn(state.formState.values, name) const after = mutate(before) state.formState.values = setIn(state.formState.values, name, after) || {} } - function blur0() {} - function change0() {} - function focus0() {} - function blur1() {} - function change1() {} - function focus1() {} - function blur2() {} - function change2() {} - function focus2() {} - function blur3() {} - function change3() {} - function focus3() {} - const state = { + function blur0() { } + function change0() { } + function focus0() { } + function blur1() { } + function change1() { } + function focus1() { } + function blur2() { } + function change2() { } + function focus2() { } + function blur3() { } + function change3() { } + function focus3() { } + const state: MutableState = { formState: { values: { foo: [array], anotherField: 42 - } + } as any }, fields: { 'foo[0][3]': { @@ -195,7 +211,7 @@ describe('remove', () => { focus: focus3, touched: false, error: 'D Error' - }, + } as any, 'foo[0][0]': { name: 'foo[0][0]', blur: blur0, @@ -203,7 +219,7 @@ describe('remove', () => { focus: focus0, touched: true, error: 'A Error' - }, + } as any, 'foo[0][1]': { name: 'foo[0][1]', blur: blur1, @@ -211,7 +227,7 @@ describe('remove', () => { focus: focus1, touched: false, error: 'B Error' - }, + } as any, 'foo[0][2]': { name: 'foo[0][2]', blur: blur2, @@ -219,7 +235,7 @@ describe('remove', () => { focus: focus2, touched: true, error: 'C Error' - }, + } as any, anotherField: { name: 'anotherField', touched: false @@ -230,7 +246,7 @@ describe('remove', () => { changeValue, getIn, setIn - }) + } as unknown as Tools) expect(returnValue).toBe('b') expect(state.formState.values.foo).not.toBe(array) // copied expect(state).toEqual({ @@ -238,7 +254,7 @@ describe('remove', () => { values: { foo: [['a', 'c', 'd']], anotherField: 42 - } + } as any }, fields: { 'foo[0][2]': { @@ -257,7 +273,7 @@ describe('remove', () => { focus: focus0, touched: true, error: 'A Error' - }, + } as any, 'foo[0][1]': { name: 'foo[0][1]', blur: blur1, @@ -279,16 +295,16 @@ describe('remove', () => { const array = ['a', { key: 'val' }] const changeValue = jest.fn() const renameField = jest.fn() - function blur0() {} - function change0() {} - function focus0() {} - function blur1() {} - function change1() {} - function focus1() {} - function blur2() {} - function change2() {} - function focus2() {} - const state = { + function blur0() { } + function change0() { } + function focus0() { } + function blur1() { } + function change1() { } + function focus1() { } + function blur2() { } + function change2() { } + function focus2() { } + const state: MutableState = { formState: { values: { foo: array, @@ -301,7 +317,7 @@ describe('remove', () => { } ] } - }, + } as any, fields: { 'foo[0]': { name: 'foo[0]', @@ -310,7 +326,7 @@ describe('remove', () => { focus: focus0, touched: true, error: 'A Error' - }, + } as any, 'foo[0].key': { name: 'foo[0].key', blur: blur2, @@ -318,7 +334,7 @@ describe('remove', () => { focus: focus2, touched: false, error: 'A Error' - }, + } as any, 'foo[1]': { name: 'foo[1]', blur: blur1, @@ -326,7 +342,7 @@ describe('remove', () => { focus: focus1, touched: false, error: 'B Error' - }, + } as any, 'foo[1].key': { name: 'foo[1].key', blur: blur2, @@ -334,20 +350,20 @@ describe('remove', () => { focus: focus2, touched: false, error: 'B Error' - }, + } as any, anotherField: { name: 'anotherField', touched: false } } - } + } as any const returnValue = remove(['foo', 0], state, { renameField, changeValue, getIn, setIn - }) + } as unknown as Tools) expect(returnValue).toBeUndefined() expect(getIn(state, 'formState.submitErrors')).toEqual({ foo: [] }) }) @@ -356,16 +372,16 @@ describe('remove', () => { const array = ['a', { key: 'val' }] const changeValue = jest.fn() const renameField = jest.fn() - function blur0() {} - function change0() {} - function focus0() {} - function blur1() {} - function change1() {} - function focus1() {} - function blur2() {} - function change2() {} - function focus2() {} - const state = { + function blur0() { } + function change0() { } + function focus0() { } + function blur1() { } + function change1() { } + function focus1() { } + function blur2() { } + function change2() { } + function focus2() { } + const state: MutableState = { formState: { values: { foo: array, @@ -381,7 +397,7 @@ describe('remove', () => { } ] } - }, + } as any, fields: { 'foo[0]': { name: 'foo[0]', @@ -390,7 +406,7 @@ describe('remove', () => { focus: focus0, touched: true, error: 'A Error' - }, + } as any, 'foo[0].key': { name: 'foo[0].key', blur: blur2, @@ -398,7 +414,7 @@ describe('remove', () => { focus: focus2, touched: false, error: 'A Error' - }, + } as any, 'foo[1]': { name: 'foo[1]', blur: blur1, @@ -406,7 +422,7 @@ describe('remove', () => { focus: focus1, touched: false, error: 'B Error' - }, + } as any, 'foo[1].key': { name: 'foo[1].key', blur: blur2, @@ -414,20 +430,20 @@ describe('remove', () => { focus: focus2, touched: false, error: 'B Error' - }, + } as any, anotherField: { name: 'anotherField', touched: false } } - } + } as any const returnValue = remove(['foo', 0], state, { renameField, changeValue, getIn, setIn - }) + } as unknown as Tools) expect(returnValue).toBeUndefined() expect(getIn(state, 'formState.submitErrors')).toEqual({ foo: [{ key: 'B Submit Error' }] diff --git a/src/remove.js b/src/remove.ts similarity index 85% rename from src/remove.js rename to src/remove.ts index dce9a24..86bf025 100644 --- a/src/remove.js +++ b/src/remove.ts @@ -1,5 +1,4 @@ -// @flow -import type { MutableState, Mutator, Tools } from 'final-form' +import { MutableState, Mutator, Tools } from 'final-form' import copyField from './copyField' import { escapeRegexTokens } from './utils' @@ -7,9 +6,9 @@ const remove: Mutator = ( [name, index]: any[], state: MutableState, { changeValue, getIn, setIn }: Tools -) => { - let returnValue - changeValue(state, name, (array: ?(any[])): ?(any[]) => { +): any => { + let returnValue: any + changeValue(state, name, (array?: any[]): any[] | undefined => { if (!array) { return array } @@ -25,7 +24,7 @@ const remove: Mutator = ( // now we have to remove any subfields for our index, // and decrement all higher indexes. const pattern = new RegExp(`^${escapeRegexTokens(name)}\\[(\\d+)\\](.*)`) - const newFields = {} + const newFields: { [key: string]: any } = {} Object.keys(state.fields).forEach(key => { const tokens = pattern.exec(key) if (tokens) { @@ -39,7 +38,7 @@ const remove: Mutator = ( // if has submitErrors for array if (Array.isArray(submitErrors)) { submitErrors.splice(index, 1) - state = setIn(state, path, submitErrors) + setIn(state, path, submitErrors) } } @@ -63,4 +62,4 @@ const remove: Mutator = ( return returnValue } -export default remove +export default remove \ No newline at end of file diff --git a/src/removeBatch.test.js b/src/removeBatch.test.ts similarity index 77% rename from src/removeBatch.test.js rename to src/removeBatch.test.ts index aaec2e5..46a7b27 100644 --- a/src/removeBatch.test.js +++ b/src/removeBatch.test.ts @@ -1,53 +1,53 @@ import removeBatch from './removeBatch' -import { getIn, setIn } from 'final-form' +import { getIn, setIn, MutableState, Tools } from 'final-form' describe('removeBatch', () => { const getOp = value => { const changeValue = jest.fn() - const state = { + const state: MutableState = { formState: { values: { foo: ['one', 'two'] - } + } as any }, fields: { 'foo[0]': { name: 'foo[0]', touched: true, error: 'First Error' - }, + } as any, 'foo[1]': { name: 'foo[1]', touched: false, error: 'Second Error' - } + } as any } } - removeBatch(['foo', value], state, { changeValue }) + removeBatch(['foo', value], state, { changeValue } as unknown as Tools) return changeValue.mock.calls[0][2] } it('should call changeValue once', () => { // implementation of changeValue taken directly from Final Form - const changeValue = jest.fn((state, name, mutate) => { + const changeValue = jest.fn((state: any, name: string, mutate: (value: any) => any) => { const before = getIn(state.formState.values, name) const after = mutate(before) - state.formState.values = setIn(state.formState.values, name, after) || {} + state.formState.values = setIn(state.formState.values, name, after) || {} as any }) - function blur0() {} - function change0() {} - function focus0() {} - function blur1() {} - function change1() {} - function focus1() {} - function blur2() {} - function change2() {} - function focus2() {} - const state = { + function blur0() { } + function change0() { } + function focus0() { } + function blur1() { } + function change1() { } + function focus1() { } + function blur2() { } + function change2() { } + function focus2() { } + const state: MutableState = { formState: { values: { foo: ['one', 'two', 'three'] - } + } as any }, fields: { 'foo[0]': { @@ -57,7 +57,7 @@ describe('removeBatch', () => { focus: focus0, touched: true, error: 'First Error' - }, + } as any, 'foo[1]': { name: 'foo[1]', blur: blur1, @@ -65,7 +65,7 @@ describe('removeBatch', () => { focus: focus1, touched: false, error: 'Second Error' - }, + } as any, 'foo[2]': { name: 'foo[2]', blur: blur2, @@ -73,10 +73,10 @@ describe('removeBatch', () => { focus: focus2, touched: true, error: 'Third Error' - } + } as any } } - const result = removeBatch(['foo', [1, 2]], state, { changeValue }) + const result = removeBatch(['foo', [1, 2]], state, { changeValue } as unknown as Tools) expect(Array.isArray(result)).toBe(true) expect(result).toEqual(['two', 'three']) expect(changeValue).toHaveBeenCalled() @@ -88,7 +88,7 @@ describe('removeBatch', () => { formState: { values: { foo: ['one'] - } + } as any }, fields: { 'foo[0]': { @@ -106,25 +106,25 @@ describe('removeBatch', () => { it('should not matter if indexes are out of order', () => { // implementation of changeValue taken directly from Final Form - const changeValue = jest.fn((state, name, mutate) => { + const changeValue = jest.fn((state: any, name: string, mutate: (value: any) => any) => { const before = getIn(state.formState.values, name) const after = mutate(before) - state.formState.values = setIn(state.formState.values, name, after) || {} + state.formState.values = setIn(state.formState.values, name, after) || {} as any }) - function blur0() {} - function change0() {} - function focus0() {} - function blur1() {} - function change1() {} - function focus1() {} - function blur2() {} - function change2() {} - function focus2() {} - const state = { + function blur0() { } + function change0() { } + function focus0() { } + function blur1() { } + function change1() { } + function focus1() { } + function blur2() { } + function change2() { } + function focus2() { } + const state: MutableState = { formState: { values: { foo: ['one', 'two', 'three'] - } + } as any }, fields: { 'foo[0]': { @@ -134,7 +134,7 @@ describe('removeBatch', () => { focus: focus0, touched: true, error: 'First Error' - }, + } as any, 'foo[1]': { name: 'foo[1]', blur: blur1, @@ -142,7 +142,7 @@ describe('removeBatch', () => { focus: focus1, touched: false, error: 'Second Error' - }, + } as any, 'foo[2]': { name: 'foo[2]', blur: blur2, @@ -150,10 +150,10 @@ describe('removeBatch', () => { focus: focus2, touched: true, error: 'Third Error' - } + } as any } } - const result = removeBatch(['foo', [2, 0]], state, { changeValue }) + const result = removeBatch(['foo', [2, 0]], state, { changeValue } as unknown as Tools) expect(Array.isArray(result)).toBe(true) expect(result).toEqual(['three', 'one']) expect(changeValue).toHaveBeenCalled() @@ -165,7 +165,7 @@ describe('removeBatch', () => { formState: { values: { foo: ['two'] - } + } as any }, fields: { 'foo[0]': { @@ -189,14 +189,14 @@ describe('removeBatch', () => { it('should keep the original state if no indexes are specified to be removed', () => { const array = ['a', 'b', 'c', 'd', 'e'] - function blur0() {} - function change0() {} - function focus0() {} - const state = { + function blur0() { } + function change0() { } + function focus0() { } + const state: MutableState = { formState: { values: { foo: array - } + } as any }, fields: { 'foo[0]': { @@ -206,20 +206,18 @@ describe('removeBatch', () => { focus: focus0, touched: true, error: 'A Error' - } + } as any } } const changeValue = jest.fn() - const returnValue = removeBatch(['foo[0]', []], state, { - changeValue - }) + const returnValue = removeBatch(['foo[0]', []], state, { changeValue } as unknown as Tools) expect(returnValue).toEqual([]) expect(state.formState.values.foo).toBe(array) // no change expect(state).toEqual({ formState: { values: { foo: array - } + } as any }, fields: { 'foo[0]': { @@ -229,7 +227,7 @@ describe('removeBatch', () => { focus: focus0, touched: true, error: 'A Error' - } + } as any } }) }) @@ -251,32 +249,32 @@ describe('removeBatch', () => { it('should adjust higher indexes when removing', () => { const array = ['a', 'b', 'c', 'd', 'e'] // implementation of changeValue taken directly from Final Form - const changeValue = (state, name, mutate) => { + const changeValue = (state: any, name: string, mutate: (value: any) => any) => { const before = getIn(state.formState.values, name) const after = mutate(before) state.formState.values = setIn(state.formState.values, name, after) || {} } - function blur0() {} - function blur1() {} - function blur2() {} - function blur3() {} - function blur4() {} - function change0() {} - function change1() {} - function change2() {} - function change3() {} - function change4() {} - function focus0() {} - function focus1() {} - function focus2() {} - function focus3() {} - function focus4() {} - const state = { + function blur0() { } + function blur1() { } + function blur2() { } + function blur3() { } + function blur4() { } + function change0() { } + function change1() { } + function change2() { } + function change3() { } + function change4() { } + function focus0() { } + function focus1() { } + function focus2() { } + function focus3() { } + function focus4() { } + const state: MutableState = { formState: { values: { foo: array, anotherField: 42 - } + } as any }, fields: { 'foo[4]': { @@ -286,7 +284,7 @@ describe('removeBatch', () => { focus: focus4, touched: true, error: 'E Error' - }, + } as any, 'foo[0]': { name: 'foo[0]', blur: blur0, @@ -294,7 +292,7 @@ describe('removeBatch', () => { focus: focus0, touched: true, error: 'A Error' - }, + } as any, 'foo[1]': { name: 'foo[1]', blur: blur1, @@ -302,7 +300,7 @@ describe('removeBatch', () => { focus: focus1, touched: false, error: 'B Error' - }, + } as any, 'foo[2]': { name: 'foo[2]', blur: blur2, @@ -310,7 +308,7 @@ describe('removeBatch', () => { focus: focus2, touched: true, error: 'C Error' - }, + } as any, 'foo[3]': { name: 'foo[3]', blur: blur3, @@ -318,14 +316,14 @@ describe('removeBatch', () => { focus: focus3, touched: false, error: 'D Error' - }, + } as any, anotherField: { name: 'anotherField', touched: false } } } - const returnValue = removeBatch(['foo', [1, 3]], state, { changeValue }) + const returnValue = removeBatch(['foo', [1, 3]], state, { changeValue } as unknown as Tools) expect(returnValue).toEqual(['b', 'd']) expect(state.formState.values.foo).not.toBe(array) // copied expect(state).toEqual({ @@ -333,7 +331,7 @@ describe('removeBatch', () => { values: { foo: ['a', 'c', 'e'], anotherField: 42 - } + } as any }, fields: { 'foo[2]': { @@ -374,32 +372,32 @@ describe('removeBatch', () => { it('should adjust higher indexes when removing (nested arrays)', () => { const array = ['a', 'b', 'c', 'd', 'e'] // implementation of changeValue taken directly from Final Form - const changeValue = (state, name, mutate) => { + const changeValue = (state: any, name: string, mutate: (value: any) => any) => { const before = getIn(state.formState.values, name) const after = mutate(before) state.formState.values = setIn(state.formState.values, name, after) || {} } - function blur0() {} - function blur1() {} - function blur2() {} - function blur3() {} - function blur4() {} - function change0() {} - function change1() {} - function change2() {} - function change3() {} - function change4() {} - function focus0() {} - function focus1() {} - function focus2() {} - function focus3() {} - function focus4() {} - const state = { + function blur0() { } + function blur1() { } + function blur2() { } + function blur3() { } + function blur4() { } + function change0() { } + function change1() { } + function change2() { } + function change3() { } + function change4() { } + function focus0() { } + function focus1() { } + function focus2() { } + function focus3() { } + function focus4() { } + const state: MutableState = { formState: { values: { foo: [array], anotherField: 42 - } + } as any }, fields: { 'foo[0][4]': { @@ -409,7 +407,7 @@ describe('removeBatch', () => { focus: focus4, touched: true, error: 'E Error' - }, + } as any, 'foo[0][0]': { name: 'foo[0][0]', blur: blur0, @@ -417,7 +415,7 @@ describe('removeBatch', () => { focus: focus0, touched: true, error: 'A Error' - }, + } as any, 'foo[0][1]': { name: 'foo[0][1]', blur: blur1, @@ -425,7 +423,7 @@ describe('removeBatch', () => { focus: focus1, touched: false, error: 'B Error' - }, + } as any, 'foo[0][2]': { name: 'foo[0][2]', blur: blur2, @@ -433,7 +431,7 @@ describe('removeBatch', () => { focus: focus2, touched: true, error: 'C Error' - }, + } as any, 'foo[0][3]': { name: 'foo[0][3]', blur: blur3, @@ -441,16 +439,14 @@ describe('removeBatch', () => { focus: focus3, touched: false, error: 'D Error' - }, + } as any, anotherField: { name: 'anotherField', touched: false } } } - const returnValue = removeBatch(['foo[0]', [1, 3]], state, { - changeValue - }) + const returnValue = removeBatch(['foo[0]', [1, 3]], state, { changeValue } as unknown as Tools) expect(returnValue).toEqual(['b', 'd']) expect(state.formState.values.foo).not.toBe(array) // copied expect(state).toEqual({ @@ -458,7 +454,7 @@ describe('removeBatch', () => { values: { foo: [['a', 'c', 'e']], anotherField: 42 - } + } as any }, fields: { 'foo[0][2]': { @@ -495,4 +491,10 @@ describe('removeBatch', () => { } }) }) + + it('should return undefined when removing all elements', () => { + const op = getOp([0, 1]) + const result = op(['a', 'b']) + expect(result).toBeUndefined() + }) }) diff --git a/src/removeBatch.js b/src/removeBatch.ts similarity index 86% rename from src/removeBatch.js rename to src/removeBatch.ts index 48057f1..34198b6 100644 --- a/src/removeBatch.js +++ b/src/removeBatch.ts @@ -1,5 +1,4 @@ -// @flow -import type { MutableState, Mutator, Tools } from 'final-form' +import { MutableState, Mutator, Tools } from 'final-form' import copyField from './copyField' import { escapeRegexTokens } from './utils' @@ -29,7 +28,7 @@ const removeBatch: Mutator = ( [name, indexes]: any[], state: MutableState, { changeValue }: Tools -) => { +): any[] => { if (indexes.length === 0) { return [] } @@ -44,10 +43,10 @@ const removeBatch: Mutator = ( } } - let returnValue = [] - changeValue(state, name, (array: ?(any[])): ?(any[]) => { + let returnValue: any[] = [] + changeValue(state, name, (array?: any[]): any[] | undefined => { // use original order of indexes for return value - returnValue = indexes.map(index => array && array[index]) + returnValue = indexes.map((index: number) => array && array[index]) if (!array) { return array @@ -67,7 +66,7 @@ const removeBatch: Mutator = ( // now we have to remove any subfields for our indexes, // and decrement all higher indexes. const pattern = new RegExp(`^${escapeRegexTokens(name)}\\[(\\d+)\\](.*)`) - const newFields = {} + const newFields: { [key: string]: any } = {} Object.keys(state.fields).forEach(key => { const tokens = pattern.exec(key) if (tokens) { @@ -80,9 +79,8 @@ const removeBatch: Mutator = ( if (fieldIndex > sortedIndexes[0]) { // Shift all higher indices down - const decrementedKey = `${name}[${fieldIndex - ~indexOfFieldIndex}]${ - tokens[2] - }` + const decrementedKey = `${name}[${fieldIndex - ~indexOfFieldIndex}]${tokens[2] + }` copyField(state.fields, key, newFields, decrementedKey) return } @@ -97,4 +95,4 @@ const removeBatch: Mutator = ( return returnValue } -export default removeBatch +export default removeBatch \ No newline at end of file diff --git a/src/shift.js b/src/shift.js deleted file mode 100644 index eb15224..0000000 --- a/src/shift.js +++ /dev/null @@ -1,11 +0,0 @@ -// @flow -import type { MutableState, Mutator, Tools } from 'final-form' -import remove from './remove' - -const shift: Mutator = ( - [name]: any[], - state: MutableState, - tools: Tools -) => remove([name, 0], state, tools) - -export default shift diff --git a/src/shift.test.js b/src/shift.test.ts similarity index 83% rename from src/shift.test.js rename to src/shift.test.ts index 84950cc..fc1ef2b 100644 --- a/src/shift.test.js +++ b/src/shift.test.ts @@ -1,29 +1,29 @@ import shift from './shift' -import { getIn, setIn } from 'final-form' +import { getIn, setIn, MutableState, Tools } from 'final-form' describe('shift', () => { it('should call changeValue once', () => { const changeValue = jest.fn() - const state = { + const state: MutableState = { formState: { values: { foo: ['one', 'two'] } - }, + } as any, fields: { 'foo[0]': { name: 'foo[0]', touched: true, error: 'First Error' - }, + } as any, 'foo[1]': { name: 'foo[1]', touched: false, error: 'Second Error' - } + } as any } - } - const result = shift(['foo'], state, { changeValue, getIn, setIn }) + } as any + const result = shift(['foo'], state, { changeValue, getIn, setIn } as unknown as Tools) expect(result).toBeUndefined() expect(changeValue).toHaveBeenCalled() expect(changeValue).toHaveBeenCalledTimes(1) @@ -34,15 +34,15 @@ describe('shift', () => { it('should treat undefined like an empty array', () => { const changeValue = jest.fn() - const state = { + const state: MutableState = { formState: { values: { foo: undefined } - }, + } as any, fields: {} - } - const returnValue = shift(['foo'], state, { changeValue, getIn, setIn }) + } as any + const returnValue = shift(['foo'], state, { changeValue, getIn, setIn } as unknown as Tools) expect(returnValue).toBeUndefined() const op = changeValue.mock.calls[0][2] const result = op(undefined) @@ -52,46 +52,46 @@ describe('shift', () => { it('should remove first value from array and return it', () => { const array = ['a', 'b', 'c', 'd'] // implementation of changeValue taken directly from Final Form - const changeValue = (state, name, mutate) => { + const changeValue = (state: any, name: string, mutate: (value: any) => any) => { const before = getIn(state.formState.values, name) const after = mutate(before) state.formState.values = setIn(state.formState.values, name, after) || {} } - const state = { + const state: MutableState = { formState: { values: { foo: array, anotherField: 42 } - }, + } as any, fields: { 'foo[0]': { name: 'foo[0]', touched: true, error: 'A Error' - }, + } as any, 'foo[1]': { name: 'foo[1]', touched: false, error: 'B Error' - }, + } as any, 'foo[2]': { name: 'foo[2]', touched: true, error: 'C Error' - }, + } as any, 'foo[3]': { name: 'foo[3]', touched: false, error: 'D Error' - }, + } as any, anotherField: { name: 'anotherField', touched: false - } + } as any } - } - const returnValue = shift(['foo'], state, { changeValue, getIn, setIn }) + } as any + const returnValue = shift(['foo'], state, { changeValue, getIn, setIn } as unknown as Tools) expect(returnValue).toBe('a') expect(state.formState.values.foo).not.toBe(array) // copied expect(state).toEqual({ @@ -127,4 +127,4 @@ describe('shift', () => { } }) }) -}) +}) \ No newline at end of file diff --git a/src/shift.ts b/src/shift.ts new file mode 100644 index 0000000..c76fa03 --- /dev/null +++ b/src/shift.ts @@ -0,0 +1,10 @@ +import { MutableState, Mutator, Tools } from 'final-form' +import remove from './remove' + +const shift: Mutator = ( + [name]: any[], + state: MutableState, + tools: Tools +): any => remove([name, 0], state, tools) + +export default shift \ No newline at end of file diff --git a/src/swap.test.js b/src/swap.test.ts similarity index 89% rename from src/swap.test.js rename to src/swap.test.ts index b74dd7b..a2fc4cb 100644 --- a/src/swap.test.js +++ b/src/swap.test.ts @@ -1,24 +1,24 @@ import swap from './swap' -import { getIn, setIn } from 'final-form' +import { getIn, setIn, MutableState, Tools } from 'final-form' describe('swap', () => { - const getOp = (from, to) => { + const getOp = (from, to: any) => { const changeValue = jest.fn() - swap(['foo', from, to], { fields: {} }, { changeValue }) + swap(['foo', from, to], { fields: {} }, { changeValue } as unknown as Tools) return changeValue.mock.calls[0][2] } it('should do nothing if indexA and indexB are equal', () => { const changeValue = jest.fn() - const result = swap(['foo', 1, 1], { fields: {} }, { changeValue }) + const result = swap(['foo', 1, 1], { fields: {} }, { changeValue } as unknown as Tools) expect(result).toBeUndefined() expect(changeValue).not.toHaveBeenCalled() }) it('should call changeValue once', () => { const changeValue = jest.fn() - const state = { fields: {} } - const result = swap(['foo', 0, 2], state, { changeValue }) + const state: MutableState = { fields: {} } + const result = swap(['foo', 0, 2], state, { changeValue } as unknown as Tools) expect(result).toBeUndefined() expect(changeValue).toHaveBeenCalled() expect(changeValue).toHaveBeenCalledTimes(1) @@ -47,16 +47,16 @@ describe('swap', () => { it('should swap field state as well as values', () => { // implementation of changeValue taken directly from Final Form - const changeValue = (state, name, mutate) => { + const changeValue = (state: any, name: string, mutate: (value: any) => any) => { const before = getIn(state.formState.values, name) const after = mutate(before) - state.formState.values = setIn(state.formState.values, name, after) || {} + state.formState.values = setIn(state.formState.values, name, after) || {} as any } - const state = { + const state: MutableState = { formState: { values: { foo: ['apple', 'banana', 'carrot', 'date'] - } + } as any }, fields: { 'foo[0]': { @@ -85,12 +85,12 @@ describe('swap', () => { } } } - swap(['foo', 0, 2], state, { changeValue }) + swap(['foo', 0, 2], state, { changeValue } as unknown as Tools) expect(state).toEqual({ formState: { values: { foo: ['carrot', 'banana', 'apple', 'date'] - } + } as any }, fields: { 'foo[2]': { @@ -123,12 +123,12 @@ describe('swap', () => { it('should swap field state for deep fields and different shapes', () => { // implementation of changeValue taken directly from Final Form - const changeValue = (state, name, mutate) => { + const changeValue = (state: any, name: string, mutate: (value: any) => any) => { const before = getIn(state.formState.values, name) const after = mutate(before) - state.formState.values = setIn(state.formState.values, name, after) || {} + state.formState.values = setIn(state.formState.values, name, after) || {} as any } - const state = { + const state: MutableState = { formState: { values: { foo: [ @@ -202,7 +202,7 @@ describe('swap', () => { } } } - swap(['foo', 0, 2], state, { changeValue }) + swap(['foo', 0, 2], state, { changeValue } as unknown as Tools) expect(state).toEqual({ formState: { values: { @@ -281,16 +281,16 @@ describe('swap', () => { it('should preserve functions in field state', () => { // implementation of changeValue taken directly from Final Form - const changeValue = (state, name, mutate) => { + const changeValue = (state: any, name: string, mutate: (value: any) => any) => { const before = getIn(state.formState.values, name) const after = mutate(before) - state.formState.values = setIn(state.formState.values, name, after) || {} + state.formState.values = setIn(state.formState.values, name, after) || {} as any } - const state = { + const state: MutableState = { formState: { values: { foo: ['apple', 'banana', 'carrot', 'date'] - } + } as any }, fields: { 'foo[0]': { @@ -323,7 +323,7 @@ describe('swap', () => { } } } - swap(['foo', 0, 2], state, { changeValue }) + swap(['foo', 0, 2], state, { changeValue } as unknown as Tools) expect(state.fields['foo[0]'].change()).toBe('foo[0]') expect(state.fields['foo[1]'].change()).toBe('foo[1]') expect(state.fields['foo[2]'].change()).toBe('foo[2]') diff --git a/src/swap.js b/src/swap.ts similarity index 85% rename from src/swap.js rename to src/swap.ts index 736adec..3c1a50d 100644 --- a/src/swap.js +++ b/src/swap.ts @@ -1,16 +1,15 @@ -// @flow -import type { MutableState, Mutator, Tools } from 'final-form' +import { MutableState, Mutator, Tools } from 'final-form' import copyField from './copyField' const swap: Mutator = ( [name, indexA, indexB]: any[], state: MutableState, { changeValue }: Tools -) => { +): void => { if (indexA === indexB) { return } - changeValue(state, name, (array: ?(any[])): any[] => { + changeValue(state, name, (array?: any[]): any[] => { const copy = [...(array || [])] const a = copy[indexA] copy[indexA] = copy[indexB] @@ -21,7 +20,7 @@ const swap: Mutator = ( // swap all field state that begin with "name[indexA]" with that under "name[indexB]" const aPrefix = `${name}[${indexA}]` const bPrefix = `${name}[${indexB}]` - const newFields = {} + const newFields: { [key: string]: any } = {} Object.keys(state.fields).forEach(key => { if (key.substring(0, aPrefix.length) === aPrefix) { const suffix = key.substring(aPrefix.length) @@ -40,4 +39,4 @@ const swap: Mutator = ( state.fields = newFields } -export default swap +export default swap \ No newline at end of file diff --git a/src/testUtils.test.ts b/src/testUtils.test.ts new file mode 100644 index 0000000..361f9bb --- /dev/null +++ b/src/testUtils.test.ts @@ -0,0 +1,37 @@ +import { createMockState, createMockTools } from './testUtils' + +describe('testUtils', () => { + describe('createMockState', () => { + it('should create a mock state object', () => { + const state = createMockState() + expect(state).toHaveProperty('fieldSubscribers') + expect(state).toHaveProperty('fields') + expect(state).toHaveProperty('formState') + expect(state.formState).toHaveProperty('values') + }) + }) + + describe('createMockTools', () => { + it('should create mock tools with default functions', () => { + const tools = createMockTools() + expect(tools.changeValue).toBeDefined() + expect(tools.getIn).toBeDefined() + expect(tools.setIn).toBeDefined() + expect(tools.shallowEqual).toBeDefined() + expect(tools.renameField).toBeDefined() + expect(tools.resetFieldState).toBeDefined() + }) + + it('should allow overriding specific tools', () => { + const customChangeValue = jest.fn() + const tools = createMockTools({ changeValue: customChangeValue }) + expect(tools.changeValue).toBe(customChangeValue) + expect(tools.getIn).toBeDefined() + }) + + it('should work with no overrides', () => { + const tools = createMockTools() + expect(typeof tools.changeValue).toBe('function') + }) + }) +}) \ No newline at end of file diff --git a/src/testUtils.ts b/src/testUtils.ts new file mode 100644 index 0000000..fcc4981 --- /dev/null +++ b/src/testUtils.ts @@ -0,0 +1,19 @@ +import { MutableState, Tools } from 'final-form' + +export const createMockState = (): MutableState => ({ + fieldSubscribers: {}, + fields: {}, + formState: { + values: {} + } +} as any) + +export const createMockTools = (overrides: Partial> = {}): Tools => ({ + changeValue: jest.fn(), + getIn: jest.fn(), + setIn: jest.fn(), + shallowEqual: jest.fn(), + renameField: jest.fn(), + resetFieldState: jest.fn(), + ...overrides +} as unknown as Tools) \ No newline at end of file diff --git a/src/unshift.js b/src/unshift.js deleted file mode 100644 index 62a99ac..0000000 --- a/src/unshift.js +++ /dev/null @@ -1,11 +0,0 @@ -// @flow -import type { MutableState, Mutator, Tools } from 'final-form' -import insert from './insert' - -const unshift: Mutator = ( - [name, value]: any[], - state: MutableState, - tools: Tools -) => insert([name, 0, value], state, tools) - -export default unshift diff --git a/src/unshift.test.js b/src/unshift.test.ts similarity index 83% rename from src/unshift.test.js rename to src/unshift.test.ts index 0dbf91b..06e2086 100644 --- a/src/unshift.test.js +++ b/src/unshift.test.ts @@ -1,59 +1,59 @@ import unshift from './unshift' -import { getIn, setIn } from 'final-form' +import { getIn, setIn, MutableState, Tools } from 'final-form' describe('unshift', () => { - const getOp = value => { + const getOp = (value: any) => { const changeValue = jest.fn() const resetFieldState = jest.fn() - const state = { + const state: MutableState = { formState: { values: { foo: ['one', 'two'] } - }, + } as any, fields: { 'foo[0]': { name: 'foo[0]', touched: true, error: 'First Error' - }, + } as any, 'foo[1]': { name: 'foo[1]', touched: false, error: 'Second Error' - } + } as any } - } - unshift(['foo', value], state, { changeValue, resetFieldState }) + } as any + unshift(['foo', value], state, { changeValue, resetFieldState } as unknown as Tools) return changeValue.mock.calls[0][2] } it('should call changeValue once', () => { const changeValue = jest.fn() const resetFieldState = jest.fn() - const state = { + const state: MutableState = { formState: { values: { foo: ['one', 'two'] } - }, + } as any, fields: { 'foo[0]': { name: 'foo[0]', touched: true, error: 'First Error' - }, + } as any, 'foo[1]': { name: 'foo[1]', touched: false, error: 'Second Error' - } + } as any } - } + } as any const result = unshift(['foo', 'bar'], state, { changeValue, resetFieldState - }) + } as unknown as Tools) expect(result).toBeUndefined() expect(changeValue).toHaveBeenCalled() expect(changeValue).toHaveBeenCalledTimes(1) @@ -73,42 +73,42 @@ describe('unshift', () => { it('should insert value to beginning of array', () => { const array = ['a', 'b', 'c'] // implementation of changeValue taken directly from Final Form - const changeValue = (state, name, mutate) => { + const changeValue = (state: any, name: string, mutate: (value: any) => any) => { const before = getIn(state.formState.values, name) const after = mutate(before) state.formState.values = setIn(state.formState.values, name, after) || {} } - const resetFieldState = name => { + const resetFieldState = (name: string) => { state.fields[name].touched = false } - const state = { + const state: MutableState = { formState: { values: { foo: array } - }, + } as any, fields: { 'foo[0]': { name: 'foo[0]', touched: true, error: 'A Error' - }, + } as any, 'foo[1]': { name: 'foo[1]', touched: false, error: 'B Error' - }, + } as any, 'foo[2]': { name: 'foo[2]', touched: true, error: 'C Error' - } + } as any } - } + } as any const returnValue = unshift(['foo', 'NEWVALUE'], state, { changeValue, resetFieldState - }) + } as unknown as Tools) expect(returnValue).toBeUndefined() expect(state.formState.values.foo).not.toBe(array) // copied expect(state).toEqual({ @@ -139,4 +139,4 @@ describe('unshift', () => { } }) }) -}) +}) \ No newline at end of file diff --git a/src/unshift.ts b/src/unshift.ts new file mode 100644 index 0000000..f575849 --- /dev/null +++ b/src/unshift.ts @@ -0,0 +1,10 @@ +import { MutableState, Mutator, Tools } from 'final-form' +import insert from './insert' + +const unshift: Mutator = ( + [name, value]: any[], + state: MutableState, + tools: Tools +): void => insert([name, 0, value], state, tools) + +export default unshift \ No newline at end of file diff --git a/src/update.test.js b/src/update.test.ts similarity index 68% rename from src/update.test.js rename to src/update.test.ts index 0084e1b..cd444ea 100644 --- a/src/update.test.js +++ b/src/update.test.ts @@ -1,16 +1,30 @@ import update from './update' +import { MutableState, Tools } from 'final-form' describe('update', () => { - const getOp = (index, value) => { + const getOp = (index: number, value: any) => { const changeValue = jest.fn() - update(['foo', index, value], {}, { changeValue }) + const mockState: MutableState = { + fieldSubscribers: {}, + fields: {}, + formState: { + values: {} + } + } as any + update(['foo', index, value], mockState, { changeValue } as unknown as Tools) return changeValue.mock.calls[0][2] } it('should call changeValue once', () => { const changeValue = jest.fn() - const state = {} - const result = update(['foo', 0, 'bar'], state, { changeValue }) + const state: MutableState = { + fieldSubscribers: {}, + fields: {}, + formState: { + values: {} + } + } as any + const result = update(['foo', 0, 'bar'], state, { changeValue } as unknown as Tools) expect(result).toBeUndefined() expect(changeValue).toHaveBeenCalled() expect(changeValue).toHaveBeenCalledTimes(1) @@ -35,4 +49,4 @@ describe('update', () => { expect(Array.isArray(result)).toBe(true) expect(result).toEqual(['a', 'd', 'c']) }) -}) +}) \ No newline at end of file diff --git a/src/update.js b/src/update.ts similarity index 57% rename from src/update.js rename to src/update.ts index dfbc5db..df77bdd 100644 --- a/src/update.js +++ b/src/update.ts @@ -1,16 +1,15 @@ -// @flow -import type { MutableState, Mutator, Tools } from 'final-form' +import { MutableState, Mutator, Tools } from 'final-form' const update: Mutator = ( [name, index, value]: any[], state: MutableState, { changeValue }: Tools -) => { - changeValue(state, name, (array: ?(any[])): any[] => { +): void => { + changeValue(state, name, (array?: any[]): any[] => { const copy = [...(array || [])] copy.splice(index, 1, value) return copy }) } -export default update +export default update \ No newline at end of file diff --git a/src/utils.js b/src/utils.ts similarity index 88% rename from src/utils.js rename to src/utils.ts index 592f664..e1294a8 100644 --- a/src/utils.js +++ b/src/utils.ts @@ -1,5 +1,3 @@ -// @flow - // From MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Escaping export const escapeRegexTokens = (string: string): string => - string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') // $& means the whole matched string + string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') // $& means the whole matched string \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index da0f67f..63bce14 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,26 @@ { "compilerOptions": { - "baseUrl": ".", - "noEmit": true, - "strict": true + "target": "ES2018", + "module": "ESNext", + "lib": ["ES2018"], + "declaration": true, + "declarationMap": true, + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "esModuleInterop": true, + "skipLibCheck": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "types": ["jest"] }, - "include": ["./src/**/*"] + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "**/*.test.ts"] } From 06af67be7fbe89f2f7e18836bdb6cacf15c01589 Mon Sep 17 00:00:00 2001 From: Erik Rasmussen Date: Mon, 26 May 2025 09:51:29 +0200 Subject: [PATCH 2/2] CI --- .github/workflows/ci.yml | 51 ++++++++++++++++++++++++++++++++++++++ .github/workflows/lock.yml | 24 ++++++++++++++++++ package-scripts.js | 4 +++ src/concat.test.ts | 7 +++--- src/copyField.ts | 6 +++-- src/insert.test.ts | 20 +++++---------- src/insert.ts | 2 +- src/move.test.ts | 38 +++++++++++++++------------- src/pop.test.ts | 15 +++++------ src/pop.ts | 2 +- src/push.test.ts | 6 +++-- src/remove.test.ts | 29 ++++++++-------------- src/removeBatch.test.ts | 15 +++++------ src/shift.test.ts | 9 ++++--- src/swap.test.ts | 15 +++++------ src/unshift.test.ts | 15 ++++------- src/update.test.ts | 7 +++--- 17 files changed, 168 insertions(+), 97 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/lock.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..db54c7c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,51 @@ +name: CI + +on: [push] + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js 22 + uses: actions/setup-node@v2 + with: + node-version: "22" + - name: Prepare env + run: yarn install --ignore-scripts --frozen-lockfile + - name: Run linter + run: yarn start lint + + prettier: + name: Prettier Check + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js 22 + uses: actions/setup-node@v2 + with: + node-version: "22" + - name: Prepare env + run: yarn install --ignore-scripts --frozen-lockfile + - name: Run prettier + run: yarn start prettier + + test: + name: Unit Tests + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js 22 + uses: actions/setup-node@v2 + with: + node-version: "22" + - name: Prepare env + run: yarn install --ignore-scripts --frozen-lockfile + - name: Run unit tests + run: yarn start test + - name: Run code coverage + uses: codecov/codecov-action@v2.1.0 diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml new file mode 100644 index 0000000..4e08172 --- /dev/null +++ b/.github/workflows/lock.yml @@ -0,0 +1,24 @@ +name: "Lock Threads" + +on: + schedule: + - cron: "0 * * * *" + workflow_dispatch: + +permissions: + issues: write + pull-requests: write + +concurrency: + group: lock + +jobs: + action: + runs-on: ubuntu-latest + steps: + - uses: dessant/lock-threads@v3 + with: + issue-inactive-days: "365" + issue-lock-reason: "resolved" + pr-inactive-days: "365" + pr-lock-reason: "resolved" diff --git a/package-scripts.js b/package-scripts.js index 92b74c2..241ce9b 100644 --- a/package-scripts.js +++ b/package-scripts.js @@ -57,6 +57,10 @@ module.exports = { description: 'Generates table of contents in README', script: 'doctoc README.md' }, + prettier: { + description: 'Runs prettier on everything', + script: 'prettier --write "**/*.([jt]s*)"' + }, copyTypes: series('tsc --declaration --emitDeclarationOnly --outDir dist'), lint: { description: 'lint the entire project', diff --git a/src/concat.test.ts b/src/concat.test.ts index 9a20349..bd093c1 100644 --- a/src/concat.test.ts +++ b/src/concat.test.ts @@ -1,5 +1,6 @@ import concat from './concat' -import { MutableState, Tools } from 'final-form' +import { MutableState } from 'final-form' +import { createMockTools } from './testUtils' describe('concat', () => { const getOp = (value: any) => { @@ -11,7 +12,7 @@ describe('concat', () => { values: {} } } as any - concat(['foo', value], mockState, { changeValue } as unknown as Tools) + concat(['foo', value], mockState, createMockTools({ changeValue })) return changeValue.mock.calls[0][2] } @@ -24,7 +25,7 @@ describe('concat', () => { values: {} } } as any - const result = concat(['foo', ['bar', 'baz']], state, { changeValue } as unknown as Tools) + const result = concat(['foo', ['bar', 'baz']], state, createMockTools({ changeValue })) expect(result).toBeUndefined() expect(changeValue).toHaveBeenCalled() expect(changeValue).toHaveBeenCalledTimes(1) diff --git a/src/copyField.ts b/src/copyField.ts index d20a490..35abbf5 100644 --- a/src/copyField.ts +++ b/src/copyField.ts @@ -1,7 +1,9 @@ +import { InternalFieldState } from 'final-form' + function copyField( - oldFields: { [key: string]: any }, + oldFields: { [key: string]: InternalFieldState }, oldKey: string, - newFields: { [key: string]: any }, + newFields: { [key: string]: Partial> }, newKey: string ): void { newFields[newKey] = { diff --git a/src/insert.test.ts b/src/insert.test.ts index 5bae632..024a283 100644 --- a/src/insert.test.ts +++ b/src/insert.test.ts @@ -1,5 +1,6 @@ import insert from './insert' -import { getIn, setIn, MutableState, Tools } from 'final-form' +import { getIn, setIn, MutableState } from 'final-form' +import { createMockTools } from './testUtils' describe('insert', () => { const getOp = (index, value: any) => { @@ -24,7 +25,7 @@ describe('insert', () => { } as any } } - insert(['foo', index, value], state, { changeValue, resetFieldState } as unknown as Tools) + insert(['foo', index, value], state, createMockTools({ changeValue, resetFieldState })) return changeValue.mock.calls[0][2] } @@ -54,10 +55,7 @@ describe('insert', () => { } } } - const result = insert(['foo', 0, 'bar'], state, { - changeValue, - resetFieldState - } as unknown as Tools) + const result = insert(['foo', 0, 'bar'], state, createMockTools({ changeValue, resetFieldState })) expect(result).toBeUndefined() expect(changeValue).toHaveBeenCalled() expect(changeValue).toHaveBeenCalledTimes(1) @@ -123,10 +121,7 @@ describe('insert', () => { } as any } } - const returnValue = insert(['foo', 1, 'NEWVALUE'], state, { - changeValue, - resetFieldState - } as unknown as Tools) + const returnValue = insert(['foo', 1, 'NEWVALUE'], state, createMockTools({ changeValue, resetFieldState })) expect(returnValue).toBeUndefined() expect(state.formState.values.foo).not.toBe(array) // copied expect(state).toEqual({ @@ -216,10 +211,7 @@ describe('insert', () => { } as any } } - const returnValue = insert(['foo[0]', 1, 'NEWVALUE'], state, { - changeValue, - resetFieldState - } as unknown as Tools) + const returnValue = insert(['foo[0]', 1, 'NEWVALUE'], state, createMockTools({ changeValue, resetFieldState })) expect(returnValue).toBeUndefined() expect(state.formState.values.foo).not.toBe(array) // copied expect(state).toEqual({ diff --git a/src/insert.ts b/src/insert.ts index 46464bf..1d1b823 100644 --- a/src/insert.ts +++ b/src/insert.ts @@ -13,7 +13,7 @@ const insert: Mutator = ( return copy }) - // now we have increment any higher indexes + // now increment any higher indices const pattern = new RegExp(`^${escapeRegexTokens(name)}\\[(\\d+)\\](.*)`) const newFields: { [key: string]: any } = {} Object.keys(state.fields).forEach(key => { diff --git a/src/move.test.ts b/src/move.test.ts index 51db272..353f560 100644 --- a/src/move.test.ts +++ b/src/move.test.ts @@ -1,24 +1,28 @@ import move from './move' -import { getIn, setIn, MutableState, Tools } from 'final-form' +import { getIn, setIn, MutableState } from 'final-form' +import { createMockTools } from './testUtils' describe('move', () => { - const getOp = (from, to: any) => { + const getOp = (from: any, to: any) => { const changeValue = jest.fn() - move(['foo', from, to], { fields: {} }, { changeValue } as unknown as Tools) + const mockTools = createMockTools({ changeValue }) + move(['foo', from, to], { fields: {} } as any, mockTools) return changeValue.mock.calls[0][2] } it('should do nothing if from and to are equal', () => { const changeValue = jest.fn() - const result = move(['foo', 1, 1], { fields: {} }, { changeValue } as unknown as Tools) + const mockTools = createMockTools({ changeValue }) + const result = move(['foo', 1, 1], { fields: {} } as any, mockTools) expect(result).toBeUndefined() expect(changeValue).not.toHaveBeenCalled() }) it('should call changeValue once', () => { const changeValue = jest.fn() - const state: MutableState = { fields: {} } - const result = move(['foo', 0, 2], state, { changeValue } as unknown as Tools) + const state: MutableState = { fields: {} } as any + const mockTools = createMockTools({ changeValue }) + const result = move(['foo', 0, 2], state, mockTools) expect(result).toBeUndefined() expect(changeValue).toHaveBeenCalled() expect(changeValue).toHaveBeenCalledTimes(1) @@ -85,7 +89,7 @@ describe('move', () => { } } } - move(['foo', 0, 2], state, { changeValue } as unknown as Tools) + move(['foo', 0, 2], state, createMockTools({ changeValue })) expect(state).toEqual({ formState: { values: { @@ -161,7 +165,7 @@ describe('move', () => { } } } - move(['foo', 2, 0], state, { changeValue } as unknown as Tools) + move(['foo', 2, 0], state, createMockTools({ changeValue })) expect(state).toEqual({ formState: { values: { @@ -258,7 +262,7 @@ describe('move', () => { } as any } } - move(['foo', 0, 2], state, { changeValue } as unknown as Tools) + move(['foo', 0, 2], state, createMockTools({ changeValue })) expect(state).toMatchObject({ formState: { values: { @@ -379,7 +383,7 @@ describe('move', () => { } as any } } - move(['foo', 2, 0], state, { changeValue } as unknown as Tools) + move(['foo', 2, 0], state, createMockTools({ changeValue })) expect(state).toMatchObject({ formState: { values: { @@ -471,7 +475,7 @@ describe('move', () => { } as any } } - move(['foo', 0, 1], state, { changeValue } as unknown as Tools) + move(['foo', 0, 1], state, createMockTools({ changeValue })) expect(state).toMatchObject({ formState: { values: { @@ -510,8 +514,8 @@ describe('move', () => { const state: MutableState = { formState: { values: { - foo: [{ dog: 'apple dog', cat: 'apple cat', colors: [{ name: 'red'}, { name: 'blue'}], deep: { inside: { rock: 'black'}} }, - { dog: 'banana dog', mouse: 'mickey', deep: { inside: { axe: 'golden' }} }] + foo: [{ dog: 'apple dog', cat: 'apple cat', colors: [{ name: 'red' }, { name: 'blue' }], deep: { inside: { rock: 'black' } } }, + { dog: 'banana dog', mouse: 'mickey', deep: { inside: { axe: 'golden' } } }] } }, fields: { @@ -557,12 +561,12 @@ describe('move', () => { } as any, } } - move(['foo', 0, 1], state, { changeValue } as unknown as Tools) + move(['foo', 0, 1], state, createMockTools({ changeValue })) expect(state).toMatchObject({ formState: { values: { - foo: [{ dog: 'banana dog', mouse: 'mickey', deep: { inside: { axe: 'golden' }} }, - { dog: 'apple dog', cat: 'apple cat', colors: [{ name: 'red'}, { name: 'blue'}], deep: { inside: { rock: 'black'}} }] + foo: [{ dog: 'banana dog', mouse: 'mickey', deep: { inside: { axe: 'golden' } } }, + { dog: 'apple dog', cat: 'apple cat', colors: [{ name: 'red' }, { name: 'blue' }], deep: { inside: { rock: 'black' } } }] } }, fields: { @@ -660,7 +664,7 @@ describe('move', () => { } } } - move(['foo', 0, 2], state, { changeValue } as unknown as Tools) + move(['foo', 0, 2], state, createMockTools({ changeValue })) expect(state.fields['foo[0]'].change()).toBe('foo[0]') expect(state.fields['foo[1]'].change()).toBe('foo[1]') expect(state.fields['foo[2]'].change()).toBe('foo[2]') diff --git a/src/pop.test.ts b/src/pop.test.ts index d6659f6..b1a320f 100644 --- a/src/pop.test.ts +++ b/src/pop.test.ts @@ -1,5 +1,6 @@ import pop from './pop' -import { getIn, setIn, MutableState, Tools } from 'final-form' +import { getIn, setIn, MutableState } from 'final-form' +import { createMockTools } from './testUtils' describe('pop', () => { it('should call changeValue once', () => { @@ -23,7 +24,7 @@ describe('pop', () => { } as any } } - const result = pop(['foo'], state, { changeValue, getIn, setIn } as unknown as Tools) + const result = pop(['foo'], state, createMockTools({ changeValue, getIn, setIn })) expect(result).toBeUndefined() expect(changeValue).toHaveBeenCalled() expect(changeValue).toHaveBeenCalledTimes(1) @@ -47,7 +48,7 @@ describe('pop', () => { }, fields: {} } as any - const returnValue = pop(['foo'], state, { changeValue, getIn, setIn } as unknown as Tools) + const returnValue = pop(['foo'], state, createMockTools({ changeValue, getIn, setIn })) expect(returnValue).toBeUndefined() const result = state.formState.foo expect(result).toBeUndefined() @@ -68,7 +69,7 @@ describe('pop', () => { }, fields: {} } as any - const returnValue = pop(['foo'], state, { changeValue, getIn, setIn } as unknown as Tools) + const returnValue = pop(['foo'], state, createMockTools({ changeValue, getIn, setIn })) expect(returnValue).toBeUndefined() const result = state.formState.values.foo expect(Array.isArray(result)).toBe(true) @@ -101,7 +102,7 @@ describe('pop', () => { } as any } } - const returnValue = pop(['foo'], state, { changeValue, getIn, setIn } as unknown as Tools) + const returnValue = pop(['foo'], state, createMockTools({ changeValue, getIn, setIn })) const result = state.formState.values.foo expect(returnValue).toBe('c') expect(Array.isArray(result)).toBe(true) @@ -150,7 +151,7 @@ describe('pop', () => { } } } - const returnValue = pop(['foo'], state, { changeValue, getIn, setIn } as unknown as Tools) + const returnValue = pop(['foo'], state, createMockTools({ changeValue, getIn, setIn })) expect(returnValue).toBe('d') expect(Array.isArray(state.formState.values.foo)).toBe(true) expect(state.formState.values.foo).not.toBe(array) // copied @@ -227,7 +228,7 @@ describe('pop', () => { } } } - const returnValue = pop(['foo[0]'], state, { changeValue, getIn, setIn } as unknown as Tools) + const returnValue = pop(['foo[0]'], state, createMockTools({ changeValue, getIn, setIn })) expect(returnValue).toBe('d') expect(Array.isArray(state.formState.values.foo)).toBe(true) expect(state.formState.values.foo).not.toBe(array) // copied diff --git a/src/pop.ts b/src/pop.ts index a4d864c..0767ada 100644 --- a/src/pop.ts +++ b/src/pop.ts @@ -6,7 +6,7 @@ const pop: Mutator = ( state: MutableState, tools: Tools ): any => { - const { getIn } = tools + const { getIn } = tools; const array = getIn(state.formState.values, name) return array && array.length > 0 ? remove([name, array.length - 1], state, tools) diff --git a/src/push.test.ts b/src/push.test.ts index 891629b..218a83d 100644 --- a/src/push.test.ts +++ b/src/push.test.ts @@ -5,14 +5,16 @@ describe('push', () => { const getOp = (value: any) => { const changeValue = jest.fn() const mockState = createMockState() - push(['foo', value], mockState, createMockTools({ changeValue })) + const mockTools = createMockTools({ changeValue }) + push(['foo', value], mockState, mockTools) return changeValue.mock.calls[0][2] } it('should call changeValue once', () => { const changeValue = jest.fn() const state = createMockState() - const result = push(['foo', 'bar'], state, createMockTools({ changeValue })) + const tools = createMockTools({ changeValue }) + const result = push(['foo', 'bar'], state, tools) expect(result).toBeUndefined() expect(changeValue).toHaveBeenCalled() expect(changeValue).toHaveBeenCalledTimes(1) diff --git a/src/remove.test.ts b/src/remove.test.ts index ff3fbc4..8f5a213 100644 --- a/src/remove.test.ts +++ b/src/remove.test.ts @@ -1,5 +1,6 @@ import remove from './remove' -import { getIn, setIn, MutableState, Tools } from 'final-form' +import { getIn, setIn, MutableState } from 'final-form' +import { createMockTools } from './testUtils' describe('remove', () => { it('should call changeValue once', () => { @@ -23,7 +24,7 @@ describe('remove', () => { } as any } } - const result = remove(['foo', 0], state, { changeValue, getIn, setIn } as unknown as Tools) + const result = remove(['foo', 0], state, createMockTools({ changeValue, getIn, setIn })) expect(result).toBeUndefined() expect(changeValue).toHaveBeenCalled() expect(changeValue).toHaveBeenCalledTimes(1) @@ -42,7 +43,7 @@ describe('remove', () => { }, fields: {} } as any - const returnValue = remove(['foo', 1], state, { changeValue, getIn, setIn } as unknown as Tools) + const returnValue = remove(['foo', 1], state, createMockTools({ changeValue, getIn, setIn })) expect(returnValue).toBeUndefined() const op = changeValue.mock.calls[0][2] const result = op(undefined) @@ -59,7 +60,7 @@ describe('remove', () => { }, fields: {} } as any - remove(['foo', 0], state, { changeValue, getIn, setIn } as unknown as Tools) + remove(['foo', 0], state, createMockTools({ changeValue, getIn, setIn })) const op = changeValue.mock.calls[0][2] const result = op(['only']) expect(result).toBeUndefined() @@ -131,7 +132,7 @@ describe('remove', () => { } } } - const returnValue = remove(['foo', 1], state, { changeValue, getIn, setIn } as unknown as Tools) + const returnValue = remove(['foo', 1], state, createMockTools({ changeValue, getIn, setIn })) expect(returnValue).toBe('b') expect(state.formState.values.foo).not.toBe(array) // copied expect(state).toEqual({ @@ -242,11 +243,11 @@ describe('remove', () => { } } } - const returnValue = remove(['foo[0]', 1], state, { + const returnValue = remove(['foo[0]', 1], state, createMockTools({ changeValue, getIn, setIn - } as unknown as Tools) + })) expect(returnValue).toBe('b') expect(state.formState.values.foo).not.toBe(array) // copied expect(state).toEqual({ @@ -358,12 +359,7 @@ describe('remove', () => { } } as any - const returnValue = remove(['foo', 0], state, { - renameField, - changeValue, - getIn, - setIn - } as unknown as Tools) + const returnValue = remove(['foo', 0], state, createMockTools({ renameField, changeValue, getIn, setIn })) expect(returnValue).toBeUndefined() expect(getIn(state, 'formState.submitErrors')).toEqual({ foo: [] }) }) @@ -438,12 +434,7 @@ describe('remove', () => { } } as any - const returnValue = remove(['foo', 0], state, { - renameField, - changeValue, - getIn, - setIn - } as unknown as Tools) + const returnValue = remove(['foo', 0], state, createMockTools({ renameField, changeValue, getIn, setIn })) expect(returnValue).toBeUndefined() expect(getIn(state, 'formState.submitErrors')).toEqual({ foo: [{ key: 'B Submit Error' }] diff --git a/src/removeBatch.test.ts b/src/removeBatch.test.ts index 46a7b27..f444964 100644 --- a/src/removeBatch.test.ts +++ b/src/removeBatch.test.ts @@ -1,5 +1,6 @@ import removeBatch from './removeBatch' -import { getIn, setIn, MutableState, Tools } from 'final-form' +import { getIn, setIn, MutableState } from 'final-form' +import { createMockTools } from './testUtils' describe('removeBatch', () => { const getOp = value => { @@ -23,7 +24,7 @@ describe('removeBatch', () => { } as any } } - removeBatch(['foo', value], state, { changeValue } as unknown as Tools) + removeBatch(['foo', value], state, createMockTools({ changeValue })) return changeValue.mock.calls[0][2] } @@ -76,7 +77,7 @@ describe('removeBatch', () => { } as any } } - const result = removeBatch(['foo', [1, 2]], state, { changeValue } as unknown as Tools) + const result = removeBatch(['foo', [1, 2]], state, createMockTools({ changeValue })) expect(Array.isArray(result)).toBe(true) expect(result).toEqual(['two', 'three']) expect(changeValue).toHaveBeenCalled() @@ -153,7 +154,7 @@ describe('removeBatch', () => { } as any } } - const result = removeBatch(['foo', [2, 0]], state, { changeValue } as unknown as Tools) + const result = removeBatch(['foo', [2, 0]], state, createMockTools({ changeValue })) expect(Array.isArray(result)).toBe(true) expect(result).toEqual(['three', 'one']) expect(changeValue).toHaveBeenCalled() @@ -210,7 +211,7 @@ describe('removeBatch', () => { } } const changeValue = jest.fn() - const returnValue = removeBatch(['foo[0]', []], state, { changeValue } as unknown as Tools) + const returnValue = removeBatch(['foo[0]', []], state, createMockTools({ changeValue })) expect(returnValue).toEqual([]) expect(state.formState.values.foo).toBe(array) // no change expect(state).toEqual({ @@ -323,7 +324,7 @@ describe('removeBatch', () => { } } } - const returnValue = removeBatch(['foo', [1, 3]], state, { changeValue } as unknown as Tools) + const returnValue = removeBatch(['foo', [1, 3]], state, createMockTools({ changeValue })) expect(returnValue).toEqual(['b', 'd']) expect(state.formState.values.foo).not.toBe(array) // copied expect(state).toEqual({ @@ -446,7 +447,7 @@ describe('removeBatch', () => { } } } - const returnValue = removeBatch(['foo[0]', [1, 3]], state, { changeValue } as unknown as Tools) + const returnValue = removeBatch(['foo[0]', [1, 3]], state, createMockTools({ changeValue })) expect(returnValue).toEqual(['b', 'd']) expect(state.formState.values.foo).not.toBe(array) // copied expect(state).toEqual({ diff --git a/src/shift.test.ts b/src/shift.test.ts index fc1ef2b..590218e 100644 --- a/src/shift.test.ts +++ b/src/shift.test.ts @@ -1,5 +1,6 @@ import shift from './shift' -import { getIn, setIn, MutableState, Tools } from 'final-form' +import { getIn, setIn, MutableState } from 'final-form' +import { createMockTools } from './testUtils' describe('shift', () => { it('should call changeValue once', () => { @@ -23,7 +24,7 @@ describe('shift', () => { } as any } } as any - const result = shift(['foo'], state, { changeValue, getIn, setIn } as unknown as Tools) + const result = shift(['foo'], state, createMockTools({ changeValue, getIn, setIn })) expect(result).toBeUndefined() expect(changeValue).toHaveBeenCalled() expect(changeValue).toHaveBeenCalledTimes(1) @@ -42,7 +43,7 @@ describe('shift', () => { } as any, fields: {} } as any - const returnValue = shift(['foo'], state, { changeValue, getIn, setIn } as unknown as Tools) + const returnValue = shift(['foo'], state, createMockTools({ changeValue, getIn, setIn })) expect(returnValue).toBeUndefined() const op = changeValue.mock.calls[0][2] const result = op(undefined) @@ -91,7 +92,7 @@ describe('shift', () => { } as any } } as any - const returnValue = shift(['foo'], state, { changeValue, getIn, setIn } as unknown as Tools) + const returnValue = shift(['foo'], state, createMockTools({ changeValue, getIn, setIn })) expect(returnValue).toBe('a') expect(state.formState.values.foo).not.toBe(array) // copied expect(state).toEqual({ diff --git a/src/swap.test.ts b/src/swap.test.ts index a2fc4cb..2314a1b 100644 --- a/src/swap.test.ts +++ b/src/swap.test.ts @@ -1,16 +1,17 @@ import swap from './swap' -import { getIn, setIn, MutableState, Tools } from 'final-form' +import { getIn, setIn, MutableState } from 'final-form' +import { createMockTools } from './testUtils' describe('swap', () => { const getOp = (from, to: any) => { const changeValue = jest.fn() - swap(['foo', from, to], { fields: {} }, { changeValue } as unknown as Tools) + swap(['foo', from, to], { fields: {} }, createMockTools({ changeValue })) return changeValue.mock.calls[0][2] } it('should do nothing if indexA and indexB are equal', () => { const changeValue = jest.fn() - const result = swap(['foo', 1, 1], { fields: {} }, { changeValue } as unknown as Tools) + const result = swap(['foo', 1, 1], { fields: {} }, createMockTools({ changeValue })) expect(result).toBeUndefined() expect(changeValue).not.toHaveBeenCalled() }) @@ -18,7 +19,7 @@ describe('swap', () => { it('should call changeValue once', () => { const changeValue = jest.fn() const state: MutableState = { fields: {} } - const result = swap(['foo', 0, 2], state, { changeValue } as unknown as Tools) + const result = swap(['foo', 0, 2], state, createMockTools({ changeValue })) expect(result).toBeUndefined() expect(changeValue).toHaveBeenCalled() expect(changeValue).toHaveBeenCalledTimes(1) @@ -85,7 +86,7 @@ describe('swap', () => { } } } - swap(['foo', 0, 2], state, { changeValue } as unknown as Tools) + swap(['foo', 0, 2], state, createMockTools({ changeValue })) expect(state).toEqual({ formState: { values: { @@ -202,7 +203,7 @@ describe('swap', () => { } } } - swap(['foo', 0, 2], state, { changeValue } as unknown as Tools) + swap(['foo', 0, 2], state, createMockTools({ changeValue })) expect(state).toEqual({ formState: { values: { @@ -323,7 +324,7 @@ describe('swap', () => { } } } - swap(['foo', 0, 2], state, { changeValue } as unknown as Tools) + swap(['foo', 0, 2], state, createMockTools({ changeValue })) expect(state.fields['foo[0]'].change()).toBe('foo[0]') expect(state.fields['foo[1]'].change()).toBe('foo[1]') expect(state.fields['foo[2]'].change()).toBe('foo[2]') diff --git a/src/unshift.test.ts b/src/unshift.test.ts index 06e2086..1b57cb2 100644 --- a/src/unshift.test.ts +++ b/src/unshift.test.ts @@ -1,5 +1,6 @@ import unshift from './unshift' -import { getIn, setIn, MutableState, Tools } from 'final-form' +import { getIn, setIn, MutableState } from 'final-form' +import { createMockTools } from './testUtils' describe('unshift', () => { const getOp = (value: any) => { @@ -24,7 +25,7 @@ describe('unshift', () => { } as any } } as any - unshift(['foo', value], state, { changeValue, resetFieldState } as unknown as Tools) + unshift(['foo', value], state, createMockTools({ changeValue, resetFieldState })) return changeValue.mock.calls[0][2] } @@ -50,10 +51,7 @@ describe('unshift', () => { } as any } } as any - const result = unshift(['foo', 'bar'], state, { - changeValue, - resetFieldState - } as unknown as Tools) + const result = unshift(['foo', 'bar'], state, createMockTools({ changeValue, resetFieldState })) expect(result).toBeUndefined() expect(changeValue).toHaveBeenCalled() expect(changeValue).toHaveBeenCalledTimes(1) @@ -105,10 +103,7 @@ describe('unshift', () => { } as any } } as any - const returnValue = unshift(['foo', 'NEWVALUE'], state, { - changeValue, - resetFieldState - } as unknown as Tools) + const returnValue = unshift(['foo', 'NEWVALUE'], state, createMockTools({ changeValue, resetFieldState })) expect(returnValue).toBeUndefined() expect(state.formState.values.foo).not.toBe(array) // copied expect(state).toEqual({ diff --git a/src/update.test.ts b/src/update.test.ts index cd444ea..5419eab 100644 --- a/src/update.test.ts +++ b/src/update.test.ts @@ -1,5 +1,6 @@ import update from './update' -import { MutableState, Tools } from 'final-form' +import { MutableState } from 'final-form' +import { createMockTools } from './testUtils' describe('update', () => { const getOp = (index: number, value: any) => { @@ -11,7 +12,7 @@ describe('update', () => { values: {} } } as any - update(['foo', index, value], mockState, { changeValue } as unknown as Tools) + update(['foo', index, value], mockState, createMockTools({ changeValue })) return changeValue.mock.calls[0][2] } @@ -24,7 +25,7 @@ describe('update', () => { values: {} } } as any - const result = update(['foo', 0, 'bar'], state, { changeValue } as unknown as Tools) + const result = update(['foo', 0, 'bar'], state, createMockTools({ changeValue })) expect(result).toBeUndefined() expect(changeValue).toHaveBeenCalled() expect(changeValue).toHaveBeenCalledTimes(1)