Skip to content

Commit

Permalink
[#58] Cover feedback page with E2E tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrey Romanov committed Mar 31, 2019
1 parent 9c54434 commit c8f88da
Show file tree
Hide file tree
Showing 16 changed files with 206 additions and 119 deletions.
3 changes: 3 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"max": 1
}
],

"import/no-extraneous-dependencies": [
"error",
{
Expand All @@ -48,6 +49,8 @@
]
}
],
"import/no-default-export": 1,

"react/require-extension": "off",
"react/jsx-indent": ["error", "tab"],
"react/jsx-indent-props": ["error", "tab"],
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ static/index.appcache
.idea
.vscode
npm-debug.*
jsconfig.json
52 changes: 52 additions & 0 deletions cypress/integration/feedback.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { eventsPage } from '../objects/events-page';
import { feedbackPage } from '../objects/feedback-page';

describe('Feedback page', () => {
beforeEach(() => {
cy.visit('/');
});

specify('should be available from events page', () => {
eventsPage.root().should('be.visible');
eventsPage.feedbackButton().should('be.visible');
eventsPage.feedbackButton().click();

eventsPage.root().should('not.be.visible');
feedbackPage.root().should('be.visible');
feedbackPage.back().should('be.visible');
feedbackPage.submit().should('be.visible');
feedbackPage.feedbackField().should('be.visible');
feedbackPage.emailField().should('be.visible');
});

specify('should have working back button', () => {
cy.openFeedbackPage();

feedbackPage.back().should('be.visible');
feedbackPage.back().click();

feedbackPage.root().should('not.be.visible');
eventsPage.root().should('be.visible');
});

specify('should return to events page after submit', () => {
cy.openFeedbackPage();

feedbackPage.feedbackField().type('Hello from E2E tests!');
feedbackPage.emailField().type('[email protected]');
feedbackPage.submit().click();

feedbackPage.root().should('not.be.visible');
eventsPage.root().should('be.visible');
});

specify('should be able to submit review without email', () => {
cy.openFeedbackPage();

feedbackPage.feedbackField().type('Hello from E2E tests!');
feedbackPage.submit().click();

feedbackPage.root().should('not.be.visible');
eventsPage.root().should('be.visible');
});
});
4 changes: 4 additions & 0 deletions cypress/objects/events-page.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ class EventsPage {
addEventButton() {
return cy.get('[data-marker="events-page/add-event"]');
}

feedbackButton() {
return cy.get('[data-marker="events-page/feedback"]');
}
}

export const eventsPage = new EventsPage();
27 changes: 27 additions & 0 deletions cypress/objects/feedback-page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
class FeedbackPage {
root() {
return cy.get('[data-marker="feedback-page"]');
}

title() {
return cy.get('[data-marker="feedback-page/title"]');
}

feedbackField() {
return cy.get('[data-marker="feedback-page/feedback-field"]');
}

emailField() {
return cy.get('[data-marker="feedback-page/email-field"]');
}

back() {
return cy.get('[data-marker="feedback-page/back"]');
}

submit() {
return cy.get('[data-marker="feedback-page/submit"]');
}
}

export const feedbackPage = new FeedbackPage();
25 changes: 0 additions & 25 deletions cypress/support/commands.js

This file was deleted.

14 changes: 14 additions & 0 deletions cypress/support/commands/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { eventsPage } from '../../objects/events-page';
import { feedbackPage } from '../../objects/feedback-page';

Cypress.Commands.add('openFeedbackPage', () => {
cy.visit('/');
feedbackPage.root().should('not.be.visible');
eventsPage.root().should('be.visible');
eventsPage.feedbackButton().should('be.visible');

eventsPage.feedbackButton().click();

eventsPage.root().should('not.be.visible');
feedbackPage.root().should('be.visible');
});
24 changes: 11 additions & 13 deletions source/scripts/components/form-input/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,17 @@ import PropTypes from 'prop-types';
import classNames from 'classnames';
import styles from './form-input.css';

