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

Create & Update the 'gardenci' usergroup in slack on plane reassignment #3

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ module.exports = function(config) {
module: {
loaders: [
{ test: /\.jsx?$/, exclude: /node_modules/, loader: 'babel-loader' },
{ test: /\.json$/, loader: 'json-loader' }
],
},
watch: true,
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@
"redux-thunk": "^1.0.3",
"socket.io": "^1.4.5",
"socket.io-client": "^1.4.5",
"whatwg-fetch": "^0.11.0"
"whatwg-fetch": "^0.11.0",
"es6-promise": "^3.1.2",
"fetch-mock": "^4.4.0"
}
}
38 changes: 37 additions & 1 deletion src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,45 @@ import Editor from './Editor';
import reduce from './reducers/';
import { load, andSave, dropCard, assignBadge, updateTrackName, randomize, randomizePlane,toggleLock, addCard, removeCard } from './actions/';
import { List } from 'immutable';
import { updateGroup } from './slack';


const store = createStore(reduce, applyMiddleware(thunkMiddleware))

store.dispatch(load())

function getCiTrack() {
const badges = store.getState().get('badges')
const tracks = badges.keys()

for (var track of tracks) {
if(badges.get(track).indexOf('gardenci') >= 0) {
return track
}
}
return "-1"
}

let currentCiTrack = getCiTrack()

store.subscribe(() => {
const newCiTrack = getCiTrack()
if (newCiTrack != currentCiTrack) {
currentCiTrack = newCiTrack
onCiTrackChanged(newCiTrack)
}
})

function onCiTrackChanged(newCiTrack) {
const state = store.getState()
const assignments = state.get('assignments')
const ciPair = assignments.get(newCiTrack)
const ciPairSlackHandles = ciPair.map(name => state.get("slack").get(name))
updateGroup('gardenci', ciPairSlackHandles, () => {
console.log("slack group 'gardenci' updated to ", ciPairSlackHandles)
})
}

