Skip to content

Commit

Permalink
Merge pull request #2163 from coursemology-collab/allen/assessment-edit
Browse files Browse the repository at this point in the history
Assessment edit page fixes
  • Loading branch information
kxmbrian authored Mar 23, 2017
2 parents ac4abbf + 5069782 commit d529ab2
Show file tree
Hide file tree
Showing 17 changed files with 340 additions and 66 deletions.
2 changes: 2 additions & 0 deletions app/views/course/assessment/assessments/_edit.json.jbuilder
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ json.attributes do
end

json.mode_switching @assessment.allow_mode_switching?
json.gamified current_course.gamified?

json.folder_attributes do
json.folder_id @assessment.folder.id
json.materials @assessment.materials.order(:name) do |material|
Expand Down
2 changes: 1 addition & 1 deletion app/views/course/assessment/assessments/index.html.slim
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
= page_header format_inline_text(@category.title) do
- if can?(:create, Course::Assessment.new(tab: @tab))
div.pull-right
div.new-btn data="#{{ tab_id: @tab.id, category_id: @category.id }.to_json}"
div.new-btn data="#{{ gamified: current_course.gamified?, tab_id: @tab.id, category_id: @category.id }.to_json}"

= display_assessment_tabs

Expand Down
8 changes: 8 additions & 0 deletions client/.eslintrc.test
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,13 @@
"rules": {
"react/no-find-dom-node": "off",
"react/jsx-filename-extension": "off",
"import/no-extraneous-dependencies": "off",
"import/extensions": "off",
"import/no-unresolved": [
"error",
{
"ignore": [ 'utils/' ]
}
]
},
}
55 changes: 55 additions & 0 deletions client/app/__test__/utils/__test__/shallowUntil.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React, { PropTypes } from 'react';
import shallowUntil from '../shallowUntil';

describe('#shallowUntil', () => {
const Div = () => <div />;
const hoc = Component => () => <Component />;

it('shallow renders the current wrapper one level deep', () => {
const EnhancedDiv = hoc(Div);
const wrapper = shallowUntil(<EnhancedDiv />, 'Div');
expect(wrapper.contains(<div />)).toBeTruthy();
});

it('shallow renders the current wrapper several levels deep', () => {
const EnhancedDiv = hoc(hoc(hoc(Div)));
const wrapper = shallowUntil(<EnhancedDiv />, 'Div');
expect(wrapper.contains(<div />)).toBeTruthy();
});

it('shallow renders the current wrapper even if the selector never matches', () => {
const EnhancedDiv = hoc(Div);
const wrapper = shallowUntil(<EnhancedDiv />, 'NotDiv');
expect(wrapper.contains(<div />)).toBeTruthy();
});

it('stops shallow rendering when it encounters a DOM element', () => {
const wrapper = shallowUntil(<div><Div /></div>, 'Div');
expect(wrapper.contains(<div><Div /></div>)).toBeTruthy();
});

describe('with context', () => {
const Foo = () => <Div />;
Foo.contextTypes = { open: PropTypes.bool.isRequired };

class Bar extends React.Component {
static childContextTypes = { open: PropTypes.bool }
getChildContext = () => ({ open: true })
render = () => <Foo />
}

it('passes down context from the root component', () => {
const EnhancedFoo = hoc(Foo);
const wrapper = shallowUntil(<EnhancedFoo />, { context: { open: true } }, 'Foo');
expect(wrapper.context('open')).toEqual(true);
expect(wrapper.contains(<Div />)).toBeTruthy();
});

it('passes down context from an intermediary component', () => {
const EnhancedBar = hoc(Bar);
const wrapper = shallowUntil(<EnhancedBar />, 'Foo');
expect(wrapper.context('open')).toEqual(true);
expect(wrapper.contains(<Div />)).toBeTruthy();
});
});
});
41 changes: 41 additions & 0 deletions client/app/__test__/utils/shallowUntil.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { shallow } from 'enzyme';

