1
0
mirror of https://github.com/danbee/chess synced 2025-03-04 08:39:06 +00:00

Fix up specs for opponent finder

This commit is contained in:
Daniel Barber 2018-10-08 10:26:05 -04:00
parent ecf87aa632
commit 41c9e28457
Signed by: danbarber
GPG Key ID: 931D8112E0103DD8
30 changed files with 2263 additions and 1543 deletions

View File

@ -40,19 +40,20 @@ exports.config = {
mode: "native",
},
babel: {
plugins: [
"transform-object-rest-spread",
"transform-class-properties",
],
presets: [
[
"env", {
"browsers": ["> 5% in US"],
"useBuiltIns": true,
"@babel/preset-env", {
useBuiltIns: "entry",
targets: {
firefox: "60",
chrome: "67",
safari: "11",
edge: "17",
phantomjs: "2.1.1",
},
},
],
"es2015",
"react",
"@babel/preset-react",
],
ignore: [/vendor/],
},

View File

@ -49,6 +49,7 @@
@import "components/game-info";
@import "components/move-list";
@import "components/game-state";
@import "components/player-finder";
// 7. Utilities utilities and helper classes with ability to override
// anything which goes before in the triangle, eg. hide helper class

View File

@ -14,15 +14,15 @@
width: 15%;
}
&.game-state--check,
&.game-state--checkmate,
&.game-state--stalemate {
.game-state--check,
.game-state--checkmate,
.game-state--stalemate {
display: block; // So PhantomJS will display it.
display: flex;
}
&.game-state--checkmate,
&.game-state--stalemate {
.game-state--checkmate,
.game-state--stalemate {
font-size: calc(var(--board-size) / 30);
height: 7.5%;
left: calc(50% - 15%);

View File

@ -0,0 +1,22 @@
.opponent-finder__result {
border-bottom: 1px dotted $black;
border-bottom-left-radius: $base-border-radius;
border-bottom-right-radius: $base-border-radius;
border-left: 1px dotted $black;
border-right: 1px dotted $black;
margin-top: -$small-spacing;
padding: $small-spacing / 3;
}
.opponent-finder__result-item {
border-radius: $base-border-radius / 2;
cursor: pointer;
display: block;
padding: $small-spacing / 3 $base-spacing / 2;
&:hover,
&:focus {
background: $black;
color: $white;
}
}

View File

@ -51,6 +51,10 @@ textarea {
border: $base-border;
}
}
&::placeholder {
opacity: 1;
}
}
textarea {

View File

@ -1,78 +1,34 @@
"use strict";
import "babel-polyfill";
import "@babel/polyfill";
import "phoenix_html";
import React from "react";
import ReactDOM from "react-dom";
import { createStore } from "redux";
import Channel from "./services/channel";
import Notifications from "./services/notifications";
import Game from "./components/game";
import OpponentFinder from "./components/opponent-finder";
import chessBoardReducer from "./reducers/chess-board";
import { setGameId } from "./store/actions";
import Listeners from "./store/listeners";
import ChessBoard from "./components/chess-board";
import MoveList from "./components/move-list";
import GameInfo from "./components/game-info";
const store = createStore(chessBoardReducer);
const notifications = new Notifications();
class App extends React.Component {
componentWillMount() {
const { gameId, store } = this.props;
const gameContainer = document.getElementById("game");
store.dispatch(setGameId(gameId));
this.listeners = new Listeners(store);
this.listeners.setListeners(notifications);
this.channel = new Channel(store, gameId);
}
componentWillUnmount() {
this.channel.leave();
}
get moves() {
const { store } = this.props;
return store.getState().moves;
}
get opponent() {
const { store } = this.props;
return store.getState().opponent;
}
render() {
const { store, gameId } = this.props;
return (
<div className="game-grid">
<ChessBoard
gameId={gameId}
store={store}
channel={this.channel}
/>
<GameInfo store={store} />
<MoveList store={store} />
</div>
);
}
}
const container = document.getElementById("app");
if (container != undefined) {
const gameId = container.getAttribute("data-game-id");
if (gameContainer != undefined) {
const gameId = gameContainer.getAttribute("data-game-id");
ReactDOM.render(
<App store={store} gameId={gameId} />,
container
<Game store={store} gameId={gameId} />,
gameContainer
);
}
const opponentFinderContainer = document.getElementById("opponent-finder");
if (opponentFinderContainer != undefined) {
ReactDOM.render(
<OpponentFinder store={store} />,
opponentFinderContainer
);
}

View File

@ -0,0 +1,65 @@
"use strict";
import "phoenix_html";
import React from "react";
import ReactDOM from "react-dom";
import Channel from "../services/channel";
import Notifications from "../services/notifications";
import { setGameId } from "../store/actions";
import Listeners from "../store/listeners";
import ChessBoard from "./chess-board";
import MoveList from "./move-list";
import GameInfo from "./game-info";
const notifications = new Notifications();
class Game extends React.Component {
componentWillMount() {
const { gameId, store } = this.props;
store.dispatch(setGameId(gameId));
this.listeners = new Listeners(store);
this.listeners.setListeners(notifications);
this.channel = new Channel(store, gameId);
}
componentWillUnmount() {
this.channel.leave();
}
get moves() {
const { store } = this.props;
return store.getState().moves;
}
get opponent() {
const { store } = this.props;
return store.getState().opponent;
}
render() {
const { store, gameId } = this.props;
return (
<div className="game-grid">
<ChessBoard
gameId={gameId}
store={store}
channel={this.channel}
/>
<GameInfo store={store} />
<MoveList store={store} />
</div>
);
}
}
export default Game;

View File

@ -0,0 +1,122 @@
"use strict";
import "phoenix_html";
import React from "react";
import ReactDOM from "react-dom";
import _ from "lodash";
import API from "../services/api";
class OpponentFinder extends React.Component {
constructor(props) {
super(props);
this.state = {
queryString: "",
foundOpponents: [],
selectedOpponent: "",
selectedOpponentId: "",
};
this.debouncedSearch = _.debounce(this.search.bind(this), 250);
}
search() {
if (this.state.queryString != "") {
API.findOpponent(this.state.queryString)
.then((response) => {
this.setState({ foundOpponents: response.data.opponents });
});
} else {
this.setState({ foundOpponents: [] });
}
}
handleChange(event) {
this.setState({ queryString: event.target.value });
this.debouncedSearch();
}
handleFocus(event) {
if (this.state.selectedOpponent) {
this.setState({ queryString: "" });
}
}
handleBlur(event) {
if (this.state.selectedOpponent) {
this.setState({ queryString: this.state.selectedOpponent.name });
}
}
selectOpponent(event) {
event.preventDefault();
const selectedOpponentId = event.target.attributes["data-id"].value;
const selectedOpponent = _.find(this.state.foundOpponents, (opponent) => {
return opponent.id == selectedOpponentId;
});
this.setState({
selectedOpponentId,
selectedOpponent,
foundOpponents: [],
queryString: selectedOpponent.name,
});
}
renderOpponents() {
return _.map(this.state.foundOpponents, (opponent) => {
return (
<li key={opponent.id}>
<a
className="opponent-finder__result-item"
data-id={opponent.id}
href="#"
onClick={this.selectOpponent.bind(this)}
>{opponent.name}</a>
</li>
);
});
}
renderOpponentsResult() {
if (this.state.foundOpponents.length) {
return (
<ul className="opponent-finder__result">
{this.renderOpponents()}
</ul>
);
}
}
render() {
const { store, gameId } = this.props;
return (
<div className="form-field opponent-finder">
<label htmlFor="query-string">Find opponent</label>
<input
id="query-string"
name="q"
value={this.state.queryString}
onChange={this.handleChange.bind(this)}
onFocus={this.handleFocus.bind(this)}
onBlur={this.handleBlur.bind(this)}
type="text"
autoComplete="off"
/>
<input
name="game[opponent_id]"
type="hidden"
value={this.state.selectedOpponentId}
/>
{this.renderOpponentsResult()}
</div>
);
}
}
export default OpponentFinder;

View File

@ -1,12 +1,8 @@
import axios from "axios";
const API = {
getGame: (gameId) => {
return axios.get(`/api/games/${gameId}`);
},
updateGame: (gameId, move) => {
return axios.patch(`/api/games/${gameId}`, { move });
findOpponent: (queryString) => {
return axios.get("/api/opponents", { params: { q: queryString } });
},
};

View File

@ -51,6 +51,8 @@ let socket = new Socket("/socket", { params: { token: window.userToken } });
// Finally, pass the token on connect as below. Or remove it
// from connect if you don't care about authentication.
socket.connect();
if (window.userToken) {
socket.connect();
}
export default socket;

View File

@ -7,6 +7,7 @@
"watch": "brunch watch --stdin"
},
"dependencies": {
"@babel/polyfill": "^7.0.0",
"axios": "^0.18.0",
"classnames": "^2.2.5",
"gettext.js": "^0.5.3",
@ -21,11 +22,9 @@
"redux-watch": "^1.1.1"
},
"devDependencies": {
"babel-brunch": "^6.1.1",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-object-rest-spread": "^6.23.0",
"babel-preset-env": "^1.4.0",
"babel-preset-react": "^6.24.1",
"@babel/preset-env": "^7.1.0",
"@babel/preset-react": "^7.0.0",
"babel-brunch": "^7.0.0",
"brunch": "~2.10.0",
"clean-css-brunch": "~2.10.0",
"core-js": "~2.5.3",

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,7 @@ use Mix.Config
# We don't run a server during test. If one is required,
# you can enable the server option below.
config :chess, ChessWeb.Endpoint,
check_origin: false,
http: [port: 4001],
server: true

View File

@ -52,6 +52,11 @@ defmodule Chess.Store.User do
|> unique_constraint(:email)
end
def find_by_name(name) do
from user in __MODULE__,
where: user.name == ^name
end
def opponents(user) do
from user in __MODULE__,
where: user.id != ^user.id

View File

@ -0,0 +1,17 @@
defmodule ChessWeb.Api.OpponentsController do
use ChessWeb, :controller
import Chess.Auth, only: [current_user: 1]
alias Chess.Repo
alias Chess.Repo.Queries
def index(conn, %{"q" => query_string}) do
opponents =
conn
|> current_user()
|> Queries.opponents(query_string)
|> Repo.all
render conn, "index.json", %{opponents: opponents}
end
end

View File

@ -26,14 +26,8 @@ defmodule ChessWeb.GameController do
def new(conn, _params) do
changeset = Game.changeset(%Game{})
opponents =
conn
|> current_user()
|> User.opponents()
|> Repo.all
conn
|> render("new.html", changeset: changeset, opponents: opponents)
|> render("new.html", changeset: changeset)
end
def create(conn, %{"game" => game}) do

View File

@ -50,7 +50,7 @@ defmodule ChessWeb.Router do
scope "/api", as: :api do
pipe_through [:api, :auth, :ensure_auth]
resources "/games", ChessWeb.Api.GameController, only: [:show, :update]
resources "/opponents", ChessWeb.Api.OpponentsController, only: [:index]
end
if Mix.env == :dev do

View File

@ -4,11 +4,7 @@
<%= form_for @changeset, game_path(@conn, :create), [class: "create-game"],
fn form -> %>
<div class="form-group">
<%= label form, :opponent_id %>
<%= select form,
:opponent_id,
@opponents,
prompt: gettext("Choose an opponent") %>
<div id="opponent-finder"></div>
<%= error_tag form, :opponent_id %>
</div>

View File

@ -1,2 +1,2 @@
<div id="app" data-game-id="<%= @game.id %>">
<div id="game" data-game-id="<%= @game.id %>">
</div>

View File

@ -1,7 +1,7 @@
<div class="form">
<h2><%= gettext "Register" %></h2>
<%= form_for @changeset, registration_path(@conn, :create), [class: "create-registration"], fn f -> %>
<%= form_for @changeset, registration_path(@conn, :create), [class: "create-registration", novalidate: "novalidate"], fn f -> %>
<%= if @changeset.action do %>
<div class="alert alert-danger">
<p>

View File

@ -0,0 +1,18 @@
defmodule ChessWeb.Api.OpponentsView do
use ChessWeb, :view
def render("index.json", %{opponents: opponents}) do
%{
opponents: Enum.map(opponents, fn opponent ->
opponent_attrs(opponent)
end)
}
end
def opponent_attrs(opponent) do
%{
id: opponent.id,
name: opponent.name,
}
end
end

View File

@ -34,7 +34,7 @@ defmodule Chess.Mixfile do
# Type `mix help deps` for examples and options.
defp deps do
[
{:argon2_elixir, "~> 1.2"},
{:argon2_elixir, "~> 1.3"},
{:bamboo, "~> 1.0"},
{:comeonin, "~> 4.0"},
{:cowboy, "~> 1.0"},
@ -49,6 +49,7 @@ defmodule Chess.Mixfile do
{:phoenix_live_reload, "~> 1.0", only: :dev},
{:phoenix_pubsub, "~> 1.0"},
{:postgrex, ">= 0.0.0"},
{:secure_random, "~> 0.5"},
{:timex_ecto, "~> 3.2"},
{:wallaby, "~> 0.20.0", [runtime: false, only: :test]},
]

View File

@ -4,7 +4,7 @@
"bamboo": {:hex, :bamboo, "1.1.0", "ecbdc851d0127d369957e5ca7bcfb4c7fe7dbfaf4fa0b2e5cc2493dd8a9a600e", [:mix], [{:hackney, ">= 1.13.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, ">= 1.5.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
"base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], [], "hexpm"},
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"},
"certifi": {:hex, :certifi, "2.3.1", "d0f424232390bf47d82da8478022301c561cf6445b5b5fb6a84d49a9e76d2639", [:rebar3], [{:parse_trans, "3.2.0", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"},
"certifi": {:hex, :certifi, "2.4.2", "75424ff0f3baaccfd34b1214184b6ef616d89e420b258bb0a5ea7d7bc628f7f0", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"},
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm"},
"comeonin": {:hex, :comeonin, "4.1.1", "c7304fc29b45b897b34142a91122bc72757bc0c295e9e824999d5179ffc08416", [:mix], [{:argon2_elixir, "~> 1.2", [hex: :argon2_elixir, repo: "hexpm", optional: true]}, {:bcrypt_elixir, "~> 0.12.1 or ~> 1.0", [hex: :bcrypt_elixir, repo: "hexpm", optional: true]}, {:pbkdf2_elixir, "~> 0.12", [hex: :pbkdf2_elixir, repo: "hexpm", optional: true]}], "hexpm"},
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm"},
@ -14,35 +14,36 @@
"db_connection": {:hex, :db_connection, "1.1.3", "89b30ca1ef0a3b469b1c779579590688561d586694a3ce8792985d4d7e575a61", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
"decimal": {:hex, :decimal, "1.5.0", "b0433a36d0e2430e3d50291b1c65f53c37d56f83665b43d79963684865beab68", [:mix], [], "hexpm"},
"distillery": {:hex, :distillery, "2.0.10", "e9f1f1d3f4a89996a3e1a555872feed8a3a73e3d10b51886941382d29ca58f99", [:mix], [{:artificery, "~> 0.2", [hex: :artificery, repo: "hexpm", optional: false]}], "hexpm"},
"ecto": {:hex, :ecto, "2.2.10", "e7366dc82f48f8dd78fcbf3ab50985ceeb11cb3dc93435147c6e13f2cda0992e", [:mix], [{:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
"ecto": {:hex, :ecto, "2.2.11", "4bb8f11718b72ba97a2696f65d247a379e739a0ecabf6a13ad1face79844791c", [:mix], [{:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
"elixir_make": {:hex, :elixir_make, "0.4.2", "332c649d08c18bc1ecc73b1befc68c647136de4f340b548844efc796405743bf", [:mix], [], "hexpm"},
"file_system": {:hex, :file_system, "0.2.6", "fd4dc3af89b9ab1dc8ccbcc214a0e60c41f34be251d9307920748a14bf41f1d3", [:mix], [], "hexpm"},
"formulator": {:hex, :formulator, "0.1.7", "feb3e4131c729535b3ad91ead282866870c1e76f343d3bb7c6937f99a5243961", [:mix], [{:gettext, ">= 0.11.0", [hex: :gettext, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.4", [hex: :phoenix_html, repo: "hexpm", optional: false]}], "hexpm"},
"gettext": {:hex, :gettext, "0.16.0", "4a7e90408cef5f1bf57c5a39e2db8c372a906031cc9b1466e963101cb927dafc", [:mix], [], "hexpm"},
"guardian": {:hex, :guardian, "1.1.1", "be14c4007eaf05268251ae114030cb7237ed9a9631c260022f020164ff4ed733", [:mix], [{:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.0 or ~> 1.2 or ~> 1.3", [hex: :phoenix, repo: "hexpm", optional: true]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
"hackney": {:hex, :hackney, "1.13.0", "24edc8cd2b28e1c652593833862435c80661834f6c9344e84b6a2255e7aeef03", [:rebar3], [{:certifi, "2.3.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "5.1.2", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
"hackney": {:hex, :hackney, "1.14.3", "b5f6f5dcc4f1fba340762738759209e21914516df6be440d85772542d4a5e412", [:rebar3], [{:certifi, "2.4.2", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
"httpoison": {:hex, :httpoison, "0.13.0", "bfaf44d9f133a6599886720f3937a7699466d23bb0cd7a88b6ba011f53c6f562", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
"idna": {:hex, :idna, "5.1.2", "e21cb58a09f0228a9e0b95eaa1217f1bcfc31a1aaa6e1fdf2f53a33f7dbd9494", [:rebar3], [{:unicode_util_compat, "0.3.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
"jason": {:hex, :jason, "1.1.1", "d3ccb840dfb06f2f90a6d335b536dd074db748b3e7f5b11ab61d239506585eb2", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
"jose": {:hex, :jose, "1.8.4", "7946d1e5c03a76ac9ef42a6e6a20001d35987afd68c2107bcd8f01a84e75aa73", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"},
"mime": {:hex, :mime, "1.3.0", "5e8d45a39e95c650900d03f897fbf99ae04f60ab1daa4a34c7a20a5151b7a5fe", [:mix], [], "hexpm"},
"mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], [], "hexpm"},
"parse_trans": {:hex, :parse_trans, "3.2.0", "2adfa4daf80c14dc36f522cf190eb5c4ee3e28008fc6394397c16f62a26258c2", [:rebar3], [], "hexpm"},
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"},
"phoenix": {:hex, :phoenix, "1.3.4", "aaa1b55e5523083a877bcbe9886d9ee180bf2c8754905323493c2ac325903dc5", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
"phoenix_ecto": {:hex, :phoenix_ecto, "3.4.0", "91cd39427006fe4b5588d69f0941b9c3d3d8f5e6477c563a08379de7de2b0c58", [:mix], [{:ecto, "~> 2.1", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"phoenix_html": {:hex, :phoenix_html, "2.12.0", "1fb3c2e48b4b66d75564d8d63df6d53655469216d6b553e7e14ced2b46f97622", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.1.5", "8d4c9b1ef9ca82deee6deb5a038d6d8d7b34b9bb909d99784a49332e0d15b3dc", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.0 or ~> 1.2 or ~> 1.3", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm"},
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.1.6", "7280f4dd88d38ee07ca70a2974ca12b9bfdbb9fa8137e4692889cf097c1bb232", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.0 or ~> 1.2 or ~> 1.3", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.0", "d55e25ff1ff8ea2f9964638366dfd6e361c52dedfd50019353598d11d4441d14", [:mix], [], "hexpm"},
"plug": {:hex, :plug, "1.6.2", "e06a7bd2bb6de5145da0dd950070110dce88045351224bd98e84edfdaaf5ffee", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"},
"plug": {:hex, :plug, "1.6.4", "35618dd2cc009b69b000f785452f6b370f76d099ece199733fea27bc473f809d", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"},
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
"poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [:rebar], [], "hexpm"},
"postgrex": {:hex, :postgrex, "0.13.5", "3d931aba29363e1443da167a4b12f06dcd171103c424de15e5f3fc2ba3e6d9c5", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm"},
"ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], [], "hexpm"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:make, :rebar], [], "hexpm"},
"timex": {:hex, :timex, "3.3.0", "e0695aa0ddb37d460d93a2db34d332c2c95a40c27edf22fbfea22eb8910a9c8d", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"},
"secure_random": {:hex, :secure_random, "0.5.1", "c5532b37c89d175c328f5196a0c2a5680b15ebce3e654da37129a9fe40ebf51b", [:mix], [], "hexpm"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"},
"timex": {:hex, :timex, "3.4.1", "e63fc1a37453035e534c3febfe9b6b9e18583ec7b37fd9c390efdef97397d70b", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"},
"timex_ecto": {:hex, :timex_ecto, "3.3.0", "d5bdef09928e7a60f10a0baa47ce653f29b43d6fee87b30b236b216d0e36b98d", [:mix], [{:ecto, "~> 2.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:timex, "~> 3.1", [hex: :timex, repo: "hexpm", optional: false]}], "hexpm"},
"tzdata": {:hex, :tzdata, "0.5.19", "7962a3997bf06303b7d1772988ede22260f3dae1bf897408ebdac2b4435f4e6a", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.3.1", "a1f612a7b512638634a603c8f401892afbf99b8ce93a45041f8aaca99cadb85e", [:rebar3], [], "hexpm"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"},
"wallaby": {:hex, :wallaby, "0.20.0", "cc6663555ff7b05afbebb2a8b461d18a5b321658b9017f7bc77d494b7063266a", [:mix], [{:httpoison, "~> 0.12", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, ">= 1.4.0", [hex: :poison, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}], "hexpm"},
}

View File

@ -11,8 +11,8 @@ defmodule Chess.Store.GameTest do
import Chess.Factory
test "game is valid with a board and user" do
user = insert(:user, %{email: "link@hyrule.com"})
opponent = insert(:user, %{email: "zelda@hyrule.com"})
user = insert(:user)
opponent = insert(:opponent)
attrs = %{
board: %{},
@ -40,8 +40,8 @@ defmodule Chess.Store.GameTest do
end
test "game is invalid without a board" do
user = insert(:user, %{email: "link@hyrule.com"})
opponent = insert(:user, %{email: "zelda@hyrule.com"})
user = insert(:user)
opponent = insert(:opponent)
attrs = %{board: nil, user_id: user.id, opponent_id: opponent.id}
changeset = Game.changeset(%Game{}, attrs)
@ -51,7 +51,7 @@ defmodule Chess.Store.GameTest do
end
test "game is invalid without a user" do
opponent = insert(:user, %{email: "zelda@hyrule.com"})
opponent = insert(:user)
attrs = %{board: %{}, opponent_id: opponent.id}
changeset = Game.changeset(%Game{}, attrs)
@ -61,7 +61,7 @@ defmodule Chess.Store.GameTest do
end
test "game is invalid without an opponent" do
user = insert(:user, %{email: "link@hyrule.com"})
user = insert(:user)
attrs = %{board: %{}, user_id: user.id}
changeset = Game.changeset(%Game{}, attrs)
@ -78,8 +78,8 @@ defmodule Chess.Store.GameTest do
end
test "moving a piece changes the turn" do
user = insert(:user, %{email: "link@hyrule.com"})
opponent = insert(:user, %{email: "zelda@hyrule.com"})
user = insert(:user)
opponent = insert(:opponent)
game = insert(:game, %{
board: Board.default,

View File

@ -11,8 +11,8 @@ defmodule Chess.Store.MoveTest do
describe "move" do
test "move is valid with a game, a from, and a to" do
user = insert(:user, %{email: "link@hyrule.com"})
opponent = insert(:user, %{email: "zelda@hyrule.com"})
user = insert(:user)
opponent = insert(:opponent)
game = insert(:game, %{
board: Board.default,
@ -42,8 +42,8 @@ defmodule Chess.Store.MoveTest do
end
test "move is invalid without a from or to" do
user = insert(:user, %{email: "link@hyrule.com"})
opponent = insert(:user, %{email: "zelda@hyrule.com"})
user = insert(:user)
opponent = insert(:opponent)
game = insert(:game, %{
board: Board.default,
@ -60,8 +60,8 @@ defmodule Chess.Store.MoveTest do
end
test "move is invalid without a piece" do
user = insert(:user, %{email: "link@hyrule.com"})
opponent = insert(:user, %{email: "zelda@hyrule.com"})
user = insert(:user)
opponent = insert(:opponent)
game = insert(:game, %{
board: Board.default,

View File

@ -19,7 +19,7 @@ defmodule ChessWeb.GameControllerTest do
end
test "creates game and redirects when data is valid", %{conn: conn} do
opponent = insert(:user, %{email: "daruk@goron.city"})
opponent = insert(:user, %{email: "daruk@goron.city", name: "Daruk"})
attrs = %{"opponent_id" => opponent.id}
user = insert(:user)
@ -35,7 +35,7 @@ defmodule ChessWeb.GameControllerTest do
end
test "sends an email when game is created", %{conn: conn} do
opponent = insert(:user, %{name: "Daruk", email: "daruk@goron.city"})
opponent = insert(:user, %{email: "daruk@goron.city", name: "Daruk"})
attrs = %{"opponent_id" => opponent.id}
user = insert(:user)
@ -52,7 +52,7 @@ defmodule ChessWeb.GameControllerTest do
test "shows chosen game", %{conn: conn} do
user = insert(:user)
opponent = insert(:user, %{email: "revali@rito.village"})
opponent = insert(:user, %{email: "revali@rito.village", name: "Revali"})
game = insert(:game, %{user_id: user.id, opponent_id: opponent.id})
conn =
@ -60,15 +60,15 @@ defmodule ChessWeb.GameControllerTest do
|> login(user)
|> get(game_path(conn, :show, game))
assert html_response(conn, 200) =~ "<div id=\"app\" data-game-id=\"#{game.id}\">"
assert html_response(conn, 200) =~ "<div id=\"game\" data-game-id=\"#{game.id}\">"
end
test "does not show a game if the user is not a player", %{conn: conn} do
user = insert(:user)
opponent = insert(:user, %{email: "revali@rito.village"})
opponent = insert(:user, %{email: "revali@rito.village", name: "Revali"})
game = insert(:game, %{user_id: user.id, opponent_id: opponent.id})
other_user = insert(:user, %{email: "mipha@zora.domain"})
other_user = insert(:user, %{email: "mipha@zora.domain", name: "Mipha"})
conn =
conn

View File

@ -24,7 +24,7 @@ defmodule Chess.Features.GamesTest do
|> create_user_and_login()
|> visit("/games")
|> click(link("New game"))
|> select("game[opponent_id]", option: "Zelda")
|> select_opponent("Zelda")
|> click(button("Create game"))
session
@ -62,7 +62,7 @@ defmodule Chess.Features.GamesTest do
|> create_user_and_login()
|> visit("/games")
|> click(link("New game"))
|> select("game[opponent_id]", option: "Urbosa")
|> select_opponent("Urbosa")
|> click(button("Create game"))
|> click(link("64squares"))

View File

@ -20,7 +20,7 @@ defmodule Chess.Features.MovesTest do
|> create_user_and_login()
|> visit("/games")
|> click(link("New game"))
|> select("game[opponent_id]", option: "Zelda")
|> select_opponent("Zelda")
|> click(button("Create game"))
|> click(css("#f4-r1"))
@ -48,7 +48,7 @@ defmodule Chess.Features.MovesTest do
|> login("link@hyrule.com", "ilovezelda")
|> visit("/games")
|> click(link("New game"))
|> select("game[opponent_id]", option: "Zelda")
|> select_opponent("Zelda")
|> click(button("Create game"))
assert_email_delivered_with(to: [{opponent.name, opponent.email}])
@ -74,7 +74,7 @@ defmodule Chess.Features.MovesTest do
|> create_user_and_login()
|> visit("/games")
|> click(link("New game"))
|> select("game[opponent_id]", option: "Zelda")
|> select_opponent("Zelda")
|> click(button("Create game"))
session
@ -98,7 +98,7 @@ defmodule Chess.Features.MovesTest do
|> login("link@hyrule.com", "ilovezelda")
|> visit("/games")
|> click(link("New game"))
|> select("game[opponent_id]", option: "Zelda")
|> select_opponent("Zelda")
|> click(button("Create game"))
{:ok, session2} = Wallaby.start_session
@ -127,7 +127,7 @@ defmodule Chess.Features.MovesTest do
|> login("link@hyrule.com", "ilovezelda")
|> visit("/games")
|> click(link("New game"))
|> select("game[opponent_id]", option: "Zelda")
|> select_opponent("Zelda")
|> click(button("Create game"))
{:ok, session2} = Wallaby.start_session

View File

@ -12,4 +12,10 @@ defmodule Chess.FormHelpers do
session
end
def select_opponent(session, name) do
session
|> fill_in(text_field("Find opponent"), with: name)
|> click(link(name))
end
end

View File

@ -7,22 +7,30 @@ defmodule Chess.Factory do
def insert(_resource, _params \\ %{})
def insert(:user, params) do
%User{
name: "Zelda",
email: "zelda@hyrule.com",
password: "ganonsucks"
}
def insert(:user, new_params) do
params =
%{
name: "Link",
email: "link@hyrule.com",
password: "ilovezelda"
}
|> Map.merge(new_params)
%User{}
|> User.changeset(params)
|> Repo.insert!
end
def insert(:opponent, params) do
%User{
name: "Link",
email: "link@hyrule.com",
password: "ilovezelda"
}
def insert(:opponent, new_params) do
params =
%{
name: "Zelda",
email: "zelda@hyrule.com",
password: "ganonsucks"
}
|> Map.merge(new_params)
%User{}
|> User.changeset(params)
|> Repo.insert!
end