export default function FormInput(props) {
const {
id,
type,
size,
disabled,
invalid,
placeholder,
value,
onChange,
...restProps
} = props;

export default function FormInput({
id,
type,
size,
disabled,
invalid,
placeholder,
value,
onChange,
...restProps
}) {
return (
<input
className={classNames(styles.root, styles[`root_size_${size}`], {
Expand Down
45 changes: 23 additions & 22 deletions source/scripts/components/top-bar/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import cn from 'classnames';

export function TopBar(props) {
const classes = ['top-bar'];
Expand All @@ -9,19 +10,16 @@ export function TopBar(props) {
return <div className={classes.join(' ')}>{props.children}</div>;
}

export function TopBarHeading(props) {
const classes = ['top-bar__heading'];

if (props.subtitle) {
classes.push('top-bar__heading--with-subtitle');
}

export function TopBarHeading({ subtitle, title, className, ...restProps }) {
return (
<div className={classes.join(' ')}>
<div className="top-bar__title">{props.title}</div>
{props.subtitle && (
<div className="top-bar__subtitle">{props.subtitle}</div>
)}
<div
{...restProps}
className={cn(className, 'top-bar__heading', {
'top-bar__heading--with-subtitle': subtitle,
})}
>
<div className="top-bar__title">{title}</div>
{subtitle && <div className="top-bar__subtitle">{subtitle}</div>}
</div>
);
}
Expand All @@ -31,18 +29,20 @@ TopBarHeading.propTypes = {
subtitle: PropTypes.string,
};

export function TopBarIcon(props) {
const baseClass = 'top-bar__icon';
const classes = [baseClass, `${baseClass}--${props.icon}`];

if (props.disabled) {
classes.push(`${baseClass}--disabled`);
}

export function TopBarIcon({
className,
icon,
disabled,
onClick,
...restProps
}) {
return (
<div
className={classes.join(' ')}
onClick={!props.disabled && props.onClick}
{...restProps}
className={cn(className, 'top-bar__icon', `top-bar__icon--${icon}`, {
'top-bar__icon--disabled': disabled,
})}
onClick={!disabled && onClick}
/>
);
}
Expand All @@ -65,6 +65,7 @@ TopBarIcon.propTypes = {
'mail',
'bordered-plus',
]).isRequired,
className: PropTypes.string,
onClick: PropTypes.func,
disabled: PropTypes.bool,
};
12 changes: 12 additions & 0 deletions source/scripts/pages/events/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { EventsPage as EventsPageView } from './view';
import withRouter from 'react-router/lib/withRouter';
import { connect } from 'react-redux';

const mapStateToProps = ({ events }) => ({
isFetchingEvents: events.isFetchingEvents,
events: events.events,
eventsById: events.eventsById,
localEvents: events.localEvents,
});

export const EventsPage = connect(mapStateToProps)(withRouter(EventsPageView));
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
import React, { PureComponent } from 'react';
import withRouter from 'react-router/lib/withRouter';
import { connect } from 'react-redux';

import Page from '../components/page';
import { TopBar, TopBarHeading, TopBarIcon } from '../components/top-bar';
import EventsListItem from '../components/events-list-item';
import ActionButton from '../components/action-button';
import readEvents from '../actions/read-events';
import FlexContainer from '../components/flex-container';
import Poster from '../components/poster';
import Spinner from '../components/spinner';
import changeCurrentEvent from '../actions/change-current-event';
import { getEventBalance } from '../modules/balance';
import getLocalEvents from '../actions/get-local-events';

class EventsPage extends PureComponent {
import Page from '~/components/page';
import { TopBar, TopBarHeading, TopBarIcon } from '~/components/top-bar';
import EventsListItem from '~/components/events-list-item';
import ActionButton from '~/components/action-button';
import readEvents from '~/actions/read-events';
import FlexContainer from '~/components/flex-container';
import Poster from '~/components/poster';
import Spinner from '~/components/spinner';
import changeCurrentEvent from '~/actions/change-current-event';
import { getEventBalance } from '~/modules/balance';
import getLocalEvents from '~/actions/get-local-events';

function getEventData(eventsById) {
return eventId => ({
eventId,
data: eventsById[eventId],
});
}

export class EventsPage extends PureComponent {
state = {
balance: {},
};
Expand Down Expand Up @@ -95,7 +99,11 @@ class EventsPage extends PureComponent {
<Page.Header>
<TopBar bordered>
<TopBarHeading title="Мероприятия" />
<TopBarIcon icon="mail" onClick={this.goToFeedback} />
<TopBarIcon
data-marker="events-page/feedback"
icon="mail"
onClick={this.goToFeedback}
/>
</TopBar>
</Page.Header>

Expand All @@ -117,19 +125,3 @@ class EventsPage extends PureComponent {
);
}
}

function getEventData(eventsById) {
return eventId => ({
eventId,
data: eventsById[eventId],
});
}

const mapStateToProps = ({ events }) => ({
isFetchingEvents: events.isFetchingEvents,
events: events.events,
eventsById: events.eventsById,
localEvents: events.localEvents,
});

export default connect(mapStateToProps)(withRouter(EventsPage));
10 changes: 0 additions & 10 deletions source/scripts/pages/feedback-page.js

This file was deleted.

5 changes: 5 additions & 0 deletions source/scripts/pages/feedback/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import withRouter from 'react-router/lib/withRouter';
import { connect } from 'react-redux';
import { FeedbackPage as FeedbackPageView } from './view';

export const FeedbackPage = connect()(withRouter(FeedbackPageView));
Loading

2 comments on commit c8f88da

@vercel
Copy link

@vercel vercel bot commented on c8f88da Dec 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on c8f88da Dec 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.