feat: tests argh
This commit is contained in:
@@ -1,216 +0,0 @@
|
||||
import { keyStore } from '../../../../../../utils';
|
||||
import * as duration from './duration';
|
||||
|
||||
jest.mock('react', () => ({
|
||||
...jest.requireActual('react'),
|
||||
useCallback: (cb, prereqs) => ({ useCallback: { cb, prereqs } }),
|
||||
}));
|
||||
|
||||
let hook;
|
||||
const durationKeys = keyStore(duration);
|
||||
const [h, m, s] = [3600000, 60000, 1000];
|
||||
const durationPairs = [
|
||||
[0, '00:00:00'],
|
||||
[5000, '00:00:05'],
|
||||
[60000, '00:01:00'],
|
||||
[3600000, '01:00:00'],
|
||||
[3665000, '01:01:05'],
|
||||
];
|
||||
const trickyDurations = [
|
||||
['10:00', 600000],
|
||||
['23', 23000],
|
||||
['99:99:99', 99 * (m + s + h)],
|
||||
['23:42:81', 23 * h + 42 * m + 81 * s],
|
||||
];
|
||||
let spies = {};
|
||||
let props;
|
||||
let cb;
|
||||
let prereqs;
|
||||
let oldDuration;
|
||||
describe('Video Settings Modal duration hooks', () => {
|
||||
beforeEach(() => {
|
||||
spies = {};
|
||||
oldDuration = { ...jest.requireActual('./duration') };
|
||||
});
|
||||
afterEach(() => {
|
||||
Object.keys(oldDuration).forEach((key) => {
|
||||
duration[key] = oldDuration[key];
|
||||
});
|
||||
Object.keys(spies).forEach((key) => {
|
||||
spies[key].mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('onDurationChange', () => {
|
||||
beforeEach(() => {
|
||||
props = {
|
||||
duration: { startTime: '00:00:00' },
|
||||
index: 'startTime',
|
||||
val: 'vAl',
|
||||
};
|
||||
hook = duration.onDurationChange;
|
||||
});
|
||||
it('returns duration with no change if duration[index] does not match HH:MM:SS format', () => {
|
||||
const badChecks = [
|
||||
'ab:cd:ef', // non-digit characters
|
||||
'12:34:567', // characters past max length
|
||||
];
|
||||
badChecks.forEach(val => expect(hook(props.duration, props.index, val)).toEqual(props.duration));
|
||||
});
|
||||
it('returns duration with an added \':\' after 2 characters when caret is at end', () => {
|
||||
props.duration = { startTime: '0' };
|
||||
props.val = '00';
|
||||
document.activeElement.selectionStart = props.duration[props.index].length + 1;
|
||||
expect(hook(props.duration, props.index, props.val)).toEqual({ startTime: '00:' });
|
||||
});
|
||||
it('returns duration with an added \':\' after 5 characters when caret is at end', () => {
|
||||
props.duration = { startTime: '00:0' };
|
||||
props.val = '00:00';
|
||||
document.activeElement.selectionStart = props.duration[props.index].length + 1;
|
||||
expect(hook(props.duration, props.index, props.val)).toEqual({ startTime: '00:00:' });
|
||||
});
|
||||
});
|
||||
describe('onDurationKeyDown', () => {
|
||||
beforeEach(() => {
|
||||
props = {
|
||||
duration: { startTime: '00:00:00' },
|
||||
index: 'startTime',
|
||||
event: 'eVeNt',
|
||||
};
|
||||
hook = duration.onDurationKeyDown;
|
||||
});
|
||||
it('enter event: calls blur()', () => {
|
||||
props.event = { key: 'Enter' };
|
||||
const blurSpy = jest.spyOn(document.activeElement, 'blur');
|
||||
hook(props.duration, props.index, props.event);
|
||||
expect(blurSpy).toHaveBeenCalled();
|
||||
});
|
||||
it('backspace event: returns duration with deleted end character when that character is \':\' and caret is at end', () => {
|
||||
props.duration = { startTime: '00:' };
|
||||
props.event = { key: 'Backspace' };
|
||||
document.activeElement.selectionStart = props.duration[props.index].length;
|
||||
expect(hook(props.duration, props.index, props.event)).toEqual({ startTime: '00' });
|
||||
});
|
||||
});
|
||||
describe('durationFromValue', () => {
|
||||
beforeEach(() => {
|
||||
hook = duration.durationFromValue;
|
||||
});
|
||||
it('returns 00:00:00 if given a bad value', () => {
|
||||
const badChecks = ['a', '', null, -1];
|
||||
badChecks.forEach(val => expect(hook(val)).toEqual('00:00:00'));
|
||||
});
|
||||
it('translates milliseconds into hh:mm:ss format', () => {
|
||||
durationPairs.forEach(
|
||||
([val, dur]) => expect(hook(val)).toEqual(dur),
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('valueFromDuration', () => {
|
||||
beforeEach(() => {
|
||||
hook = duration.valueFromDuration;
|
||||
});
|
||||
it('returns 0 if given a bad duration string', () => {
|
||||
const badChecks = ['a', '00:00:1f', '0adg:00:04'];
|
||||
badChecks.forEach(dur => expect(hook(dur)).toEqual(0));
|
||||
});
|
||||
it('returns simple durations', () => {
|
||||
durationPairs.forEach(([val, dur]) => expect(hook(dur)).toEqual(val));
|
||||
});
|
||||
it('returns tricky durations, prepending zeros and expanding out sections', () => {
|
||||
trickyDurations.forEach(([dur, val]) => expect(hook(dur)).toEqual(val));
|
||||
});
|
||||
});
|
||||
describe('durationValue', () => {
|
||||
const mock = jest.fn(v => ({ duration: v }));
|
||||
beforeEach(() => {
|
||||
jest.spyOn(duration, durationKeys.durationFromValue).mockImplementation(mock);
|
||||
});
|
||||
it('returns an object that maps durationFromValue to the passed duration keys', () => {
|
||||
const testDuration = { startTime: 1, stopTime: 2, other: 'values' };
|
||||
expect(duration.durationValue(testDuration)).toEqual({
|
||||
startTime: mock(testDuration.startTime),
|
||||
stopTime: mock(testDuration.stopTime),
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('updateDuration', () => {
|
||||
const testValidIndex = 'startTime';
|
||||
const testStopIndex = 'stopTime';
|
||||
const testValidDuration = '00:00:00';
|
||||
const testValidValue = 0;
|
||||
const testInvalidDuration = 'abc';
|
||||
beforeEach(() => {
|
||||
props = {
|
||||
formValue: { startTime: 23000, stopTime: 600000 },
|
||||
local: { startTime: '00:00:23', stopTime: '00:10:00' },
|
||||
setLocal: jest.fn(),
|
||||
setFormValue: jest.fn(),
|
||||
};
|
||||
hook = duration.updateDuration;
|
||||
({ cb, prereqs } = hook(props).useCallback);
|
||||
});
|
||||
it('returns a useCallback field based on the passed args', () => {
|
||||
expect(prereqs).toEqual([
|
||||
props.formValue,
|
||||
props.local,
|
||||
props.setLocal,
|
||||
props.setFormValue,
|
||||
]);
|
||||
});
|
||||
describe('callback', () => {
|
||||
describe('if the passed durationString is valid', () => {
|
||||
it('sets the local value to updated strings and form value to new timestamp value', () => {
|
||||
cb(testValidIndex, testValidDuration);
|
||||
expect(props.setLocal).toHaveBeenCalledWith({
|
||||
...props.local,
|
||||
[testValidIndex]: testValidDuration,
|
||||
});
|
||||
expect(props.setFormValue).toHaveBeenCalledWith({
|
||||
...props.formValue,
|
||||
[testValidIndex]: testValidValue,
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('if the passed durationString is not valid', () => {
|
||||
it('updates local values to 0 (the default)', () => {
|
||||
hook(props).useCallback.cb(testValidIndex, testInvalidDuration);
|
||||
expect(props.setLocal).toHaveBeenCalledWith({
|
||||
...props.local,
|
||||
[testValidIndex]: testValidDuration,
|
||||
});
|
||||
expect(props.setFormValue).toHaveBeenCalledWith({
|
||||
...props.formValue,
|
||||
[testValidIndex]: testValidValue,
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('if the passed startTime is after (or equal to) the stored non-zero stopTime', () => {
|
||||
it('updates local startTime values to 1 second before stopTime', () => {
|
||||
hook(props).useCallback.cb(testValidIndex, '00:10:00');
|
||||
expect(props.setLocal).toHaveBeenCalledWith({
|
||||
...props.local,
|
||||
[testValidIndex]: '00:09:59',
|
||||
});
|
||||
expect(props.setFormValue).toHaveBeenCalledWith({
|
||||
...props.formValue,
|
||||
[testValidIndex]: 599000,
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('if the passed stopTime is before (or equal to) the stored startTime', () => {
|
||||
it('updates local stopTime values to 1 second after startTime', () => {
|
||||
hook(props).useCallback.cb(testStopIndex, '00:00:22');
|
||||
expect(props.setLocal).toHaveBeenCalledWith({
|
||||
...props.local,
|
||||
[testStopIndex]: '00:00:24',
|
||||
});
|
||||
expect(props.setFormValue).toHaveBeenCalledWith({
|
||||
...props.formValue,
|
||||
[testStopIndex]: 24000,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -2,63 +2,79 @@ import { useEffect, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { actions, selectors } from '../../../../../../data/redux';
|
||||
import messages from '../messages';
|
||||
|
||||
import * as module from './hooks';
|
||||
|
||||
const durationMatcher = /^(\d{0,2}):?(\d{0,2})?:?(\d{0,2})?$/i;
|
||||
const MAXTIME = 86399000;
|
||||
const MINTIME = 1000;
|
||||
|
||||
export const durationWidget = ({ dispatch }) => {
|
||||
const formValue = useSelector(selectors.video.duration);
|
||||
const setFormValue = (val) => dispatch(actions.video.updateField({ duration: val }));
|
||||
const initialState = durationValue(formValue);
|
||||
const [local, setLocal] = useState(initialState);
|
||||
const reduxStartStopTimes = useSelector(selectors.video.duration);
|
||||
const setReduxStartStopTimes = (val) => dispatch(actions.video.updateField({ duration: val }));
|
||||
const initialState = module.durationString(reduxStartStopTimes);
|
||||
const [unsavedStartStopTimes, setUnsavedStartStopTimes] = useState(initialState);
|
||||
|
||||
useEffect(() => {
|
||||
setLocal(durationValue(formValue))
|
||||
}, [formValue]);
|
||||
setUnsavedStartStopTimes(module.durationString(reduxStartStopTimes));
|
||||
}, [reduxStartStopTimes]);
|
||||
|
||||
return {
|
||||
formValue,
|
||||
local,
|
||||
reduxStartStopTimes,
|
||||
unsavedStartStopTimes,
|
||||
onBlur: (index) => (
|
||||
(e) => module.updateDuration({
|
||||
formValue,
|
||||
setFormValue,
|
||||
local,
|
||||
setLocal,
|
||||
reduxStartStopTimes,
|
||||
setReduxStartStopTimes,
|
||||
unsavedStartStopTimes,
|
||||
setUnsavedStartStopTimes,
|
||||
index,
|
||||
durationString: e.target.value,
|
||||
})
|
||||
),
|
||||
onChange: (index) => (
|
||||
(e) => setLocal(module.onDurationChange(local, index, e.target.value))
|
||||
(e) => setUnsavedStartStopTimes(module.onDurationChange(unsavedStartStopTimes, index, e.target.value))
|
||||
),
|
||||
onKeyDown: (index) => (
|
||||
(e) => setLocal(module.onDurationKeyDown(local, index, e))
|
||||
(e) => setUnsavedStartStopTimes(module.onDurationKeyDown(unsavedStartStopTimes, index, e))
|
||||
),
|
||||
getTotalLabel: ({ duration, subtitle, intl }) => {
|
||||
if (!duration.stopTime) {
|
||||
if (!duration.startTime) {
|
||||
return intl.formatMessage(messages.fullVideoLength);
|
||||
}
|
||||
if (subtitle) {
|
||||
return intl.formatMessage(
|
||||
messages.startsAt,
|
||||
{ startTime: module.durationStringFromValue(duration.startTime) },
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
const total = duration.stopTime - (duration.startTime || 0);
|
||||
return intl.formatMessage(messages.total, { total: module.durationStringFromValue(total) });
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* durationValue(duration)
|
||||
* durationString(duration)
|
||||
* Returns the display value for embedded start and stop times
|
||||
* @param {object} duration - object containing startTime and stopTime millisecond values
|
||||
* @return {object} - start and stop time from incoming object mapped to duration strings.
|
||||
*/
|
||||
export const durationValue = (duration) => ({
|
||||
startTime: module.durationFromValue(duration.startTime),
|
||||
stopTime: module.durationFromValue(duration.stopTime),
|
||||
export const durationString = (duration) => ({
|
||||
startTime: module.durationStringFromValue(duration.startTime),
|
||||
stopTime: module.durationStringFromValue(duration.stopTime),
|
||||
});
|
||||
|
||||
/**
|
||||
* durationFromValue(value)
|
||||
* durationStringFromValue(value)
|
||||
* Returns a duration string in 'hh:mm:ss' format from the given ms value
|
||||
* @param {number} value - duration (in milliseconds)
|
||||
* @return {string} - duration in 'hh:mm:ss' format
|
||||
*/
|
||||
export const durationFromValue = (value) => {
|
||||
export const durationStringFromValue = (value) => {
|
||||
// return 'why';
|
||||
if (!value || typeof value !== 'number' || value <= 0) {
|
||||
return '00:00:00';
|
||||
}
|
||||
@@ -70,48 +86,49 @@ export const durationFromValue = (value) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* updateDuration({ formValue, local, setLocal, setFormValue })
|
||||
* Returns a memoized callback based on inputs that updates local value and form value
|
||||
* if the new string is valid (formValue stores a number, local stores a string).
|
||||
* If the duration string is invalid, resets the local value to the latest good value.
|
||||
* @param {object} formValue - redux-stored durations in milliseconds
|
||||
* @param {object} local - hook-stored duration in 'hh:mm:ss' format
|
||||
* @param {func} setFormValue - set form value
|
||||
* @param {func} setLocal - set local object
|
||||
* @return {func} - callback to update duration locally and in redux
|
||||
* updateDuration({ reduxStartStopTimes, unsavedStartStopTimes, setUnsavedStartStopTimes, setReduxStartStopTimes })
|
||||
* Returns a memoized callback based on inputs that updates unsavedStartStopTimes value and form value
|
||||
* if the new string is valid (reduxStartStopTimes stores a number, unsavedStartStopTimes stores a string).
|
||||
* If the duration string is invalid, resets the unsavedStartStopTimes value to the latest good value.
|
||||
* @param {object} reduxStartStopTimes - redux-stored durations in milliseconds
|
||||
* @param {object} unsavedStartStopTimes - hook-stored duration in 'hh:mm:ss' format
|
||||
* @param {func} setReduxStartStopTimes - set form value
|
||||
* @param {func} setUnsavedStartStopTimes - set unsavedStartStopTimes object
|
||||
* @param {string} index - startTime or stopTime
|
||||
* @return {func} - callback to update duration unsavedStartStopTimesly and in redux
|
||||
* updateDuration(args)(index, durationString)
|
||||
*/
|
||||
export const updateDuration = ({
|
||||
formValue,
|
||||
local,
|
||||
setFormValue,
|
||||
setLocal,
|
||||
reduxStartStopTimes,
|
||||
unsavedStartStopTimes,
|
||||
setReduxStartStopTimes,
|
||||
setUnsavedStartStopTimes,
|
||||
index,
|
||||
durationString,
|
||||
inputString,
|
||||
}) => {
|
||||
let newDurationString = durationString;
|
||||
let newValue = module.valueFromDuration(newDurationString);
|
||||
// maxTime is 23:59:59 or 86399 seconds
|
||||
if (newValue > MAXTIME) {
|
||||
newValue = MAXTIME;
|
||||
}
|
||||
// stopTime must be at least 1 second, if not zero
|
||||
if (index === 'stopTime' && newValue > 0 && newValue < MINTIME) {
|
||||
newValue = MINTIME;
|
||||
}
|
||||
// stopTime must be at least 1 second after startTime, except 0 means no custom stopTime
|
||||
if (index === 'stopTime' && newValue > 0 && newValue < (formValue.startTime + MINTIME)) {
|
||||
newValue = formValue.startTime + MINTIME;
|
||||
}
|
||||
// startTime must be at least 1 second before stopTime, except when stopTime is less than a second
|
||||
// (stopTime should only be less than a second if it's zero, but we're being paranoid)
|
||||
if (index === 'startTime' && formValue.stopTime >= MINTIME && newValue > (formValue.stopTime - MINTIME)) {
|
||||
newValue = formValue.stopTime - MINTIME;
|
||||
}
|
||||
newDurationString = module.durationFromValue(newValue);
|
||||
setLocal({ ...local, [index]: newDurationString });
|
||||
setFormValue({ ...formValue, [index]: newValue });
|
||||
};
|
||||
let newDurationString = inputString;
|
||||
let newValue = module.valueFromDuration(newDurationString);
|
||||
// maxTime is 23:59:59 or 86399 seconds
|
||||
if (newValue > 86399000) {
|
||||
newValue = 86399000;
|
||||
}
|
||||
// stopTime must be at least 1 second, if not zero
|
||||
if (index === 'stopTime' && newValue > 0 && newValue < 1000) {
|
||||
newValue = 1000;
|
||||
}
|
||||
// stopTime must be at least 1 second after startTime, except 0 means no custom stopTime
|
||||
if (index === 'stopTime' && newValue > 0 && newValue < (reduxStartStopTimes.startTime + 1000)) {
|
||||
newValue = reduxStartStopTimes.startTime + 1000;
|
||||
}
|
||||
// startTime must be at least 1 second before stopTime, except when stopTime is less than a second
|
||||
// (stopTime should only be less than a second if it's zero, but we're being paranoid)
|
||||
if (index === 'startTime' && reduxStartStopTimes.stopTime >= 1000 && newValue > (reduxStartStopTimes.stopTime - 1000)) {
|
||||
newValue = reduxStartStopTimes.stopTime - 1000;
|
||||
}
|
||||
newDurationString = module.durationStringFromValue(newValue);
|
||||
setUnsavedStartStopTimes({ ...unsavedStartStopTimes, [index]: newDurationString });
|
||||
setReduxStartStopTimes({ ...reduxStartStopTimes, [index]: newValue });
|
||||
};
|
||||
|
||||
/**
|
||||
* onDurationChange(duration)
|
||||
@@ -121,7 +138,7 @@ export const updateDuration = ({
|
||||
* @param {string} val - duration in 'hh:mm:ss' format
|
||||
* @return {object} duration - object containing startTime and stopTime millisecond values
|
||||
*/
|
||||
export const onDurationChange = (duration, index, val) => {
|
||||
export const onDurationChange = (duration, index, val) => {
|
||||
const match = val.trim().match(durationMatcher);
|
||||
if (!match) {
|
||||
return duration;
|
||||
@@ -193,8 +210,8 @@ export const valueFromDuration = (duration) => {
|
||||
|
||||
export default {
|
||||
durationWidget,
|
||||
durationValue,
|
||||
durationFromValue,
|
||||
durationString,
|
||||
durationStringFromValue,
|
||||
updateDuration,
|
||||
onDurationChange,
|
||||
onDurationKeyDown,
|
||||
|
||||
@@ -0,0 +1,328 @@
|
||||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { selectors } from '../../../../../../data/redux';
|
||||
import * as hooks from './hooks';
|
||||
import messages from '../messages';
|
||||
|
||||
jest.mock('react', () => {
|
||||
const updateState = jest.fn();
|
||||
const dispatchFn = jest.fn();
|
||||
return {
|
||||
...jest.requireActual('react'),
|
||||
updateState,
|
||||
useState: jest.fn(val => ([{ state: val }, (newVal) => updateState({ val, newVal })])),
|
||||
useCallback: (cb, prereqs) => ({ useCallback: { cb, prereqs } }),
|
||||
useEffect: jest.fn(),
|
||||
useSelector: jest.fn(),
|
||||
dispatch: dispatchFn,
|
||||
useDispatch: jest.fn(() => dispatchFn),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../../../../../data/redux', () => ({
|
||||
actions: {
|
||||
video: {
|
||||
updateField: (val) => ({ updateField: val }),
|
||||
},
|
||||
},
|
||||
selectors: {
|
||||
video: {
|
||||
duration: (state) => ({ duration: state }),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
let hook;
|
||||
const dispatch = jest.fn(val => ({ dispatch: val }));
|
||||
const intl = {
|
||||
formatMessage: jest.fn(val => val),
|
||||
};
|
||||
|
||||
const [h, m, s] = [3600000, 60000, 1000];
|
||||
const durationPairs = [
|
||||
[0, '00:00:00'],
|
||||
[5000, '00:00:05'],
|
||||
[60000, '00:01:00'],
|
||||
[3600000, '01:00:00'],
|
||||
[3665000, '01:01:05'],
|
||||
];
|
||||
const trickyDurations = [
|
||||
['10:00', 600000],
|
||||
['23', 23000],
|
||||
['99:99:99', 99 * (m + s + h)],
|
||||
['23:42:81', 23 * h + 42 * m + 81 * s],
|
||||
];
|
||||
let props;
|
||||
const e = {
|
||||
target: {
|
||||
value: 'vAlUE',
|
||||
},
|
||||
};
|
||||
|
||||
describe('Video Settings DurationWidget hooks', () => {
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
describe('durationWidget', () => {
|
||||
let reduxStartStopTimes;
|
||||
beforeEach(() => {
|
||||
hook = hooks.durationWidget({ dispatch });
|
||||
reduxStartStopTimes = useSelector(selectors.video.duration);
|
||||
});
|
||||
describe('behavior', () => {
|
||||
describe('initialization', () => {
|
||||
test('useEffect memoized on reduxStartStopTimes', () => {
|
||||
hooks.durationWidget({ dispatch });
|
||||
expect(React.useEffect).toHaveBeenCalled();
|
||||
expect(React.useEffect.mock.calls[0][1]).toEqual([reduxStartStopTimes]);
|
||||
});
|
||||
test('calls setUnsavedStartStopTimes with durationString(reduxStartStopTimes)', () => {
|
||||
hooks.durationWidget({ dispatch });
|
||||
React.useEffect.mock.calls[0][0]();
|
||||
expect(React.updateState).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('returns', () => {
|
||||
hook = hooks.durationWidget({ dispatch });
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
describe('reduxStartStopTimes, with redux duration value', () => {
|
||||
expect(hook.reduxStartStopTimes).toEqual(useSelector(selectors.video.duration));
|
||||
});
|
||||
describe('unsavedStartStopTimes, defaulted to reduxStartStopTimes', () => {
|
||||
expect(hook.unsavedStartStopTimes).toEqual({ state: hooks.durationString(hook.reduxStartStopTimes) });
|
||||
});
|
||||
describe('onBlur, calls updateDuration', () => {
|
||||
jest.spyOn(hooks, 'updateDuration').mockImplementation(jest.fn());
|
||||
hook.onBlur('IndEX')(e);
|
||||
expect(hooks.updateDuration).toHaveBeenCalled();
|
||||
});
|
||||
describe('onChange', () => {
|
||||
hook.onChange('IndEX')(e);
|
||||
expect(React.updateState).toHaveBeenCalled();
|
||||
});
|
||||
describe('onKeyDown', () => {
|
||||
hook.onKeyDown('iNDex')(e);
|
||||
expect(React.updateState).toHaveBeenCalled();
|
||||
});
|
||||
describe('getTotalLabel', () => {
|
||||
describe('returns fullVideoLength message when no startTime and no stop Time are set', () => {
|
||||
expect(hook.getTotalLabel({
|
||||
duration: {},
|
||||
subtitle: true,
|
||||
intl,
|
||||
})).toEqual(messages.fullVideoLength);
|
||||
});
|
||||
describe('returns startAt message for subtitle when only startTime is set', () => {
|
||||
expect(hook.getTotalLabel({
|
||||
duration: {
|
||||
startTime: '00:00:00',
|
||||
},
|
||||
subtitle: true,
|
||||
intl,
|
||||
})).toEqual(messages.startsAt);
|
||||
});
|
||||
describe('returns null for widget (not subtitle) when there only startTime is set', () => {
|
||||
expect(hook.getTotalLabel({
|
||||
duration: {
|
||||
startTime: '00:00:00',
|
||||
},
|
||||
subtitle: false,
|
||||
intl,
|
||||
})).toEqual(null);
|
||||
});
|
||||
describe('returns total message when at least stopTime is set', () => {
|
||||
expect(hook.getTotalLabel({
|
||||
duration: {
|
||||
startTime: '00:00:00',
|
||||
stopTime: '00:00:10',
|
||||
},
|
||||
subtitle: true,
|
||||
intl,
|
||||
})).toEqual(messages.total);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('durationString', () => {
|
||||
beforeEach(() => {
|
||||
hook = hooks.durationString;
|
||||
});
|
||||
it('returns an object that maps durationStringFromValue to the passed duration keys', () => {
|
||||
const testDuration = { startTime: 1000, stopTime: 2000, other: 'values' };
|
||||
expect(hook(testDuration)).toEqual({
|
||||
startTime: '00:00:01',
|
||||
stopTime: '00:00:02',
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('durationStringFromValue', () => {
|
||||
beforeEach(() => {
|
||||
hook = hooks.durationStringFromValue;
|
||||
});
|
||||
it('returns 00:00:00 if given a bad value', () => {
|
||||
const badChecks = ['a', '', null, -1];
|
||||
badChecks.forEach(val => expect(hook(val)).toEqual('00:00:00'));
|
||||
});
|
||||
it('translates milliseconds into hh:mm:ss format', () => {
|
||||
durationPairs.forEach(
|
||||
([val, dur]) => expect(hook(val)).toEqual(dur),
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('updateDuration', () => {
|
||||
const testValidIndex = 'startTime';
|
||||
const testStopIndex = 'stopTime';
|
||||
const testValidDuration = '00:00:00';
|
||||
const testValidValue = 0;
|
||||
const testInvalidDuration = 'abc';
|
||||
beforeEach(() => {
|
||||
hook = hooks.updateDuration;
|
||||
props = {
|
||||
reduxStartStopTimes: { startTime: 23000, stopTime: 600000 },
|
||||
unsavedStartStopTimes: { startTime: '00:00:23', stopTime: '00:10:00' },
|
||||
setReduxStartStopTimes: jest.fn(),
|
||||
setUnsavedStartStopTimes: jest.fn(),
|
||||
index: 'startTime',
|
||||
inputString: '01:23:45',
|
||||
};
|
||||
});
|
||||
describe('if the passed durationString is valid', () => {
|
||||
it('sets the unsavedStartStopTimes to updated strings and reduxStartStopTimes to new timestamp value', () => {
|
||||
hook({
|
||||
...props,
|
||||
index: testValidIndex,
|
||||
inputString: testValidDuration,
|
||||
});
|
||||
expect(props.setUnsavedStartStopTimes).toHaveBeenCalledWith({
|
||||
...props.unsavedStartStopTimes,
|
||||
[testValidIndex]: testValidDuration,
|
||||
});
|
||||
expect(props.setReduxStartStopTimes).toHaveBeenCalledWith({
|
||||
...props.reduxStartStopTimes,
|
||||
[testValidIndex]: testValidValue,
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('if the passed durationString is not valid', () => {
|
||||
it('updates unsavedStartStopTimes values to 0 (the default)', () => {
|
||||
hook({
|
||||
...props,
|
||||
index: testValidIndex,
|
||||
inputString: testInvalidDuration,
|
||||
});
|
||||
expect(props.setUnsavedStartStopTimes).toHaveBeenCalledWith({
|
||||
...props.unsavedStartStopTimes,
|
||||
[testValidIndex]: testValidDuration,
|
||||
});
|
||||
expect(props.setReduxStartStopTimes).toHaveBeenCalledWith({
|
||||
...props.reduxStartStopTimes,
|
||||
[testValidIndex]: testValidValue,
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('if the passed startTime is after (or equal to) the stored non-zero stopTime', () => {
|
||||
it('updates unsavedStartStopTimes startTime values to 1 second before stopTime', () => {
|
||||
hook({
|
||||
...props,
|
||||
index: testValidIndex,
|
||||
inputString: '00:10:00',
|
||||
});
|
||||
expect(props.setUnsavedStartStopTimes).toHaveBeenCalledWith({
|
||||
...props.unsavedStartStopTimes,
|
||||
[testValidIndex]: '00:09:59',
|
||||
});
|
||||
expect(props.setReduxStartStopTimes).toHaveBeenCalledWith({
|
||||
...props.reduxStartStopTimes,
|
||||
[testValidIndex]: 599000,
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('if the passed stopTime is before (or equal to) the stored startTime', () => {
|
||||
it('updates unsavedStartStopTimes stopTime values to 1 second after startTime', () => {
|
||||
hook({
|
||||
...props,
|
||||
index: testStopIndex,
|
||||
inputString: '00:00:22',
|
||||
});
|
||||
expect(props.setUnsavedStartStopTimes).toHaveBeenCalledWith({
|
||||
...props.unsavedStartStopTimes,
|
||||
[testStopIndex]: '00:00:24',
|
||||
});
|
||||
expect(props.setReduxStartStopTimes).toHaveBeenCalledWith({
|
||||
...props.reduxStartStopTimes,
|
||||
[testStopIndex]: 24000,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('onDurationChange', () => {
|
||||
beforeEach(() => {
|
||||
props = {
|
||||
duration: { startTime: '00:00:00' },
|
||||
index: 'startTime',
|
||||
val: 'vAl',
|
||||
};
|
||||
hook = hooks.onDurationChange;
|
||||
});
|
||||
it('returns duration with no change if duration[index] does not match HH:MM:SS format', () => {
|
||||
const badChecks = [
|
||||
'ab:cd:ef', // non-digit characters
|
||||
'12:34:567', // characters past max length
|
||||
];
|
||||
badChecks.forEach(val => expect(hook(props.duration, props.index, val)).toEqual(props.duration));
|
||||
});
|
||||
it('returns duration with an added \':\' after 2 characters when caret is at end', () => {
|
||||
props.duration = { startTime: '0' };
|
||||
props.val = '00';
|
||||
document.activeElement.selectionStart = props.duration[props.index].length + 1;
|
||||
expect(hook(props.duration, props.index, props.val)).toEqual({ startTime: '00:' });
|
||||
});
|
||||
it('returns duration with an added \':\' after 5 characters when caret is at end', () => {
|
||||
props.duration = { startTime: '00:0' };
|
||||
props.val = '00:00';
|
||||
document.activeElement.selectionStart = props.duration[props.index].length + 1;
|
||||
expect(hook(props.duration, props.index, props.val)).toEqual({ startTime: '00:00:' });
|
||||
});
|
||||
});
|
||||
describe('onDurationKeyDown', () => {
|
||||
beforeEach(() => {
|
||||
props = {
|
||||
duration: { startTime: '00:00:00' },
|
||||
index: 'startTime',
|
||||
event: 'eVeNt',
|
||||
};
|
||||
hook = hooks.onDurationKeyDown;
|
||||
});
|
||||
it('enter event: calls blur()', () => {
|
||||
props.event = { key: 'Enter' };
|
||||
const blurSpy = jest.spyOn(document.activeElement, 'blur');
|
||||
hook(props.duration, props.index, props.event);
|
||||
expect(blurSpy).toHaveBeenCalled();
|
||||
});
|
||||
it('backspace event: returns duration with deleted end character when that character is \':\' and caret is at end', () => {
|
||||
props.duration = { startTime: '00:' };
|
||||
props.event = { key: 'Backspace' };
|
||||
document.activeElement.selectionStart = props.duration[props.index].length;
|
||||
expect(hook(props.duration, props.index, props.event)).toEqual({ startTime: '00' });
|
||||
});
|
||||
});
|
||||
describe('valueFromDuration', () => {
|
||||
beforeEach(() => {
|
||||
hook = hooks.valueFromDuration;
|
||||
});
|
||||
it('returns 0 if given a bad duration string', () => {
|
||||
const badChecks = ['a', '00:00:1f', '0adg:00:04'];
|
||||
badChecks.forEach(dur => expect(hook(dur)).toEqual(0));
|
||||
});
|
||||
it('returns simple durations', () => {
|
||||
durationPairs.forEach(([val, dur]) => expect(hook(dur)).toEqual(val));
|
||||
});
|
||||
it('returns tricky durations, prepending zeros and expanding out sections', () => {
|
||||
trickyDurations.forEach(([dur, val]) => expect(hook(dur)).toEqual(val));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -7,7 +7,6 @@ import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/
|
||||
import { keyStore } from '../../../../../../utils';
|
||||
import CollapsibleFormWidget from '../CollapsibleFormWidget';
|
||||
import hooks from './hooks';
|
||||
import { durationFromValue } from '../duration';
|
||||
import messages from '../messages';
|
||||
|
||||
/**
|
||||
@@ -21,34 +20,25 @@ export const DurationWidget = ({
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const {
|
||||
formValue,
|
||||
local,
|
||||
reduxStartStopTimes,
|
||||
unsavedStartStopTimes,
|
||||
onBlur,
|
||||
onChange,
|
||||
onKeyDown,
|
||||
getTotalLabel,
|
||||
} = hooks.durationWidget({ dispatch });
|
||||
|
||||
const timeKeys = keyStore(formValue);
|
||||
|
||||
const getTotalLabel = (startTime, stopTime, subtitle) => {
|
||||
if (!stopTime) {
|
||||
if (!startTime) {
|
||||
return intl.formatMessage(messages.fullVideoLength);
|
||||
}
|
||||
if (subtitle) {
|
||||
return intl.formatMessage(messages.startsAt, { startTime: durationFromValue(startTime) });
|
||||
}
|
||||
return null;
|
||||
}
|
||||
const total = stopTime - (startTime || 0);
|
||||
return intl.formatMessage(messages.total, { total: durationFromValue(total) });
|
||||
};
|
||||
const timeKeys = keyStore(reduxStartStopTimes);
|
||||
|
||||
return (
|
||||
<CollapsibleFormWidget
|
||||
fontSize="x-small"
|
||||
title={intl.formatMessage(messages.durationTitle)}
|
||||
subtitle={getTotalLabel(formValue.startTime, formValue.stopTime, true)}
|
||||
subtitle={getTotalLabel({
|
||||
duration: reduxStartStopTimes,
|
||||
subtitle: true,
|
||||
intl,
|
||||
})}
|
||||
>
|
||||
<FormattedMessage {...messages.durationDescription} />
|
||||
<Form.Row className="mt-4.5">
|
||||
@@ -58,7 +48,7 @@ export const DurationWidget = ({
|
||||
onBlur={onBlur(timeKeys.startTime)}
|
||||
onChange={onChange(timeKeys.startTime)}
|
||||
onKeyDown={onKeyDown(timeKeys.startTime)}
|
||||
value={local.startTime}
|
||||
value={unsavedStartStopTimes.startTime}
|
||||
/>
|
||||
<Form.Control.Feedback>
|
||||
<FormattedMessage {...messages.durationHint} />
|
||||
@@ -70,7 +60,7 @@ export const DurationWidget = ({
|
||||
onBlur={onBlur(timeKeys.stopTime)}
|
||||
onChange={onChange(timeKeys.stopTime)}
|
||||
onKeyDown={onKeyDown(timeKeys.stopTime)}
|
||||
value={local.stopTime}
|
||||
value={unsavedStartStopTimes.stopTime}
|
||||
/>
|
||||
<Form.Control.Feedback>
|
||||
<FormattedMessage {...messages.durationHint} />
|
||||
@@ -78,7 +68,11 @@ export const DurationWidget = ({
|
||||
</Form.Group>
|
||||
</Form.Row>
|
||||
<div className="mt-4">
|
||||
{getTotalLabel(formValue.startTime, formValue.stopTime)}
|
||||
{getTotalLabel({
|
||||
duration: reduxStartStopTimes,
|
||||
subtitle: false,
|
||||
intl,
|
||||
})}
|
||||
</div>
|
||||
</CollapsibleFormWidget>
|
||||
);
|
||||
|
||||
@@ -2,7 +2,6 @@ import {
|
||||
useCallback,
|
||||
useState,
|
||||
useEffect,
|
||||
useMemo,
|
||||
} from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
@@ -10,18 +9,9 @@ import { StrictDict, keyStore } from '../../../../../utils';
|
||||
import { actions, selectors } from '../../../../../data/redux';
|
||||
|
||||
import {
|
||||
updateDuration,
|
||||
durationValue,
|
||||
onDurationChange,
|
||||
onDurationKeyDown,
|
||||
} from './duration';
|
||||
|
||||
import {
|
||||
handleIndexEvent,
|
||||
handleIndexTransformEvent,
|
||||
onValue,
|
||||
onChecked,
|
||||
onEvent,
|
||||
} from './handlers';
|
||||
import * as module from './hooks';
|
||||
|
||||
@@ -88,21 +78,6 @@ export const updateFormField = ({ dispatch, key }) => useCallback(
|
||||
[],
|
||||
);
|
||||
|
||||
/**
|
||||
* currentValue({ key, formValue })
|
||||
* Returns the current display value based on the form value.
|
||||
* If duration, uses durationValue to transform the formValue
|
||||
* @param {string} key - redux video state key
|
||||
* @param {any} formValue - current value in the redux
|
||||
* @return {any} - to-local translation of formValue
|
||||
*/
|
||||
export const currentValue = ({ key, formValue }) => {
|
||||
if (key === selectorKeys.duration) {
|
||||
return durationValue(formValue);
|
||||
}
|
||||
return formValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* valueHooks({ dispatch, key })
|
||||
* returns local and redux state associated with the given data key, as well as methods
|
||||
@@ -118,16 +93,11 @@ export const currentValue = ({ key, formValue }) => {
|
||||
*/
|
||||
export const valueHooks = ({ dispatch, key }) => {
|
||||
const formValue = useSelector(selectors.video[key]);
|
||||
const initialValue = useMemo(() => module.currentValue({ key, formValue }), []);
|
||||
const [local, setLocal] = module.state[key](initialValue);
|
||||
const [local, setLocal] = module.state[key](formValue);
|
||||
const setFormValue = module.updateFormField({ dispatch, key });
|
||||
|
||||
useEffect(() => {
|
||||
if (key === selectorKeys.duration) {
|
||||
setLocal(durationValue(formValue));
|
||||
} else {
|
||||
setLocal(formValue);
|
||||
}
|
||||
setLocal(formValue);
|
||||
}, [formValue]);
|
||||
|
||||
const setAll = useCallback(
|
||||
@@ -264,63 +234,6 @@ export const objectWidget = ({ dispatch, key }) => {
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* durationWidget({ dispatch, key })
|
||||
* Returns the value-tied hooks for the video duration widget.
|
||||
* Includes onChange, and onBlur. blur changes local and redux state, on-change affects
|
||||
* only local state.
|
||||
* The creators from this widget will require an index to provide the final event-handler.
|
||||
* @param {func} dispatch - redux dispatch method
|
||||
* @param {string} key - redux video shape key
|
||||
* @return {object} - state hooks
|
||||
* formValue - value state in redux
|
||||
* setFormValue - sets form field in redux
|
||||
* local - value state in hook
|
||||
* setLocal - sets form field in hook
|
||||
* setAll - sets form field in hook AND redux
|
||||
* onChange(index) - handle input change by updating local state
|
||||
* onBlur(index) - handle input blur by updating local and redux states
|
||||
* onClear(index) - handle clear event by setting value to empty string
|
||||
*/
|
||||
export const durationWidget = ({ dispatch }) => {
|
||||
const widget = module.valueHooks({ dispatch, key: selectorKeys.duration });
|
||||
const {
|
||||
formValue,
|
||||
local,
|
||||
setFormField,
|
||||
setLocal,
|
||||
} = widget;
|
||||
console.log('test', widget)
|
||||
return {
|
||||
...widget,
|
||||
onBlur: useCallback(
|
||||
handleIndexEvent({
|
||||
handler: onValue,
|
||||
transform: updateDuration(widget),
|
||||
}),
|
||||
[formValue, local, setFormField],
|
||||
),
|
||||
onChange: useCallback(
|
||||
handleIndexTransformEvent({
|
||||
handler: onValue,
|
||||
setter: setLocal,
|
||||
transform: onDurationChange,
|
||||
local,
|
||||
}),
|
||||
[local],
|
||||
),
|
||||
onKeyDown: useCallback(
|
||||
handleIndexTransformEvent({
|
||||
handler: onEvent,
|
||||
setter: setLocal,
|
||||
transform: onDurationKeyDown,
|
||||
local,
|
||||
}),
|
||||
[local],
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* widgetValues({ fields, dispatch })
|
||||
* widget value populator, that takes a fields mapping (dataKey: widgetFn) and dispatch
|
||||
@@ -339,7 +252,6 @@ export const widgetValues = ({ fields, dispatch }) => Object.keys(fields).reduce
|
||||
|
||||
export default {
|
||||
arrayWidget,
|
||||
durationWidget,
|
||||
genericWidget,
|
||||
objectWidget,
|
||||
selectorKeys,
|
||||
|
||||
@@ -5,7 +5,6 @@ import { keyStore } from '../../../../../utils';
|
||||
import { actions, selectors } from '../../../../../data/redux';
|
||||
import { MockUseState } from '../../../../../../testUtils';
|
||||
|
||||
import * as duration from './duration';
|
||||
import * as handlers from './handlers';
|
||||
import * as hooks from './hooks';
|
||||
|
||||
@@ -17,13 +16,6 @@ jest.mock('react', () => ({
|
||||
useMemo: jest.fn((cb, prereqs) => ({ useMemo: { cb, prereqs } })),
|
||||
}));
|
||||
|
||||
jest.mock('./duration', () => ({
|
||||
onDurationChange: jest.fn(value => ({ onDurationChange: value })),
|
||||
onDurationKeyDown: jest.fn(value => ({ onDurationKeyDown: value })),
|
||||
updateDuration: jest.fn(value => ({ updateDuration: value })),
|
||||
durationValue: jest.fn(value => ({ durationValue: value })),
|
||||
}));
|
||||
|
||||
jest.mock('./handlers', () => ({
|
||||
handleIndexEvent: jest.fn(args => ({ handleIndexEvent: args })),
|
||||
handleIndexTransformEvent: jest.fn(args => ({ handleIndexTransformEvent: args })),
|
||||
@@ -56,8 +48,6 @@ jest.mock('../../../../../data/redux', () => ({
|
||||
}));
|
||||
|
||||
const keys = {
|
||||
duration: keyStore(duration),
|
||||
handlers: keyStore(handlers),
|
||||
hooks: keyStore(hooks),
|
||||
selectors: hooks.selectorKeys,
|
||||
};
|
||||
@@ -126,18 +116,6 @@ describe('Video Settings modal hooks', () => {
|
||||
}));
|
||||
});
|
||||
});
|
||||
describe('currentValue', () => {
|
||||
it('returns duration display of form value if is duration key', () => {
|
||||
expect(
|
||||
hooks.currentValue({ key: keys.selectors.duration, formValue: testValue }),
|
||||
).toEqual(duration.durationValue(testValue));
|
||||
});
|
||||
it('returns the raw formValue by default', () => {
|
||||
expect(
|
||||
hooks.currentValue({ key: testKey, formValue: testValue }),
|
||||
).toEqual(testValue);
|
||||
});
|
||||
});
|
||||
describe('valueHooks', () => {
|
||||
let formValue;
|
||||
beforeEach(() => {
|
||||
@@ -150,13 +128,6 @@ describe('Video Settings modal hooks', () => {
|
||||
expect(useEffect).toHaveBeenCalled();
|
||||
expect(useEffect.mock.calls[0][1]).toEqual([formValue]);
|
||||
});
|
||||
test('calls setLocal with durationValue(formValue) if is duration', () => {
|
||||
hooks.valueHooks({ dispatch, key: keys.selectors.duration });
|
||||
useEffect.mock.calls[0][0]();
|
||||
expect(state.setState[keys.selectors.duration]).toHaveBeenCalledWith(
|
||||
duration.durationValue(useSelector(selectors.video.duration)),
|
||||
);
|
||||
});
|
||||
test('calls setLocal with formValue by default', () => {
|
||||
hooks.valueHooks({ dispatch, key: testKey });
|
||||
useEffect.mock.calls[0][0]();
|
||||
@@ -165,13 +136,10 @@ describe('Video Settings modal hooks', () => {
|
||||
});
|
||||
});
|
||||
describe('returned object', () => {
|
||||
const mockCurrentValue = (args) => ({ currentValue: args });
|
||||
const mockUpdateFormField = (args) => jest.fn(
|
||||
(val) => ({ updateFormField: { args, val } }),
|
||||
);
|
||||
beforeEach(() => {
|
||||
jest.spyOn(hooks, keys.hooks.currentValue)
|
||||
.mockImplementationOnce(mockCurrentValue);
|
||||
jest.spyOn(hooks, keys.hooks.updateFormField)
|
||||
.mockImplementationOnce(mockUpdateFormField);
|
||||
out = hooks.valueHooks({ dispatch, key: testKey });
|
||||
@@ -180,10 +148,9 @@ describe('Video Settings modal hooks', () => {
|
||||
expect(out.formValue).toEqual(useSelector(selectors.video[testKey]));
|
||||
});
|
||||
describe('local and setLocal', () => {
|
||||
test('keyed to state, initialized with memo of currentValue that never updates', () => {
|
||||
test('keyed to state, initialized with formValue', () => {
|
||||
const { local, setLocal } = out;
|
||||
expect(local.useMemo.cb()).toEqual(mockCurrentValue({ key: testKey, formValue }));
|
||||
expect(local.useMemo.prereqs).toEqual([]);
|
||||
expect(local).toEqual(formValue);
|
||||
setLocal(testValue);
|
||||
expect(state.setState[testKey]).toHaveBeenCalledWith(testValue);
|
||||
});
|
||||
@@ -315,60 +282,6 @@ describe('Video Settings modal hooks', () => {
|
||||
}));
|
||||
});
|
||||
});
|
||||
describe('durationWidget', () => {
|
||||
beforeEach(() => {
|
||||
out = hooks.durationWidget({ dispatch });
|
||||
});
|
||||
it('forwards widget values', () => {
|
||||
expect(out.formValue).toEqual(widgetValues.formValue);
|
||||
expect(out.local).toEqual(widgetValues.local);
|
||||
});
|
||||
describe('onBlur', () => {
|
||||
test('memoized callback based on formValue, local, and setFormValue from widget', () => {
|
||||
expect(out.onBlur.useCallback.prereqs).toEqual(
|
||||
[widgetValues.formValue, widgetValues.local, widgetValues.setFormField],
|
||||
);
|
||||
});
|
||||
test('calls handleIndexEvent with updateDuration', () => {
|
||||
expect(out.onBlur.useCallback.cb).toEqual(
|
||||
handlers.handleIndexEvent({
|
||||
handler: handlers.onValue,
|
||||
transform: duration.updateDuration(widgetValues),
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('onChange', () => {
|
||||
test('memoized callback based on local from widget', () => {
|
||||
expect(out.onChange.useCallback.prereqs).toEqual([widgetValues.local]);
|
||||
});
|
||||
test('calls handleIndexTransformEvent with setLocal', () => {
|
||||
expect(out.onChange.useCallback.cb).toEqual(
|
||||
handlers.handleIndexTransformEvent({
|
||||
handler: handlers.onValue,
|
||||
setter: widgetValues.setLocal,
|
||||
transform: duration.onDurationChange,
|
||||
local: widgetValues.local,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('onKeyDown', () => {
|
||||
test('memoized callback based on local from widget', () => {
|
||||
expect(out.onKeyDown.useCallback.prereqs).toEqual([widgetValues.local]);
|
||||
});
|
||||
test('calls handleIndexTransformEvent with setLocal', () => {
|
||||
expect(out.onKeyDown.useCallback.cb).toEqual(
|
||||
handlers.handleIndexTransformEvent({
|
||||
handler: handlers.onEvent,
|
||||
setter: widgetValues.setLocal,
|
||||
transform: duration.onDurationKeyDown,
|
||||
local: widgetValues.local,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('widgetValues', () => {
|
||||
describe('returned object', () => {
|
||||
|
||||
@@ -3,7 +3,7 @@ import * as urls from './urls';
|
||||
import { get, post, deleteObject } from './utils';
|
||||
import * as module from './api';
|
||||
import * as mockApi from './mockApi';
|
||||
import { durationFromValue } from '../../../containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/hooks';
|
||||
import { durationStringFromValue } from '../../../containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/hooks';
|
||||
|
||||
export const apiMethods = {
|
||||
fetchBlockById: ({ blockId, studioEndpointUrl }) => get(
|
||||
@@ -176,8 +176,8 @@ export const apiMethods = {
|
||||
track: '', // TODO Downloadable Transcript URL. Backend expects a file name, for example: "something.srt"
|
||||
show_captions: content.showTranscriptByDefault,
|
||||
handout: content.handout,
|
||||
start_time: durationFromValue(content.duration.startTime),
|
||||
end_time: durationFromValue(content.duration.stopTime),
|
||||
start_time: durationStringFromValue(content.duration.startTime),
|
||||
end_time: durationStringFromValue(content.duration.stopTime),
|
||||
license: module.processLicense(content.licenseType, content.licenseDetails),
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user