<|start_filename|>JS/Counter.js<|end_filename|>
import React from 'react';
import { connect } from 'react-redux';

const Counter = React.createClass({
	render() {
		const { value, onIncrement, onDecrement } = this.props;
		return (
			<div>
				<p>
					Value: { value }
				</p>
				<p>
					<button onClick={onIncrement}>+</button>
					<button onClick={onDecrement}>-</button>
				</p>
			</div>
		);
	}
});

export default connect(
	state => { return { value: state.value }; },
	dispatch => { 
		return {
			onIncrement: () => dispatch({ type: 'INCREMENT' }),
			onDecrement: () => dispatch({ type: 'DECREMENT' })
		}; 
	}
)(Counter);


<|start_filename|>Makefile<|end_filename|>
WEBPACK=./node_modules/.bin/webpack

.PHONY: all
all: build

.PHONY: build
build:
ifeq ($(OPTIMIZE), 1)
	NODE_ENV=production $(WEBPACK) --bail
	swift build -v -c release
else
	$(WEBPACK) --bail
	swift build -v
endif

.PHONY: run
run:
	./.build/debug/App serve

.PHONY: watch
watch:
	$(WEBPACK) --progress --colors --watch

.PHONY: run-dev-server
run-dev-server:
	webpack-dev-server --inline --progress --colors -d --host 0.0.0.0 --port 8081

.PHONY: deps
deps:
	yarn

.PHONY: docker
docker:
	docker build -t react-swift-example .

.PHONY: docker-run
docker-run:
	docker run -it --rm=true -p 8080:8080 react-swift-example $(DOCKER_COMMAND)

.PHONY: check-integration
check-integration:
	./node_modules/.bin/mocha --timeout 60000 --compilers js:babel-core/register Tests/Integration/**/*.js

.PHONY: run-webdriver
run-webdriver:
	./node_modules/.bin/phantomjs --webdriver=4444


<|start_filename|>Dockerfile<|end_filename|>
FROM ubuntu:14.04

RUN apt-get update && apt-get -y install curl git libedit-dev libcurl4-openssl-dev clang libicu52 make
RUN cd /usr/local && \
	curl -O https://swift.org/builds/swift-3.0-release/ubuntu1404/swift-3.0-RELEASE/swift-3.0-RELEASE-ubuntu14.04.tar.gz && \
	tar xzf swift-3.0-RELEASE-ubuntu14.04.tar.gz --strip-components=2
RUN (curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.32.0/install.sh | bash) && \
	export NVM_DIR="/root/.nvm" && \
	[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" && \
	nvm install 4

ADD package.json /opt/src/
RUN export NVM_DIR="/root/.nvm" && \
	[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" && \
	cd /opt/src && npm install

ADD Package.swift /opt/src/
RUN cd /opt/src && swift package fetch

ADD . /opt/src
RUN export NVM_DIR="/root/.nvm" && \
	[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" && \
	cd /opt/src && make

WORKDIR "/opt/src"
EXPOSE 8080
CMD ["./.build/debug/App", "serve"]


<|start_filename|>Tests/Integration/test.js<|end_filename|>
const webdriverio = require('webdriverio');
import { expect } from 'chai';

const options = { 
	desiredCapabilities: { 
		browserName: 'phantomjs'
	}, 
  // logLevel: 'verbose',
	baseUrl: 'http://localhost:8080',
	port: 4444 
}; 

describe("Integration", () => {
	let client;

	beforeEach(() => {
		client = webdriverio 
			.remote(options) 
			.init();
	});

	it("should work with bare server", () => {
		return client
			.url('http://localhost:8080/') 
			.getTitle().then(title => { 
				expect(title).to.equal("React+Swift Test App");
			}) 
			.getText("#root p:first-of-type").then(text => {
				expect(text).to.equal("Value: 42");
			})
			.click("button")
			.click("button")
			.getText("#root p:first-of-type").then(text => {
				expect(text).to.equal("Value: 44");
			})
			.click("button + button")
			.getText("#root p:first-of-type").then(text => {
				expect(text).to.equal("Value: 43");
			});
	});

	it("should work with dev server", () => {
		return client
			.url('http://localhost:8081/') 
			.getTitle().then(title => { 
				expect(title).to.equal("React+Swift Test App");
			}) 
			.waitForExist("#root p:first-of-type", 5000)
			.getText("#root p:first-of-type").then(text => {
				expect(text).to.equal("Value: 42");
			})
			.click("button")
			.click("button")
			.getText("#root p:first-of-type").then(text => {
				expect(text).to.equal("Value: 44");
			})
			.click("button + button")
			.getText("#root p:first-of-type").then(text => {
				expect(text).to.equal("Value: 43");
			});
	});
});


<|start_filename|>JS/client.js<|end_filename|>
import React from 'react';
import { render } from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import 'whatwg-fetch';
import 'es6-promise';

import Counter from './Counter';
import { rootReducer } from './reducers';

function load(state) {
	const store = createStore(rootReducer, state);

	render(
		<Provider store={store}>
			<Counter/>
		</Provider>,
		document.getElementById('root')
	);
}

// Check if we can preload the state from a server-rendered page
if (window.__PRELOADED_STATE__) {
	load(window.__PRELOADED_STATE__);
}
else {
	// We didn't prerender on the server, so we need to get our state
	fetch('/api/state')
		.then(
			response => response.json().then(load),
			err => console.error(err)
		);
}



<|start_filename|>JS/server.js<|end_filename|>
import React from 'react';
import { renderToString } from 'react-dom/server';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import Counter from './Counter';
import { rootReducer } from './reducers';

export function render(preloadedState = undefined) {
	const store = createStore(rootReducer, preloadedState);
	return {
		html: renderToString(
			<Provider store={store}>
				<Counter/>
			</Provider>
		),
		state: JSON.stringify(store.getState())
	};
}
