feat: image dimension lock logic update (#34)
This commit is contained in:
@@ -14,7 +14,7 @@ import hooks from './hooks';
|
||||
|
||||
/**
|
||||
* Wrapper for image dimension inputs and the lock checkbox.
|
||||
* @param {obj} locked - locked dimension object
|
||||
* @param {bool} isLocked - are dimensions locked
|
||||
* @param {func} lock - lock dimensions
|
||||
* @param {func} setHeight - updates dimensions based on new height
|
||||
* @param {func} setWidth - updates dimensions based on new width
|
||||
@@ -23,7 +23,7 @@ import hooks from './hooks';
|
||||
* @param {obj} value - local dimension values { height, width }
|
||||
*/
|
||||
export const DimensionControls = ({
|
||||
locked,
|
||||
isLocked,
|
||||
lock,
|
||||
setHeight,
|
||||
setWidth,
|
||||
@@ -54,16 +54,15 @@ export const DimensionControls = ({
|
||||
/>
|
||||
<IconButton
|
||||
className="d-inline-block"
|
||||
alt={locked ? 'unlock dimensions' : 'lock dimensions'}
|
||||
alt={isLocked ? 'unlock dimensions' : 'lock dimensions'}
|
||||
iconAs={Icon}
|
||||
src={locked ? Locked : Unlocked}
|
||||
onClick={locked ? unlock : lock}
|
||||
src={isLocked ? Locked : Unlocked}
|
||||
onClick={isLocked ? unlock : lock}
|
||||
/>
|
||||
</div>
|
||||
</Form.Group>
|
||||
));
|
||||
DimensionControls.defaultProps = {
|
||||
locked: null,
|
||||
value: {
|
||||
height: 100,
|
||||
width: 100,
|
||||
@@ -76,10 +75,7 @@ DimensionControls.propTypes = ({
|
||||
}),
|
||||
setHeight: PropTypes.func.isRequired,
|
||||
setWidth: PropTypes.func.isRequired,
|
||||
locked: PropTypes.shape({
|
||||
width: PropTypes.number,
|
||||
height: PropTypes.number,
|
||||
}),
|
||||
isLocked: PropTypes.bool.isRequired,
|
||||
lock: PropTypes.func.isRequired,
|
||||
unlock: PropTypes.func.isRequired,
|
||||
updateDimensions: PropTypes.func.isRequired,
|
||||
|
||||
@@ -8,7 +8,9 @@ jest.mock('./hooks', () => ({
|
||||
|
||||
describe('DimensionControls', () => {
|
||||
const props = {
|
||||
lockDims: { width: 12, height: 15 },
|
||||
locked: { 'props.locked': 'lockedValue' },
|
||||
isLocked: true,
|
||||
value: { width: 20, height: 40 },
|
||||
};
|
||||
beforeEach(() => {
|
||||
@@ -27,5 +29,9 @@ describe('DimensionControls', () => {
|
||||
expect(el).toMatchSnapshot();
|
||||
expect(el.isEmptyRender()).toEqual(true);
|
||||
});
|
||||
test('unlocked dimensions', () => {
|
||||
const el = shallow(<DimensionControls {...props} isLocked={false} />);
|
||||
expect(el).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -48,3 +48,50 @@ exports[`DimensionControls render snapshot 1`] = `
|
||||
</div>
|
||||
</Form.Group>
|
||||
`;
|
||||
|
||||
exports[`DimensionControls render unlocked dimensions 1`] = `
|
||||
<Form.Group>
|
||||
<Form.Label
|
||||
as="h4"
|
||||
>
|
||||
Image Dimensions
|
||||
</Form.Label>
|
||||
<div
|
||||
className="mt-4.5"
|
||||
>
|
||||
<Form.Control
|
||||
className="dimension-input"
|
||||
floatingLabel="Width"
|
||||
min={1}
|
||||
onBlur={[MockFunction props.updateDimensions]}
|
||||
onChange={
|
||||
Object {
|
||||
"hooks.onInputChange": [MockFunction props.setWidth],
|
||||
}
|
||||
}
|
||||
type="number"
|
||||
value={20}
|
||||
/>
|
||||
<Form.Control
|
||||
className="dimension-input"
|
||||
floatingLabel="Height"
|
||||
min={1}
|
||||
onBlur={[MockFunction props.updateDimensions]}
|
||||
onChange={
|
||||
Object {
|
||||
"hooks.onInputChange": [MockFunction props.setHeight],
|
||||
}
|
||||
}
|
||||
type="number"
|
||||
value={40}
|
||||
/>
|
||||
<IconButton
|
||||
alt="lock dimensions"
|
||||
className="d-inline-block"
|
||||
iconAs="Icon"
|
||||
onClick={[MockFunction props.lock]}
|
||||
src={[MockFunction icons.Unlocked]}
|
||||
/>
|
||||
</div>
|
||||
</Form.Group>
|
||||
`;
|
||||
|
||||
@@ -5,12 +5,13 @@ import * as module from './hooks';
|
||||
|
||||
// Simple wrappers for useState to allow easy mocking for tests.
|
||||
export const state = {
|
||||
dimensions: (val) => React.useState(val),
|
||||
locked: (val) => React.useState(val),
|
||||
local: (val) => React.useState(val),
|
||||
lockInitialized: (val) => React.useState(val),
|
||||
altText: (val) => React.useState(val),
|
||||
dimensions: (val) => React.useState(val),
|
||||
isDecorative: (val) => React.useState(val),
|
||||
isLocked: (val) => React.useState(val),
|
||||
local: (val) => React.useState(val),
|
||||
lockDims: (val) => React.useState(val),
|
||||
lockInitialized: (val) => React.useState(val),
|
||||
};
|
||||
|
||||
export const dimKeys = StrictDict({
|
||||
@@ -39,12 +40,15 @@ const checkEqual = (d1, d2) => (d1.height === d2.height && d1.width === d2.width
|
||||
export const getValidDimensions = ({
|
||||
dimensions,
|
||||
local,
|
||||
locked,
|
||||
isLocked,
|
||||
lockDims,
|
||||
}) => {
|
||||
if (!isLocked || checkEqual(local, dimensions)) {
|
||||
return local;
|
||||
}
|
||||
const out = {};
|
||||
let iter;
|
||||
const { minInc } = locked;
|
||||
const isMin = dimensions.height === minInc.height;
|
||||
const isMin = dimensions.height === lockDims.height;
|
||||
|
||||
const keys = (local.height !== dimensions.height)
|
||||
? { changed: dimKeys.height, other: dimKeys.width }
|
||||
@@ -55,51 +59,16 @@ export const getValidDimensions = ({
|
||||
// don't move down if already at minimum size
|
||||
if (direction < 0 && isMin) { return dimensions; }
|
||||
// find closest valid iteration of the changed field
|
||||
iter = Math.max(Math.round(local[keys.changed] / minInc[keys.changed]), 1);
|
||||
iter = Math.max(Math.round(local[keys.changed] / lockDims[keys.changed]), 1);
|
||||
// if closest valid iteration is current iteration, move one iteration in the change direction
|
||||
if (iter === (dimensions[keys.changed] / minInc[keys.changed])) { iter += direction; }
|
||||
if (iter === (dimensions[keys.changed] / lockDims[keys.changed])) { iter += direction; }
|
||||
|
||||
out[keys.changed] = Math.round(iter * minInc[keys.changed]);
|
||||
out[keys.other] = Math.round(out[keys.changed] * (locked[keys.other] / locked[keys.changed]));
|
||||
out[keys.changed] = Math.round(iter * lockDims[keys.changed]);
|
||||
out[keys.other] = Math.round(out[keys.changed] * (lockDims[keys.other] / lockDims[keys.changed]));
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
/**
|
||||
* newDimensions({ dimensions, local, locked })
|
||||
* Returns the local dimensions if unlocked or unchanged, and otherwise returns new valid
|
||||
* dimensions.
|
||||
* @param {obj} dimensions - current stored dimensions
|
||||
* @param {obj} local - local (active) dimensions in the inputs
|
||||
* @param {obj} locked - locked dimensions
|
||||
* @return {obj} - output dimensions after attempted move ({ height, width })
|
||||
*/
|
||||
export const newDimensions = ({ dimensions, local, locked }) => (
|
||||
(!locked || checkEqual(local, dimensions))
|
||||
? local
|
||||
: module.getValidDimensions({ dimensions, local, locked })
|
||||
);
|
||||
|
||||
/**
|
||||
* lockDimensions({ dimensions, lockInitialized, setLocked })
|
||||
* Lock dimensions if lock initialized. Store minimum valid increment on lock so
|
||||
* that we don't have re-compute.
|
||||
* @param {obj} dimensions - current stored dimensions
|
||||
* @param {bool} lockInitialized - has the lock state initialized?
|
||||
* @param {func} setLocked - set lock state
|
||||
*/
|
||||
export const lockDimensions = ({ dimensions, lockInitialized, setLocked }) => {
|
||||
if (!lockInitialized) { return; }
|
||||
|
||||
// find minimum viable increment
|
||||
let gcd = findGcd(dimensions.width, dimensions.height);
|
||||
if ([dimensions.width, dimensions.height].some(v => !Number.isInteger(v / gcd))) {
|
||||
gcd = 1;
|
||||
}
|
||||
const minInc = { width: dimensions.width / gcd, height: dimensions.height / gcd, gcd };
|
||||
setLocked({ ...dimensions, minInc });
|
||||
};
|
||||
|
||||
/**
|
||||
* dimensionLockHooks({ dimensions })
|
||||
* Returns a set of hooks pertaining to the dimension locks.
|
||||
@@ -107,22 +76,30 @@ export const lockDimensions = ({ dimensions, lockInitialized, setLocked }) => {
|
||||
* @param {obj} dimensions - current stored dimensions
|
||||
* @return {obj} - dimension lock hooks
|
||||
* {func} initializeLock - enable the lock mechanism
|
||||
* {obj} locked - current locked state
|
||||
* {func} lock - lock the current dimensions
|
||||
* {bool} isLocked - are dimensions locked?
|
||||
* {obj} lockDims - image dimensions ({ height, width })
|
||||
* {func} lock - lock the dimensions
|
||||
* {func} unlock - unlock the dimensions
|
||||
*/
|
||||
export const dimensionLockHooks = ({ dimensions }) => {
|
||||
const [locked, setLocked] = module.state.locked(null);
|
||||
const [lockInitialized, setLockInitialized] = module.state.lockInitialized(null);
|
||||
const lock = () => module.lockDimensions({ lockInitialized, dimensions, setLocked });
|
||||
export const dimensionLockHooks = () => {
|
||||
const [lockDims, setLockDims] = module.state.lockDims(null);
|
||||
const [isLocked, setIsLocked] = module.state.isLocked(true);
|
||||
|
||||
React.useEffect(lock, [lockInitialized]);
|
||||
const initializeLock = ({ width, height }) => {
|
||||
// find minimum viable increment
|
||||
let gcd = findGcd(width, height);
|
||||
if ([width, height].some(v => !Number.isInteger(v / gcd))) {
|
||||
gcd = 1;
|
||||
}
|
||||
setLockDims({ width: width / gcd, height: height / gcd });
|
||||
};
|
||||
|
||||
return {
|
||||
initializeLock: () => setLockInitialized(true),
|
||||
locked,
|
||||
lock,
|
||||
unlock: () => setLocked(null),
|
||||
initializeLock,
|
||||
isLocked,
|
||||
lock: () => setIsLocked(true),
|
||||
lockDims,
|
||||
unlock: () => setIsLocked(false),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -146,31 +123,36 @@ export const dimensionLockHooks = ({ dimensions }) => {
|
||||
export const dimensionHooks = () => {
|
||||
const [dimensions, setDimensions] = module.state.dimensions(null);
|
||||
const [local, setLocal] = module.state.local(null);
|
||||
const setAll = (value) => {
|
||||
setDimensions(value);
|
||||
setLocal(value);
|
||||
const setAll = ({ height, width }) => {
|
||||
setDimensions({ height, width });
|
||||
setLocal({ height, width });
|
||||
};
|
||||
const {
|
||||
initializeLock,
|
||||
isLocked,
|
||||
lock,
|
||||
locked,
|
||||
lockDims,
|
||||
unlock,
|
||||
} = module.dimensionLockHooks({ dimensions });
|
||||
|
||||
return {
|
||||
onImgLoad: (selection) => ({ target: img }) => {
|
||||
setAll({
|
||||
height: selection.height || img.naturalHeight,
|
||||
width: selection.width || img.naturalWidth,
|
||||
});
|
||||
initializeLock();
|
||||
const imageDims = { height: img.naturalHeight, width: img.naturalWidth };
|
||||
setAll(selection.height ? selection : imageDims);
|
||||
initializeLock(imageDims);
|
||||
},
|
||||
locked,
|
||||
isLocked,
|
||||
lock,
|
||||
unlock,
|
||||
value: local,
|
||||
setHeight: (height) => setLocal({ ...local, height: parseInt(height, 10) }),
|
||||
setWidth: (width) => setLocal({ ...local, width: parseInt(width, 10) }),
|
||||
updateDimensions: () => setAll(module.newDimensions({ dimensions, local, locked })),
|
||||
updateDimensions: () => setAll(module.getValidDimensions({
|
||||
dimensions,
|
||||
local,
|
||||
isLocked,
|
||||
lockDims,
|
||||
})),
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import React from 'react';
|
||||
import { StrictDict } from '../../../../utils';
|
||||
import { MockUseState } from '../../../../../testUtils';
|
||||
import * as hooks from './hooks';
|
||||
@@ -5,6 +6,7 @@ import * as hooks from './hooks';
|
||||
jest.mock('react', () => ({
|
||||
...jest.requireActual('react'),
|
||||
useEffect: jest.fn(),
|
||||
useState: (val) => ({ useState: val }),
|
||||
}));
|
||||
|
||||
const simpleDims = { width: 3, height: 4 };
|
||||
@@ -24,29 +26,69 @@ const hookKeys = StrictDict(Object.keys(hooks).reduce(
|
||||
|
||||
let hook;
|
||||
|
||||
const testVal = 'MY test VALUE';
|
||||
|
||||
describe('state values', () => {
|
||||
const testStateMethod = (key) => {
|
||||
expect(hooks.state[key](testVal)).toEqual(React.useState(testVal));
|
||||
};
|
||||
test('provides altText state value', () => testStateMethod(state.keys.altText));
|
||||
test('provides dimensions state value', () => testStateMethod(state.keys.dimensions));
|
||||
test('provides isDecorative state value', () => testStateMethod(state.keys.isDecorative));
|
||||
test('provides isLocked state value', () => testStateMethod(state.keys.isLocked));
|
||||
test('provides local state value', () => testStateMethod(state.keys.local));
|
||||
test('provides lockDims state value', () => testStateMethod(state.keys.lockDims));
|
||||
test('provides lockInitialized state value', () => testStateMethod(state.keys.lockInitialized));
|
||||
});
|
||||
|
||||
describe('ImageSettingsModal hooks', () => {
|
||||
describe('dimensions-related hooks', () => {
|
||||
describe('getValidDimensions', () => {
|
||||
it('returns local dimensions if not locked', () => {
|
||||
expect(hooks.getValidDimensions({
|
||||
dimensions: simpleDims,
|
||||
local: reducedDims,
|
||||
isLocked: false,
|
||||
lockDims: simpleDims,
|
||||
})).toEqual(reducedDims);
|
||||
});
|
||||
it('returns local dimensions if the same as stored', () => {
|
||||
expect(hooks.getValidDimensions({
|
||||
dimensions: simpleDims,
|
||||
local: simpleDims,
|
||||
isLocked: true,
|
||||
lockDims: reducedDims,
|
||||
})).toEqual(simpleDims);
|
||||
});
|
||||
describe('decreasing change when at minimum valid increment', () => {
|
||||
it('returns current dimensions', () => {
|
||||
const dimensions = { ...reducedDims };
|
||||
const locked = { minInc: { ...dimensions, gcd } };
|
||||
const lockDims = { ...dimensions };
|
||||
let local = { ...dimensions, width: dimensions.width - 1 };
|
||||
expect(
|
||||
hooks.getValidDimensions({ dimensions, local, locked }),
|
||||
hooks.getValidDimensions({
|
||||
dimensions,
|
||||
isLocked: true,
|
||||
local,
|
||||
lockDims,
|
||||
}),
|
||||
).toEqual(dimensions);
|
||||
local = { ...dimensions, height: dimensions.height - 1 };
|
||||
expect(
|
||||
hooks.getValidDimensions({ dimensions, local, locked }),
|
||||
hooks.getValidDimensions({
|
||||
dimensions,
|
||||
isLocked: true,
|
||||
local,
|
||||
lockDims,
|
||||
}),
|
||||
).toEqual(dimensions);
|
||||
});
|
||||
});
|
||||
describe('valid change', () => {
|
||||
it(
|
||||
'returns the nearest valid pair of dimensions in the change direciton',
|
||||
'returns the nearest valid pair of dimensions in the change direction',
|
||||
() => {
|
||||
const w = 7;
|
||||
const h = 13;
|
||||
const [w, h] = [7, 13];
|
||||
const values = [
|
||||
// bumps up if direction is up but nearest is current
|
||||
[[w + 1, h], [w * 2, h * 2]],
|
||||
@@ -66,75 +108,14 @@ describe('ImageSettingsModal hooks', () => {
|
||||
expect(hooks.getValidDimensions({
|
||||
dimensions,
|
||||
local: { width: local[0], height: local[1] },
|
||||
locked: { ...dimensions, minInc: { ...dimensions, gcd: 1 } },
|
||||
lockDims: { ...dimensions },
|
||||
isLocked: true,
|
||||
})).toEqual({ width: expected[0], height: expected[1] });
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('newDimensions', () => {
|
||||
it('returns the local values if not locked, or if local is equal to dimensions', () => {
|
||||
expect(hooks.newDimensions({
|
||||
dimensions: { ...simpleDims },
|
||||
local: { ...simpleDims },
|
||||
locked: { ...simpleDims },
|
||||
})).toEqual({ ...simpleDims });
|
||||
expect(hooks.newDimensions({
|
||||
dimensions: { ...simpleDims },
|
||||
local: { ...reducedDims },
|
||||
locked: null,
|
||||
})).toEqual({ ...reducedDims });
|
||||
});
|
||||
it('returns getValidDimensions if locked and local has changed', () => {
|
||||
const getValidDimensions = (args) => ({ getValidDimensions: args });
|
||||
jest.spyOn(hooks, hookKeys.getValidDimensions).mockImplementationOnce(getValidDimensions);
|
||||
const args = {
|
||||
dimensions: { ...simpleDims },
|
||||
local: { ...multiDims },
|
||||
locked: { ...reducedDims },
|
||||
};
|
||||
expect(hooks.newDimensions(args)).toEqual(getValidDimensions(args));
|
||||
});
|
||||
});
|
||||
describe('lockDimensions', () => {
|
||||
it('does not call setLocked if lockInitialized is false', () => {
|
||||
state.setState.locked = jest.fn();
|
||||
hooks.lockDimensions({
|
||||
dimensions: simpleDims,
|
||||
setLocked: state.setState.locked,
|
||||
lockInitialized: false,
|
||||
});
|
||||
expect(state.setState.locked).not.toHaveBeenCalled();
|
||||
});
|
||||
it(
|
||||
'calls setLocked with the given dimensions and minInc, including gcd',
|
||||
() => {
|
||||
state.setState.locked = jest.fn();
|
||||
hooks.lockDimensions({
|
||||
dimensions: simpleDims,
|
||||
setLocked: state.setState.locked,
|
||||
lockInitialized: true,
|
||||
});
|
||||
expect(state.setState.locked).toHaveBeenCalledWith({
|
||||
...simpleDims,
|
||||
minInc: { gcd: 1, ...simpleDims },
|
||||
});
|
||||
state.setState.locked.mockClear();
|
||||
|
||||
hooks.lockDimensions({
|
||||
dimensions: multiDims,
|
||||
setLocked: state.setState.locked,
|
||||
lockInitialized: true,
|
||||
});
|
||||
expect(hooks.findGcd(multiDims.width, multiDims.height)).toEqual(7);
|
||||
expect(state.setState.locked).toHaveBeenCalledWith({
|
||||
...multiDims,
|
||||
minInc: { gcd, ...reducedDims },
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
describe('dimensionLockHooks', () => {
|
||||
beforeEach(() => {
|
||||
state.mock();
|
||||
@@ -143,29 +124,27 @@ describe('ImageSettingsModal hooks', () => {
|
||||
afterEach(() => {
|
||||
state.restore();
|
||||
});
|
||||
|
||||
test('locked is initially null', () => {
|
||||
expect(hook.locked).toEqual(null);
|
||||
test('lockDims defaults to null', () => {
|
||||
expect(hook.lockDims).toEqual(null);
|
||||
});
|
||||
test('initializeLock calls setLockInitialized with true', () => {
|
||||
hook.initializeLock();
|
||||
expect(state.setState.lockInitialized).toHaveBeenCalledWith(true);
|
||||
test('isLocked defaults to true', () => {
|
||||
expect(hook.isLocked).toEqual(true);
|
||||
});
|
||||
test('lock calls lockDimensions with lockInitialized, dimensions, and setLocked', () => {
|
||||
state.mockVal(state.keys.lockInitialized, true, state.setState.lockInitialized);
|
||||
hook = hooks.dimensionLockHooks({ dimensions: simpleDims });
|
||||
const lockDimensionsSpy = jest.spyOn(hooks, hookKeys.lockDimensions);
|
||||
hook.lock();
|
||||
expect(lockDimensionsSpy).toHaveBeenCalledWith({
|
||||
dimensions: simpleDims,
|
||||
setLocked: state.setState.locked,
|
||||
lockInitialized: true,
|
||||
describe('initializeLock', () => {
|
||||
it('calls setLockDims with the passed dimensions divided by their gcd', () => {
|
||||
hook.initializeLock(multiDims);
|
||||
expect(state.setState.lockDims).toHaveBeenCalledWith(reducedDims);
|
||||
});
|
||||
});
|
||||
test('lock sets isLocked to true', () => {
|
||||
hook = hooks.dimensionLockHooks({ dimensions: simpleDims });
|
||||
hook.lock();
|
||||
expect(state.setState.isLocked).toHaveBeenCalledWith(true);
|
||||
});
|
||||
test('unlock sets locked to null', () => {
|
||||
hook = hooks.dimensionLockHooks({ dimensions: simpleDims });
|
||||
hook.unlock();
|
||||
expect(state.setState.locked).toHaveBeenCalledWith(null);
|
||||
expect(state.setState.isLocked).toHaveBeenCalledWith(false);
|
||||
});
|
||||
});
|
||||
describe('dimensionHooks', () => {
|
||||
@@ -230,17 +209,21 @@ describe('ImageSettingsModal hooks', () => {
|
||||
});
|
||||
describe('updateDimensions', () => {
|
||||
it('sets local and stored dimensions to newDimensions output', () => {
|
||||
const newDimensions = (args) => ({ newDimensions: args });
|
||||
state.mockVal(state.keys.dimensions, simpleDims, state.setState.dimensions);
|
||||
state.mockVal(state.keys.locked, reducedDims, state.setState.locked);
|
||||
state.mockVal(state.keys.local, multiDims, state.setState.local);
|
||||
jest.spyOn(hooks, hookKeys.newDimensions).mockImplementationOnce(newDimensions);
|
||||
// store values we care about under height or width, and add junk data to be stripped out.
|
||||
const testDims = (args) => ({ ...simpleDims, height: args });
|
||||
const getValidDimensions = (args) => ({ ...testDims(args), junk: 'data' });
|
||||
state.mockVal(state.keys.isLocked, true);
|
||||
state.mockVal(state.keys.dimensions, simpleDims);
|
||||
state.mockVal(state.keys.lockDims, reducedDims);
|
||||
state.mockVal(state.keys.local, multiDims);
|
||||
jest.spyOn(hooks, hookKeys.getValidDimensions).mockImplementationOnce(getValidDimensions);
|
||||
hook = hooks.dimensionHooks();
|
||||
hook.updateDimensions();
|
||||
const expected = newDimensions({
|
||||
const expected = testDims({
|
||||
dimensions: simpleDims,
|
||||
locked: reducedDims,
|
||||
lockDims: reducedDims,
|
||||
local: multiDims,
|
||||
isLocked: true,
|
||||
});
|
||||
expect(state.setState.local).toHaveBeenCalledWith(expected);
|
||||
expect(state.setState.dimensions).toHaveBeenCalledWith(expected);
|
||||
|
||||
Reference in New Issue
Block a user