Skip to content

Commit

Permalink
parse expression fix
Browse files Browse the repository at this point in the history
  • Loading branch information
one-more committed Apr 29, 2017
1 parent b3196e8 commit a346446
Show file tree
Hide file tree
Showing 10 changed files with 188 additions and 36 deletions.
2 changes: 1 addition & 1 deletion dist/bundle.css

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions dist/bundle.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@
"cronstrue": "^0.10.3",
"react-if": "^2.1.0",
"react-select": "^1.0.0-rc.2",
"react-bem-helper": "^1.4.1",
"normalize.css": "^6.0.0"
"react-bem-helper": "^1.4.1"
},
"devDependencies": {
"@kadira/storybook": "^2.35.3",
Expand All @@ -30,6 +29,7 @@
"babel-eslint": "^7.2.2",
"babel-jest": "^17.0.2",
"babel-loader": "*",
"normalize.css": "^6.0.0",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-decorators": "^6.13.0",
"babel-plugin-transform-decorators-legacy": "^1.3.4",
Expand Down
1 change: 0 additions & 1 deletion src/CronBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import PeriodicallyTab from './components/PeriodicallyTab'
import PeriodicallyFrameTab from './components/PeriodicallyFrameTab'
import FixedTimeTab from './components/FixedTimeTab'

import 'normalize.css'
import './cron-builder.styl'

const styleNameFactory = new BEMHelper('cron-builder');
Expand Down
111 changes: 102 additions & 9 deletions src/Cronbuilder.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ describe('CronBuilder', () => {
expect(wrapper.instance().presetComponent.state).toEqual({
minutes: ['5', '15', '25'],
hours: '2',
dayOfWeek: '1-5',
dayOfWeek: ['1-5'],
dayOfMonth: EVERY,
month: EVERY,
activeTime: MINUTES,
Expand All @@ -48,15 +48,15 @@ describe('CronBuilder', () => {
cronExpression={'25 17-21 4 2 6-7'}
/>);
expect(wrapper.instance().presetComponent.state).toEqual({
minutes: '25',
minutes: ['25'],
hours: '17-21',
hoursFrom: '17',
hoursTo: '21',
dayOfWeek: '6-7',
dayOfMonth: '4',
month: '2',
dayOfWeek: ['6-7'],
dayOfMonth: ['4'],
month: ['2'],
activeTime: MINUTES,
minutesMultiple: false,
minutesMultiple: true,
hoursMultiple: false
});
expect(wrapper.state().activeIndex).toEqual(1);
Expand All @@ -78,9 +78,9 @@ describe('CronBuilder', () => {
expect(wrapper.instance().presetComponent.state).toEqual({
minutes: '48',
hours: '6',
dayOfWeek: '2',
dayOfMonth: '24',
month: '6',
dayOfWeek: ['2'],
dayOfMonth: ['24'],
month: ['6'],
activeTime: MINUTES,
minutesMultiple: true,
hoursMultiple: true
Expand All @@ -94,5 +94,98 @@ describe('CronBuilder', () => {
/>);
wrapper.find('[data-action]').simulate('click');
expect(wrapper.find('[data-result]')).toHaveLength(0)
});

it('should should correctly parse single value when it is not every', () => {
const wrapper = mount(<CronBuilder
cronExpression={'48 6 24 6 2'}
showResult={false}
/>);
expect(wrapper.instance().presetComponent.state).toEqual({
minutes: ['48'],
hours: ['6'],
dayOfWeek: ['2'],
dayOfMonth: ['24'],
month: ['6'],
activeTime: MINUTES,
minutesMultiple: true,
hoursMultiple: true
});
});

it('frame tab should correctly parse multiple hours', () => {
const wrapper = mount(<CronBuilder
cronExpression={'48 6 24 6 2'}
showResult={false}
/>);
wrapper.find(Tab).at(1).simulate('click');
expect(wrapper.instance().presetComponent.state).toEqual({
minutes: ['48'],
hours: '6-18',
hoursFrom: '6',
hoursTo: '18',
dayOfWeek: ['2'],
dayOfMonth: ['24'],
month: ['6'],
activeTime: MINUTES,
minutesMultiple: true,
hoursMultiple: true
});
});

it('frame tab should correctly parse single hours', () => {
const wrapper = mount(<CronBuilder
cronExpression={'48 */6 24 6 2'}
showResult={false}
/>);
wrapper.find(Tab).at(1).simulate('click');
expect(wrapper.instance().presetComponent.state).toEqual({
minutes: ['48'],
hours: '6-18',
hoursFrom: '6',
hoursTo: '18',
dayOfWeek: ['2'],
dayOfMonth: ['24'],
month: ['6'],
activeTime: MINUTES,
minutesMultiple: true,
hoursMultiple: false
});
});

it('periodically tab should parse range hours to single', () => {
const wrapper = mount(<CronBuilder
cronExpression={'48 12-20 24 6 2'}
showResult={false}
/>);
wrapper.find(Tab).at(0).simulate('click');
expect(wrapper.instance().presetComponent.state).toEqual({
minutes: ['48'],
hours: '12',
dayOfWeek: ['2'],
dayOfMonth: ['24'],
month: ['6'],
activeTime: MINUTES,
minutesMultiple: true,
hoursMultiple: false
});
});

it('fixed time tab should parse range hours to single', () => {
const wrapper = mount(<CronBuilder
cronExpression={'48 12-20 24 6 2'}
showResult={false}
/>);
wrapper.find(Tab).at(2).simulate('click');
expect(wrapper.instance().presetComponent.state).toEqual({
minutes: '48',
hours: '12',
dayOfWeek: ['2'],
dayOfMonth: ['24'],
month: ['6'],
activeTime: MINUTES,
minutesMultiple: true,
hoursMultiple: true
});
})
});
11 changes: 7 additions & 4 deletions src/components/FixedTimeTab.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @flow

import React from 'react'
import {toOptions, addLeadingZeroToOption, defaultTo} from 'utils'
import {toOptions, addLeadingZeroToOption, defaultTo, ensureMultiple, rangeHoursToSingle} from 'utils'
import range from 'lodash/range'
import PresetTab from './PresetTab'
import TimeInput from './components/TimeInput'
Expand All @@ -14,9 +14,12 @@ const minutesOptions = toOptions(range(0, 60)).map(addLeadingZeroToOption);
export default class FixedTimeTab extends PresetTab {
constructor(props: PresetTabProps, ctx: Object) {
super(props, ctx);
const {hours, minutes} = this.state;
this.state.hours = defaultTo(hours, '8');
this.state.minutes = defaultTo(minutes, '45');
let {hours, minutes} = this.state;
hours = ensureMultiple(hours, false);
hours = rangeHoursToSingle(hours);
minutes = ensureMultiple(minutes, false);
this.state.hours = defaultTo(String(hours), '8');
this.state.minutes = defaultTo(String(minutes), '45');
this.state.minutesMultiple = true;
this.state.hoursMultiple = true;
}
Expand Down
12 changes: 10 additions & 2 deletions src/components/PeriodicallyFrameTab.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import React from 'react'
import range from 'lodash/range'
import {toOptions, defaultTo} from 'utils'
import {toOptions, defaultTo, ensureMultiple} from 'utils'
import type {Option} from 'types/Option'
import PresetTab from './PresetTab'
import MultipleSwitcher from './MultipleSwitcher'
Expand Down Expand Up @@ -39,13 +39,21 @@ const timeInputProps = {
}
};

const defaultHours = (hours: string, defaultValue: string) => {
const [fromDefault, toDefault] = defaultValue.split('-');
const value = defaultTo(hours, defaultValue);
const [from, to] = value.split('-');
return `${defaultTo(from, fromDefault)}-${defaultTo(to, toDefault)}`
};

export default class PeriodicallyFrameTab extends PresetTab {
constructor(props: PresetTabProps, ctx: Object) {
super(props, ctx);
const {state} = this;
let {hours, minutes} = state;
minutes = defaultTo(minutes, '6');
hours = defaultTo(hours, '9-18');
hours = ensureMultiple(hours, false);
hours = defaultHours(String(hours), '9-18');
const [hoursFrom, hoursTo] = hours.split('-');
this.state = {
...state,
Expand Down
9 changes: 8 additions & 1 deletion src/components/PeriodicallyTab.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
import React from 'react'
import {If, Then, Else} from 'react-if'
import {MINUTES} from 'data/constants'
import {isMultiple, toggleDateType, toOptions} from 'utils'
import {isMultiple, toggleDateType, toOptions, rangeHoursToSingle} from 'utils'
import range from 'lodash/range'
import MultipleSwitcher from './MultipleSwitcher'
import TimeInput from './components/TimeInput'
import DateComponent, {DayOfWeek, DayOfMonth, Month} from './components/DateComponent'
import PresetTab from './PresetTab'
import type {PresetTabState} from './types/PresetTabState'
import type {PresetTabProps} from './types/PresetTabProps'

const minutesOptions = toOptions(range(1, 60));
const hoursOptions = toOptions(range(0, 24));
Expand All @@ -19,6 +20,12 @@ const isMinutes = (activeTime: string) => activeTime === MINUTES;
const timeInputProps = {style: {minWidth: 75}};

export default class PeriodicallyTab extends PresetTab {
constructor(props: PresetTabProps, ctx: Object) {
super(props, ctx);
const {hours} = this.state;
this.state.hours = rangeHoursToSingle(hours)
}


toggleActiveTime = () => {
this.setState(({activeTime}: PresetTabState) => ({
Expand Down
18 changes: 16 additions & 2 deletions src/stories/CronBuilder.stories.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
import {storiesOf} from '@kadira/storybook'
import {storiesOf, action} from '@kadira/storybook'
import React from 'react'
import CronBuilder from '../CronBuilder'

storiesOf('CronBuilder', module)
.add('default', () => <CronBuilder />);
.add('default', () => <CronBuilder
onChange={action('got expression')}
/>)
.add('do not show result', () => <CronBuilder
showResult={false}
onChange={action('got expression')}
/>)
.add('with predefined expression', () => <CronBuilder
cronExpression="*/4 10,20 * * 6-7"
onChange={action('got expression')}
/>)
.add('with predefined expression time frame', () => <CronBuilder
cronExpression="30 9-18 * * 1-5"
onChange={action('got expression')}
/>);
48 changes: 38 additions & 10 deletions src/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import type {CronExpression} from 'types/CronExpression'

import head from 'lodash/head'
import values from 'lodash/values'
import {MINUTES, HOURS} from 'data/constants'
import {EVERY} from "../data/constants";
import {MINUTES, HOURS, EVERY} from 'data/constants'

export const toggleMultiple = (value: any) => {
if(value instanceof Array) {
Expand Down Expand Up @@ -61,24 +60,45 @@ export const generateCronExpression = (expression: CronExpression) => {
return values(expression).join(' ')
};

export const splitMultiple = (value: string) => {
export const splitMultiple = (value: string, field: ?string = undefined) => {
if(value.includes(',')) {
return value.split(',')
}
return value
if(value.includes('/')) {
return value
}
if(value.includes('-') && field === HOURS) {
return value
}
if(value === EVERY) {
return value
}
return [value]
};

export const replaceEvery = (value: string) => value.replace('*/', '');
export const replaceEvery = (value: any) => {
if(typeof value === 'string') {
return value.replace('*/', '')
}
return value
};

export const parseCronExpression = (expression: string) => {
const [minutes, hours, dayOfMonth, month, dayOfWeek] = expression.split(' ');
return {
minutes: splitMultiple(replaceEvery(minutes)),
hours: splitMultiple(replaceEvery(hours)),
const defaultExpression = {
minutes: EVERY,
hours: EVERY,
dayOfMonth: EVERY,
month: EVERY,
dayOfWeek: EVERY
};
return Object.assign(defaultExpression, {
minutes: replaceEvery(splitMultiple(minutes)),
hours: replaceEvery(splitMultiple(hours, HOURS)),
dayOfMonth: splitMultiple(dayOfMonth),
month: splitMultiple(month),
dayOfWeek: splitMultiple(dayOfWeek)
}
})
};

export const addLeadingZero = (el: any) => `0${el}`.slice(-2);
Expand All @@ -92,5 +112,13 @@ export const addLeadingZeroToOption = (option: Option) => {
};

export const defaultTo = (item: string, defaultItem: string) => {
return item === EVERY ? defaultItem : item
return (item === EVERY || !item) ? defaultItem : item
};

export const rangeHoursToSingle = (hours: any) => {
if(hours instanceof Array) {
return hours
}
return hours.split('-')[0]
};

0 comments on commit a346446

Please sign in to comment.