const ConnectedBoard = connect(
state => {
const s = state.toJS()
Expand All @@ -26,6 +60,7 @@ const ConnectedBoard = connect(
return {
name: name,
photo: s["photos"][ name ],
slack: s["slack"][ name ],
locked: s["locked"][ name ],
}
})
Expand Down Expand Up @@ -57,11 +92,12 @@ const ConnectedEditor = connect(
return {
photos: state.get("photos").toJS(),
cards: state.get("assignments").reduce((r, cards) => r.concat(cards), List()).sort().toJS(),
slack: state.get("slack").toJS(),
}
},
dispatch => {
return {
onAdd: (name, photo) => dispatch(andSave(addCard(name, photo))),
onAdd: (name, photo, handle) => dispatch(andSave(addCard(name, photo, handle))),
onRemove: (name) => dispatch(andSave(removeCard(name))),
}
}
Expand Down
10 changes: 9 additions & 1 deletion src/Editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class Editor extends Component {
const rows = this.props.cards.map(card => (
<tr>
<td><input type="text" value={ card } onChange={x => x}></input></td>
<td><input type="text" value={ this.props.slack[card] } onChange={x => x}></input></td>
<td><input size="80" type="text" value={ this.props.photos[card] } onChange={x => x}></input></td>
<td><button style={linkStyle} onClick={() => this.props.onRemove(card)}>Remove</button></td>
</tr>
Expand All @@ -26,13 +27,20 @@ class Editor extends Component {
<div>
<table>
<tbody>
<tr>
<th>Name</th>
<th>Slack Handle</th>
<th>Picture</th>
</tr>
{rows}
<tr>
<td><input value={this.state.newName} onChange={(event) => this.setState({newName: event.target.value})} /></td>
<td><input value={this.state.newHandle} onChange={(event) => this.setState({newHandle: event.target.value})} /></td>
<td><input size="80" value={this.state.newPhoto} onChange={(event) => this.setState({newPhoto: event.target.value})} /></td>
<td><button style={linkStyle} onClick={() => {
this.props.onAdd(this.state.newName, this.state.newPhoto);
this.props.onAdd(this.state.newName, this.state.newPhoto, this.state.newHandle);
this.setState({"newName": ""});
this.setState({"newHandle": ""});
this.setState({"newPhoto": ""});
}}>Add Card</button></td>
</tr>
Expand Down
31 changes: 23 additions & 8 deletions src/__tests__/editor-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,41 +8,56 @@ import Editor from '../Editor';

describe("Editor", function() {
let renderedEditor
let newName, newPhoto
let newName, newPhoto, newHandle

beforeEach(function() {
renderedEditor = renderIntoDocument(<Editor
cards={[ "julz", "gareth" ]}
photos={{ "julz": "some-url", "gareth": "another-url" }}
onAdd={(name, photo) => { newName = name; newPhoto = photo }}
slack={{ "julz": "julz", "gareth": "gsmith" }}
onAdd={(name, photo, handle) => { newName = name; newPhoto = photo; newHandle = handle; }}
/>)
})

it("contains a row for each card, plus the 'new row' bar", function() {
it("contains a header row, a row for each card, plus the 'new row' bar", function() {
const rows = scryRenderedDOMComponentsWithTag(renderedEditor, "tr")
expect(rows.length).toEqual(3)
expect(rows.length).toEqual(4)
})

it("contains card name column", function() {
const rows = scryRenderedDOMComponentsWithTag(renderedEditor, "tr")
expect(rows.map(tr => tr.getElementsByTagName("input")[0].value)).toContain("julz", "gareth")
expect(rows.filter(tr => tr.getElementsByTagName("input").length > 0)
.map(tr => tr.getElementsByTagName("input")[0].value)
).toContain("julz", "gareth")
})

it("contains slack handle column", function() {
const rows = scryRenderedDOMComponentsWithTag(renderedEditor, "tr")
expect(rows.filter(tr => tr.getElementsByTagName("input").length > 0)
.map(tr => tr.getElementsByTagName("input")[1].value)
).toContain("julz", "gsmith")
})

it("contains photo url column", function() {
const rows = scryRenderedDOMComponentsWithTag(renderedEditor, "tr")
expect(rows.map(tr => tr.getElementsByTagName("input")[1].value)).toContain("some-url", "another-url")
expect(rows.filter(tr => tr.getElementsByTagName("input").length > 0)
.map(tr => tr.getElementsByTagName("input")[2].value)
).toContain("some-url", "another-url")
})

it("sends the entered name and photo when the Add Card button is clicked", function() {
it("sends the entered name, slack handle and photo when the Add Card button is clicked", function() {
const rows = scryRenderedDOMComponentsWithTag(renderedEditor, "tr")
const lastRow = rows[rows.length-1]
lastRow.getElementsByTagName("input")[0].value = "foo"
lastRow.getElementsByTagName("input")[1].value = "url"
lastRow.getElementsByTagName("input")[1].value = "@foo"
lastRow.getElementsByTagName("input")[2].value = "url"

Simulate.change(lastRow.getElementsByTagName("input")[0])
Simulate.change(lastRow.getElementsByTagName("input")[1])
Simulate.change(lastRow.getElementsByTagName("input")[2])
Simulate.click(lastRow.getElementsByTagName("button")[0])
expect(newName).toEqual("foo")
expect(newHandle).toEqual("@foo")
expect(newPhoto).toEqual("url")
})
})
10 changes: 4 additions & 6 deletions src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ export function dropCard(card, track) {
}
}

export function addCard(name, photo) {
export function addCard(name, photo, handle) {
return {
type: "ADD_CARD",
name: name,
photo: photo,
slack: handle,
}
}

Expand Down Expand Up @@ -98,12 +99,9 @@ export function randomizePlane() {

const tracksWithPeople = tracks.filter( track => assignments.get(track) && assignments.get(track).count() > 0 )
const i = Math.random() * tracksWithPeople.count()
const trackNumber = tracksWithPeople.get(i)

dispatch({
type: "DROP_BADGE",
target: tracksWithPeople.get(i),
badge: "CI",
})
dispatch(assignBadge('gardenci', trackNumber))
}
}

Expand Down
15 changes: 14 additions & 1 deletion src/reducers/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,20 @@ export const defaultState = {
"petar": "http://danube.panda.org/wwf/design/i/ico/i_1.png",
"cool will": "https://avatars2.githubusercontent.com/u/1611510?v=3&s=72",
},
badges: { "1": [ "CI" ] },
slack: {
"julz": "julz",
"svett":"svett" ,
"zhou": "zyu",
"georgi": "gsabev",
"gareth": "gsmith",
"george": "glestaris",
"old will": "willpragnell",
"alberto": "aleal",
"ed": "eking",
"petar": "pppepito86",
"cool will": "wmartin",
},
badges: { "1": [ "gardenci" ] },
locked: { "julz": true },
version: 0,
}
8 changes: 8 additions & 0 deletions src/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ function photos(state = Immutable.Map(), action) {
return state
}

function slack(state = Immutable.Map(), action) {
if (action.type == "ADD_CARD") {
return state.set(action.name, action.slack)
}

return state
}
function locked(state = Immutable.Map(), action) {
return (action.type == "TOGGLE_LOCK")
? state.set(action.card, !state.get(action.card))
Expand All @@ -70,6 +77,7 @@ const reduce = combineReducers({
tracks: tracks,
trackNames: trackNames,
photos: photos,
slack: slack,
locked: locked,
version: version,
})
Expand Down
71 changes: 71 additions & 0 deletions src/slack/__tests__/slack-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
const Promise = require('es6-promise')
const fetchMock = require('fetch-mock/es5/server')

import { updateGroup } from '..'

describe("updateGroup", function() {
beforeEach(function() {
fetchMock.mock('https://slack.com/api/users.list?token=', {
ok: true,
members: [
{name: 'zyu', id: 'z'},
{name: 'gsabev', id: 'g'}
]
}).mock('https://slack.com/api/usergroups.users.update', 'POST', {
ok: true,
usergroup: { id: 'some-id' }
})
})

afterEach(function() {
fetchMock.restore()
})


describe("when the 'gardenci' slack group exists", function() {
beforeEach(function() {
fetchMock.mock('https://slack.com/api/usergroups.list?token=', {
ok: true,
usergroups: [{
handle: 'gardenci',
id: 'some-id'
}],
})
})

it("updates the 'gardenci' slack usergroup", function(done) {
updateGroup('gardenci', ['zyu', 'gsabev'], (groupId) => {
expect(fetchMock.called('https://slack.com/api/usergroups.list?token=')).toBe(true)
expect(fetchMock.called('https://slack.com/api/users.list?token=')).toBe(true)
expect(fetchMock.called('https://slack.com/api/usergroups.create')).toBe(false)
expect(fetchMock.called('https://slack.com/api/usergroups.users.update')).toBe(true)
expect(groupId).toEqual('some-id')
done()
})
})
})

describe("when the 'gardenci' slack group does not existxs", function() {
beforeEach(function() {
fetchMock.mock('https://slack.com/api/usergroups.list?token=', {
ok: true,
usergroups: []
}).mock('https://slack.com/api/usergroups.create', 'POST', {
ok: true,
usergroup: {id: 'some-id'}
})
})

it("creates & updates the 'gardenci' slack usergroup", function(done) {
updateGroup('gardenci', ['zyu', 'gsabev'], (groupId) => {
expect(fetchMock.called('https://slack.com/api/usergroups.list?token=')).toBe(true)
expect(fetchMock.called('https://slack.com/api/users.list?token=')).toBe(true)
expect(fetchMock.called('https://slack.com/api/usergroups.create')).toBe(true)
expect(fetchMock.called('https://slack.com/api/usergroups.users.update')).toBe(true)
expect(groupId).toEqual('some-id')
done()
})
})
})
})

Loading