Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flow type components #235

Merged
merged 19 commits into from
Aug 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
9e1794c
refactor(user): do not throw error if invalid user app_metadata encou…
landonreed Aug 9, 2018
b6d6d5a
refactor(user-admin): clean up user admin UI
landonreed Aug 9, 2018
562bf4d
test(flow): add flow typing for components
landonreed Aug 17, 2018
16e2fae
Merge branch 'user-admin-fixes' into flow-type-components
landonreed Aug 17, 2018
c8669f4
test(flow): fix lint and add flow to final admin/editor components
landonreed Aug 17, 2018
4570605
refactor(misc): fix NPE bug introduced on missing activeProject
landonreed Aug 20, 2018
9b501a5
test(flow): add flow typing to alert components
landonreed Aug 20, 2018
a18e3fd
test(flow): update entity flow type
landonreed Aug 20, 2018
444ed42
test(flow): add flow to common components
landonreed Aug 20, 2018
c3b9891
test(flow): add flow to remaining components, fix issues introduced b…
landonreed Aug 21, 2018
aeb46f8
test(end-to-end): tweaks to account for delays in modal opening/clicks
landonreed Aug 21, 2018
68735d5
fix(feeds): handle missing project when fetching project's feed sources
landonreed Aug 21, 2018
65ab27d
refactor(mtc): add back processing field for feed version
landonreed Aug 21, 2018
6354d5b
test(flow): add flow notation to file
landonreed Aug 21, 2018
d79140b
refactor(pr-review): address PR comments
landonreed Aug 23, 2018
73814cc
style(timetable): simplify function signatures for flow clarity
landonreed Aug 23, 2018
423b21f
fix(user-admin): fix create user bug introduced during flow refactor
landonreed Aug 27, 2018
b525a6a
feat(job-status): check for pre-existing server jobs when sidebar mounts
landonreed Aug 27, 2018
17fd1cf
refactor(props): alphabetize flow props
landonreed Aug 28, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 17 additions & 17 deletions __tests__/end-to-end.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,8 @@ async function expectSelectorToNotContainHtml (selector: string, html: string) {
async function createProject (projectName: string) {
log.info(`creating project with name: ${projectName}`)
await click('#context-dropdown')
await waitForSelector('a[href="/project"]')
await click('a[href="/project"]')
await waitForSelector('[data-test-id="create-new-project-button"]')
await wait(2000, 'for projects to load')
await click('[data-test-id="create-new-project-button"]')
await waitForAndClick('a[href="/project"]')
await waitForAndClick('[data-test-id="create-new-project-button"]')
await waitForSelector('.project-name-editable input')
await page.type('.project-name-editable input', projectName)
await click('.project-name-editable button')
Expand All @@ -172,17 +169,15 @@ async function deleteProject (projectId: string) {
await goto(`http://localhost:9966/project/${projectId}/settings`)

// delete that project
await waitForSelector('[data-test-id="delete-project-button"]')
await click('[data-test-id="delete-project-button"]')
await waitForSelector('[data-test-id="modal-confirm-ok-button"]')
await click('[data-test-id="modal-confirm-ok-button"]')
await waitForAndClick('[data-test-id="delete-project-button"]')
await wait(500, 'for modal to appear')
await waitForAndClick('[data-test-id="modal-confirm-ok-button"]')
log.info('deleted project')

// verify deletion
await goto(`http://localhost:9966/project/${projectId}`)
await waitForSelector('.project-not-found')
await expectSelectorToContainHtml('.project-not-found', projectId)
await click('[data-test-id="status-modal-close-button"]')
log.info(`confirmed successful deletion of project with id ${projectId}`)
}

Expand Down Expand Up @@ -410,14 +405,15 @@ async function appendText (selector: string, text: string) {

async function waitForSelector (selector: string, options?: any) {
const startTime = new Date()
await wait(100, 'delay before looking for selector...')
log.info(`waiting for selector: ${selector}`)
await page.waitForSelector(selector, options)
log.info(`selector ${selector} took ${formatSecondsElapsed(startTime)}`)
}

async function click (selector: string) {
log.info(`clicking selector: ${selector}`)
await page.click(selector)
await page.click(selector) // , {delay: 3})
}

async function waitForAndClick (selector: string, waitOptions?: any) {
Expand All @@ -441,9 +437,10 @@ describe('end-to-end', () => {
// Ping the otp endpoint to ensure the server is running.
try {
log.info(`Pinging OTP at ${OTP_ROOT}`)
const response = await fetch(`${OTP_ROOT}`)
if (response.status !== 200) throw new Error('OTP not ready!')
else log.info('OTP is OK.')
await fetch(`${OTP_ROOT}`)
log.info('OTP is OK.')
// if (response.status !== 200) throw new Error('OTP not ready!')
// else log.info('OTP is OK.')
} catch (e) {
if (testOptions.failFast) {
log.error('OpenTripPlanner not accepting requests. Exiting due to fail fast option.')
Expand Down Expand Up @@ -477,15 +474,18 @@ describe('end-to-end', () => {
log.info('Chromium closed.')
})

/// Begin tests

makeTest('should load the page', async () => {
await goto('http://localhost:9966')
await waitForSelector('h1')
await expectSelectorToContainHtml('h1', 'Conveyal Datatools')
testResults['should load the page'] = true
})

makeTest('should login', async () => {
await goto('http://localhost:9966')
await click('[data-test-id="header-log-in-button"]')
await waitForAndClick('[data-test-id="header-log-in-button"]')
await waitForSelector('button[class="auth0-lock-submit"]')
await page.type('input[class="auth0-lock-input"][name="email"]', config.username)
await page.type('input[class="auth0-lock-input"][name="password"]', config.password)
Expand Down Expand Up @@ -520,7 +520,7 @@ describe('end-to-end', () => {

makeTestPostLogin('should update a project by adding a otp server', async () => {
// open settings tab
await click('#project-viewer-tabs-tab-settings')
await waitForAndClick('#project-viewer-tabs-tab-settings')

// navigate to deployments
await waitForAndClick('[data-test-id="deployment-settings-link"]', { visible: true })
Expand Down Expand Up @@ -645,7 +645,7 @@ describe('end-to-end', () => {
// set fetch url
await page.type(
'[data-test-id="feed-source-url-input-group"] input',
'https://github.com/catalogueglobal/datatools-ui/raw/end-to-end/configurations/end-to-end/test-gtfs-to-fetch.zip'
'https://github.com/catalogueglobal/datatools-ui/raw/dev/configurations/end-to-end/test-gtfs-to-fetch.zip'
)
await click('[data-test-id="feed-source-url-input-group"] button')
await wait(2000, 'for feed source to update')
Expand Down
48 changes: 26 additions & 22 deletions lib/admin/components/CreateUser.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ import UserSettings from './UserSettings'
import UserPermissions from '../../common/user/UserPermissions'
import {getComponentMessages, getMessage} from '../../common/util/config'

import type {UserState} from '../../manager/reducers/user'
import type {Organization, Project} from '../../types'

type Props = {
createUser: ({email: string, password: string, permissions: UserPermissions}) => void,
fetchProjectFeeds: string => void,
projects: Array<Project>,
creatingUser: boolean,
creatingUser: UserState,
organizations: Array<Organization>
}

Expand All @@ -24,39 +25,37 @@ type State = {
showModal: boolean
}

const DEFAULT_STATE = {
email: '',
password: '',
showModal: false
}

export default class CreateUser extends Component<Props, State> {
state = {
email: '',
password: '',
showModal: false
}
state = DEFAULT_STATE

save = (e: any) => {
save = (e: SyntheticInputEvent<HTMLInputElement>) => {
e.preventDefault()
if (!e.target.checkValidity()) {
console.warn('Form inputs are invalid!')
return
}
this.setState({showModal: false})
const {email, password} = this.state
this.props.createUser({
email,
password,
permissions: this.refs.userSettings.getSettings()
})
this.setState(DEFAULT_STATE)
}

cancel () {
this.setState({
showModal: false
})
_updateField = (evt: SyntheticInputEvent<HTMLInputElement>) => {
this.setState({[evt.target.name]: evt.target.value})
}

open = () => {
this.setState({
showModal: true
})
}
cancel = () => this.setState(DEFAULT_STATE)

open = () => this.setState({showModal: true})

render () {
const messages = getComponentMessages('CreateUser')
Expand All @@ -66,6 +65,7 @@ export default class CreateUser extends Component<Props, State> {
projects,
fetchProjectFeeds
} = this.props
const {email, password, showModal} = this.state
return (
<div>
<Button
Expand All @@ -75,7 +75,9 @@ export default class CreateUser extends Component<Props, State> {
<Icon type='plus' />{' '}
Create User
</Button>
<Modal show={this.state.showModal} onHide={this.cancel.bind(this)}>
<Modal
show={showModal}
onHide={this.cancel}>
<Modal.Header closeButton>
<Modal.Title>Create User</Modal.Title>
</Modal.Header>
Expand All @@ -84,18 +86,20 @@ export default class CreateUser extends Component<Props, State> {
<FormGroup controlId='formControlsEmail'>
<ControlLabel>Email Address</ControlLabel>
<FormControl
ref='email'
name='email'
autoFocus
value={this.state.email}
onChange={this._updateField}
value={email}
autoComplete='username email'
type='email'
placeholder='Enter email' />
</FormGroup>
<FormGroup controlId='formControlsPassword'>
<ControlLabel>Password</ControlLabel>
<FormControl
ref='password'
value={this.state.password}
name='password'
onChange={this._updateField}
value={password}
minLength='8'
type='password'
placeholder='Enter password for new user'
Expand Down
61 changes: 32 additions & 29 deletions lib/admin/components/OrganizationList.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,35 @@
// @flow

import Icon from '@conveyal/woonerf/components/icon'
import React, { PropTypes, Component} from 'react'
import React, {Component} from 'react'
import { Panel, Modal, Row, Col, Button, FormControl, Label, ListGroup, ListGroupItem, Image } from 'react-bootstrap'
import validator from 'validator'

import UserPermissions from '../../common/user/UserPermissions'
import { getComponentMessages, getMessage } from '../../common/util/config'
import OrganizationSettings from './OrganizationSettings'

export default class OrganizationList extends Component {
static propTypes = {
// userSearch: PropTypes.func,
// page: PropTypes.number,
// perPage: PropTypes.number,
// userCount: PropTypes.number,
// projects: PropTypes.array,
// fetchProjectFeeds: PropTypes.func,
// createUser: PropTypes.func,
// setPage: PropTypes.func,
// isFetching: PropTypes.bool,
organizations: PropTypes.object,
users: PropTypes.object
// setUserPermission: PropTypes.func,
// saveUser: PropTypes.func,
// deleteUser: PropTypes.func,
// token: PropTypes.string
}
import type {OrganizationsState} from '../reducers/organizations'
import type {UsersState} from '../reducers/users'
import type {Organization, UserProfile, Project} from '../../types'

type Props = {
createOrganization: any => Promise<any>,
deleteOrganization: Organization => void,
fetchOrganizations: () => void,
saveOrganization: Organization => void,
isFetching: boolean,
organizations: OrganizationsState,
projects: Array<Project>,
updateOrganization: (Organization, any) => void,
users: UsersState
}

type State = {
showModal?: boolean
}

export default class OrganizationList extends Component<Props, State> {
state = {}

componentWillMount () {
Expand Down Expand Up @@ -82,15 +86,15 @@ export default class OrganizationList extends Component {
</ListGroupItem>
: organizations.data && organizations.data.map((organization, i) => {
const orgUsers = users.data ? users.data.filter(u => {
const permissions = new UserPermissions(u.app_metadata && u.app_metadata.datatools ? u.app_metadata.datatools : null)
const permissions = new UserPermissions(u.app_metadata && u.app_metadata.datatools)
return permissions.getOrganizationId() === organization.id
}) : []
return (
<OrganizationRow
{...this.props}
organization={organization}
key={i}
users={orgUsers} />
orgUsers={orgUsers} />
)
})
}
Expand Down Expand Up @@ -119,12 +123,11 @@ export default class OrganizationList extends Component {
}
}

class OrganizationRow extends Component {
static propTypes = {
organization: PropTypes.object,
users: PropTypes.array
}

class OrganizationRow extends Component<Props & {
organization: Organization,
orgUsers: Array<UserProfile>
},
{isEditing: boolean}> {
state = {
isEditing: false
}
Expand Down Expand Up @@ -152,7 +155,7 @@ class OrganizationRow extends Component {
render () {
const {
organization,
users
orgUsers
} = this.props
return (
<ListGroupItem
Expand All @@ -167,7 +170,7 @@ class OrganizationRow extends Component {
<Col xs={8} sm={5} md={6}>
<h5>
{organization.name}{' '}
{users.length ? <Label>{users.length} users</Label> : null}
{orgUsers.length ? <Label>{orgUsers.length} users</Label> : null}
</h5>
<small />
</Col>
Expand Down
Loading