// See https://github.com/airbnb/enzyme/issues/539 and the `until` helper was borrowed from there.
function until(selector, {context} = this.options) {
if (!selector || this.isEmptyRender() || typeof this.node.type === 'string')
return this;

const instance = this.instance();
if (instance.getChildContext) {
context = {
...context,
...instance.getChildContext(),
}
}

return this.is(selector)
? this.shallow({context})
: until.call(this.shallow({context}), selector, {context})
}



/**
* Shallow renders the component until the component matches the selector.
* This is useful when the component you want to test is nested inside another component.
* example:
* ```
* const component = <Provider>
* <MyComponent />
* </Provider>
* ```
* In the above case, `shallow(component)` will render the <Provider />, and
* `shallowUntil(component, 'MyComponent')` will render <MyComponent />
*/
export default function shallowUntil(component, options, selector) {
if (selector === undefined) {
selector = options;
options = undefined;
}
return until.call(shallow(component, options), selector);
}
1 change: 1 addition & 0 deletions client/app/bundles/course/assessment/assessments-edit.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ $(document).ready(() => {
<ProviderWrapper store={store}>
<AssessmentEditPage
modeSwitching={data.mode_switching}
gamified={data.gamified}
folderAttributes={data.folder_attributes}
conditionAttributes={data.condition_attributes}
initialValues={{ ...data.attributes, password_protected: !!data.attributes.password }}
Expand Down
1 change: 1 addition & 0 deletions client/app/bundles/course/assessment/assessments-index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ $(document).ready(() => {
const Page = () => (
<ProviderWrapper store={store}>
<AssessmentIndexPage
gamified={attributes.gamified}
categoryId={attributes.category_id}
tabId={attributes.tab_id}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ class AssessmentForm extends React.Component {
onSubmit: PropTypes.func.isRequired,
// If the Form is in editing mode, `published` button will be displayed.
editing: PropTypes.bool,
// if the EXP fields should be displayed
gamified: PropTypes.bool,
// If allow to switch between autoraded and manually graded mode.
modeSwitching: PropTypes.bool,
folderAttributes: PropTypes.shape({
Expand All @@ -103,6 +105,10 @@ class AssessmentForm extends React.Component {
}),
};

static defaultProps = {
gamified: true,
}

onStartAtChange = (_, newStartAt) => {
const { start_at: startAt, end_at: endAt, bonus_end_at: bonusEndAt, dispatch } = this.props;
const newStartTime = newStartAt && newStartAt.getTime();
Expand Down Expand Up @@ -196,7 +202,7 @@ class AssessmentForm extends React.Component {
}

render() {
const { handleSubmit, onSubmit, modeSwitching, submitting, editing, folderAttributes,
const { handleSubmit, onSubmit, gamified, modeSwitching, submitting, editing, folderAttributes,
conditionAttributes } = this.props;

return (
Expand Down Expand Up @@ -232,35 +238,41 @@ class AssessmentForm extends React.Component {
style={styles.flexChild}
disabled={submitting}
/>
<Field
name="bonus_end_at"
component={DateTimePicker}
floatingLabelText={<FormattedMessage {...translations.bonusEndAt} />}
style={styles.flexChild}
disabled={submitting}
/>
</div>
<div style={styles.flexGroup}>
<Field
name="base_exp"
component={TextField}
floatingLabelText={<FormattedMessage {...translations.baseExp} />}
type="number"
style={styles.flexChild}
disabled={submitting}
/>
<Field
name="time_bonus_exp"
component={TextField}
floatingLabelText={<FormattedMessage {...translations.timeBonusExp} />}
type="number"
style={styles.flexChild}
disabled={submitting}
/>
{
gamified &&
<Field
name="bonus_end_at"
component={DateTimePicker}
floatingLabelText={<FormattedMessage {...translations.bonusEndAt} />}
style={styles.flexChild}
disabled={submitting}
/>
}
</div>
{
gamified &&
<div style={styles.flexGroup}>
<Field
name="base_exp"
component={TextField}
floatingLabelText={<FormattedMessage {...translations.baseExp} />}
type="number"
style={styles.flexChild}
disabled={submitting}
/>
<Field
name="time_bonus_exp"
component={TextField}
floatingLabelText={<FormattedMessage {...translations.timeBonusExp} />}
type="number"
style={styles.flexChild}
disabled={submitting}
/>
</div>
}

{
this.props.editing &&
editing &&
<Field
name="published"
component={Toggle}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,19 @@ jest.mock('lib/components/redux-form/RichTextField', () => {
});

describe('<AssessmentEdit />', () => {
const store = storeCreator({});
const id = 1;
const intitialValues = {
id,
title: 'Assessement',
description: 'Awesome assessment',
autograded: false,
start_at: new Date(),
base_exp: 0,
time_bonus_exp: 0,
};

it('renders the edit page', async () => {
const store = storeCreator({});
const id = 1;
const intitialValues = {
id,
title: 'Assessement',
description: 'Awesome assessment',
autograded: false,
start_at: new Date(),
base_exp: 0,
time_bonus_exp: 0,
};
const editPage = mount(
<ProviderWrapper store={store}>
<AssessmentEdit
Expand Down Expand Up @@ -58,4 +59,37 @@ describe('<AssessmentEdit />', () => {
expect(spy).toHaveBeenCalledWith(id,
{ assessment: { ...intitialValues, title: newTitle, autograded: true, password: null } });
});

it('renders the gamified fields by default', () => {
const editPage = mount(
<ProviderWrapper store={store}>
<AssessmentEdit
id={id}
modeSwitching
initialValues={intitialValues}
/>
</ProviderWrapper>
);

expect(editPage.find('input[name="bonus_end_at"]').length).toBeGreaterThan(0);
expect(editPage.find('input[name="base_exp"]').length).toBeGreaterThan(0);
expect(editPage.find('input[name="time_bonus_exp"]').length).toBeGreaterThan(0);
});

it('does not render the gamified fields', () => {
const editPage = mount(
<ProviderWrapper store={store}>
<AssessmentEdit
id={id}
gamified={false}
modeSwitching
initialValues={intitialValues}
/>
</ProviderWrapper>
);

expect(editPage.find('input[name="bonus_end_at"]')).toHaveLength(0);
expect(editPage.find('input[name="base_exp"]')).toHaveLength(0);
expect(editPage.find('input[name="time_bonus_exp"]')).toHaveLength(0);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ class EditPage extends React.Component {
static propTypes = {
dispatch: PropTypes.func.isRequired,
intl: intlShape,
// If the gamification feature is enabled in the course.
gamified: PropTypes.bool,
// If allow to switch between autoraded and manually graded mode.
modeSwitching: PropTypes.bool,
// An array of materials of current assessment.
Expand Down Expand Up @@ -53,13 +55,14 @@ class EditPage extends React.Component {
};

render() {
const { modeSwitching, initialValues, folderAttributes,
const { gamified, modeSwitching, initialValues, folderAttributes,
conditionAttributes, dispatch } = this.props;

return (
<div>
<AssessmentForm
editing
gamified={gamified}
onSubmit={this.onFormSubmit}
modeSwitching={modeSwitching}
folderAttributes={folderAttributes}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ class PopupDialog extends React.Component {
static propTypes = {
dispatch: PropTypes.func.isRequired,
intl: intlShape,
// If the gamification feature is enabled in the course.
gamified: PropTypes.bool,
categoryId: PropTypes.number.isRequired,
tabId: PropTypes.number.isRequired,
pristine: PropTypes.bool,
Expand Down Expand Up @@ -108,6 +110,7 @@ class PopupDialog extends React.Component {
contentStyle={styles.dialog}
>
<AssessmentForm
gamified={this.props.gamified}
modeSwitching
onSubmit={this.onFormSubmit}
initialValues={initialValues}
Expand Down
21 changes: 0 additions & 21 deletions client/app/lib/components/redux-form/Toggle.js

This file was deleted.

Loading

0 comments on commit d529ab2

Please sign in to comment.