mirror of
https://github.com/danbee/chess
synced 2025-03-04 08:39:06 +00:00
Compare commits
45 Commits
080574070b
...
205d5a5a24
| Author | SHA1 | Date | |
|---|---|---|---|
| 205d5a5a24 | |||
| 87504064e7 | |||
| 3543ccdcae | |||
| 1ea842d0ab | |||
| f5c35de8d8 | |||
| bbc1838d7e | |||
| 04e00e530b | |||
| 7e2c841b59 | |||
| b01403cbe4 | |||
| dea6198e30 | |||
| d2074ade11 | |||
| 2f561c28fe | |||
| 34551788c7 | |||
| a26f044822 | |||
| a750756187 | |||
| 662efda6b1 | |||
| 6f7b087b82 | |||
| b1451535ca | |||
| e4fc3a1b6f | |||
| 6cecc3005c | |||
| c19f586d13 | |||
| f5641150a0 | |||
| f0ed129028 | |||
| 1a9358d5e0 | |||
| 8f683dd73f | |||
| 69f05a1c5d | |||
| 01471967c8 | |||
| 5cd457bc62 | |||
| 27f5fc6730 | |||
| d6ca73e4bd | |||
| 9909abd17f | |||
| 6eb2a53695 | |||
| 9b0c75c2ce | |||
| 7bf2312106 | |||
| 1de19ae6c9 | |||
| fc83a1e16d | |||
| 909d04f2a1 | |||
| 6c51aa113f | |||
| a893dd3e85 | |||
| c3e1206b58 | |||
| 14952a5d6c | |||
| a83ee57e7b | |||
| cfa472e13a | |||
| 1d7fb6c87b | |||
| d56502a001 |
@ -1,4 +1,3 @@
|
||||
https://github.com/gigalixir/gigalixir-buildpack-clean-cache.git
|
||||
https://github.com/HashNuke/heroku-buildpack-elixir
|
||||
https://github.com/gjaldon/heroku-buildpack-phoenix-static
|
||||
https://github.com/gigalixir/gigalixir-buildpack-distillery.git
|
||||
https://github.com/gigalixir/gigalixir-buildpack-mix.git
|
||||
|
||||
3
.formatter.exs
Normal file
3
.formatter.exs
Normal file
@ -0,0 +1,3 @@
|
||||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||
]
|
||||
@ -1,4 +1,4 @@
|
||||
elixir 1.11.3
|
||||
python 2.7.14
|
||||
nodejs 10.16.0
|
||||
erlang 23.2.3
|
||||
elixir 1.14.1
|
||||
python 3.9.1
|
||||
nodejs 16.14.0
|
||||
erlang 24.2.1
|
||||
|
||||
42
Dockerfile
Normal file
42
Dockerfile
Normal file
@ -0,0 +1,42 @@
|
||||
FROM --platform=linux/amd64 elixir:1.14.1
|
||||
|
||||
# Create a directory for your application code and set it as the WORKDIR. All following commands will be run in this directory.
|
||||
RUN mkdir /app
|
||||
WORKDIR /app
|
||||
|
||||
# Update package repo
|
||||
RUN apt-get update
|
||||
|
||||
# Install Chrome
|
||||
RUN wget -q https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb && \
|
||||
apt-get -y install ./google-chrome-stable_current_amd64.deb
|
||||
|
||||
# Install ChromeDriver
|
||||
RUN CHROMEDRIVER_VERSION=`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE` && \
|
||||
mkdir -p /opt/chromedriver-$CHROMEDRIVER_VERSION && \
|
||||
curl -sS -o /tmp/chromedriver_linux64.zip http://chromedriver.storage.googleapis.com/$CHROMEDRIVER_VERSION/chromedriver_linux64.zip && \
|
||||
unzip -qq /tmp/chromedriver_linux64.zip -d /opt/chromedriver-$CHROMEDRIVER_VERSION && \
|
||||
rm /tmp/chromedriver_linux64.zip && \
|
||||
chmod +x /opt/chromedriver-$CHROMEDRIVER_VERSION/chromedriver && \
|
||||
ln -fs /opt/chromedriver-$CHROMEDRIVER_VERSION/chromedriver /usr/local/bin/chromedriver
|
||||
|
||||
# Install NodeJS
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_16.x | bash - && \
|
||||
apt-get install nodejs
|
||||
|
||||
# Install Yarn
|
||||
RUN npm install --global yarn
|
||||
|
||||
# Install Rebar and Hex
|
||||
RUN mix local.rebar --force && mix local.hex --force
|
||||
|
||||
# Install the Phoenix Mix archive
|
||||
RUN mix archive.install --sha512 2a2f5167f5ea30f314500da449dbd8cfb4b6986cc27197c82fa4cc328798814f89a4dbe0183a5f213faed3587e8133ce99c1fab74cf1597978a270bdcc7bf789 --force https://github.com/phoenixframework/archives/raw/master/phx_new.ez
|
||||
|
||||
# COPY mix.exs and mix.lock and install dependencies before adding the full code so the cache only
|
||||
# gets invalidated when dependencies are changed
|
||||
COPY mix.exs mix.lock ./
|
||||
RUN mix deps.get
|
||||
|
||||
# Copy the app source code into the image
|
||||
COPY ./ /app
|
||||
@ -8,13 +8,16 @@
|
||||
width: 1.5%;
|
||||
}
|
||||
|
||||
.board__container {
|
||||
grid-area: board;
|
||||
}
|
||||
|
||||
.board {
|
||||
background: $background-color;
|
||||
border-collapse: unset;
|
||||
border-radius: 2.8%;
|
||||
border-spacing: 1px;
|
||||
color: $foreground-color;
|
||||
grid-area: board;
|
||||
height: var(--board-size);
|
||||
padding: calc(var(--board-size) / 20);
|
||||
position: relative;
|
||||
@ -58,6 +61,14 @@
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.board__body {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
.board__row {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
.board--player-is-black {
|
||||
@ -70,20 +81,45 @@
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.board__body {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.board__row {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
}
|
||||
|
||||
.board__body {
|
||||
background: $background-color;
|
||||
border: 0.25rem solid $foreground-color;
|
||||
border-radius: calc(var(--board-size) / 100);
|
||||
display: grid;
|
||||
grid-template-columns: repeat(8, 1fr);
|
||||
grid-template-rows: repeat(8, 1fr);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
padding: 1%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.board__row {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
|
||||
@include odd-between(1, 8) {
|
||||
.square {
|
||||
@include odd-between(1, 8) { @extend %square--black; }
|
||||
@include even-between(1, 8) { @extend %square--white; }
|
||||
}
|
||||
}
|
||||
@include even-between(1, 8) {
|
||||
.square {
|
||||
@include odd-between(1, 8) { @extend %square--white; }
|
||||
@include even-between(1, 8) { @extend %square--black; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.board__rank-labels {
|
||||
display: none;
|
||||
height: 90%;
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
@use "sass:math";
|
||||
|
||||
.opponent-finder__result {
|
||||
border-bottom: 1px dotted $black;
|
||||
border-bottom-left-radius: $base-border-radius;
|
||||
@ -5,14 +7,14 @@
|
||||
border-left: 1px dotted $black;
|
||||
border-right: 1px dotted $black;
|
||||
margin-top: -$small-spacing;
|
||||
padding: $small-spacing / 3;
|
||||
padding: math.div($small-spacing, 3);
|
||||
}
|
||||
|
||||
.opponent-finder__result-item {
|
||||
border-radius: $base-border-radius / 2;
|
||||
border-radius: $base-border-radius * 0.5;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
padding: $small-spacing / 3 $base-spacing / 2;
|
||||
padding: math.div($small-spacing, 3) $base-spacing * 0.5;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover,
|
||||
|
||||
@ -57,36 +57,13 @@
|
||||
border-radius: 4%;
|
||||
margin: 0.5px;
|
||||
position: relative;
|
||||
flex-grow: 1;
|
||||
|
||||
// This is to ensure the squares can be clicked on in PhantomJS
|
||||
// TODO: Figure out why we need this
|
||||
min-height: 20px;
|
||||
min-width: 20px;
|
||||
|
||||
@include odd-between(1, 8) { @extend %square--white; }
|
||||
@include even-between(1, 8) { @extend %square--black; }
|
||||
|
||||
@include odd-between(9, 16) { @extend %square--black; }
|
||||
@include even-between(9, 16) { @extend %square--white; }
|
||||
|
||||
@include odd-between(17, 24) { @extend %square--white; }
|
||||
@include even-between(17, 24) { @extend %square--black; }
|
||||
|
||||
@include odd-between(25, 32) { @extend %square--black; }
|
||||
@include even-between(25, 32) { @extend %square--white; }
|
||||
|
||||
@include odd-between(33, 40) { @extend %square--white; }
|
||||
@include even-between(33, 40) { @extend %square--black; }
|
||||
|
||||
@include odd-between(41, 48) { @extend %square--black; }
|
||||
@include even-between(41, 48) { @extend %square--white; }
|
||||
|
||||
@include odd-between(49, 56) { @extend %square--white; }
|
||||
@include even-between(49, 56) { @extend %square--black; }
|
||||
|
||||
@include odd-between(57, 64) { @extend %square--black; }
|
||||
@include even-between(57, 64) { @extend %square--white; }
|
||||
|
||||
&::before {
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
@use "sass:math";
|
||||
|
||||
$_form-background-color: $background-color;
|
||||
|
||||
fieldset {
|
||||
@ -9,14 +11,14 @@ fieldset {
|
||||
|
||||
legend {
|
||||
font-weight: $heavy-font-weight;
|
||||
margin-bottom: $small-spacing / 2;
|
||||
margin-bottom: $small-spacing * 0.5;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
font-weight: $heavy-font-weight;
|
||||
margin-bottom: $small-spacing / 2;
|
||||
margin-bottom: $small-spacing * 0.5;
|
||||
}
|
||||
|
||||
input,
|
||||
@ -35,7 +37,7 @@ textarea {
|
||||
box-sizing: border-box;
|
||||
color: $foreground-color;
|
||||
margin-bottom: $small-spacing;
|
||||
padding: $base-spacing / 3;
|
||||
padding: math.div($base-spacing, 3);
|
||||
transition: border-color $base-duration $base-timing;
|
||||
width: 100%;
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
$base-border-radius: 0.5rem;
|
||||
$base-spacing: 2rem;
|
||||
$small-spacing: $base-spacing / 2;
|
||||
$x-small-spacing: $small-spacing / 2;
|
||||
$small-spacing: $base-spacing * 0.5;
|
||||
$x-small-spacing: $small-spacing * 0.5;
|
||||
$base-z-index: 0;
|
||||
|
||||
$container-width: 90%;
|
||||
|
||||
2
assets/css/vendor/_family.scss
vendored
2
assets/css/vendor/_family.scss
vendored
@ -130,7 +130,7 @@
|
||||
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
|
||||
/// @param {number} $num - id of the child
|
||||
@mixin middle($num) {
|
||||
&:nth-child(#{round($num / 2)}) {
|
||||
&:nth-child(#{round($num * 0.5)}) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,6 +67,8 @@
|
||||
///
|
||||
/// @require {function} _fetch-bourbon-setting
|
||||
|
||||
@use "sass:math";
|
||||
|
||||
@function modular-scale(
|
||||
$increment,
|
||||
$value: _fetch-bourbon-setting("modular-scale-base"),
|
||||
@ -78,7 +80,7 @@
|
||||
|
||||
// scale $v2 to just above $v1
|
||||
@while $v2 > $v1 {
|
||||
$v2: ($v2 / $ratio); // will be off-by-1
|
||||
$v2: math.div($v2, $ratio); // will be off-by-1
|
||||
}
|
||||
@while $v2 < $v1 {
|
||||
$v2: ($v2 * $ratio); // will fix off-by-1
|
||||
@ -102,15 +104,15 @@
|
||||
@if $increment < 0 {
|
||||
// adjust $v2 to just below $v1
|
||||
@if $double-stranded {
|
||||
$v2: ($v2 / $ratio);
|
||||
$v2: math.div($v2, $ratio);
|
||||
}
|
||||
|
||||
@for $i from $increment through -1 {
|
||||
@if $double-stranded and ($v1 / $ratio) < $v2 {
|
||||
@if $double-stranded and math.div($v1, $ratio) < $v2 {
|
||||
$value: $v2;
|
||||
$v2: ($v2 / $ratio);
|
||||
$v2: math.div($v2, $ratio);
|
||||
} @else {
|
||||
$v1: ($v1 / $ratio);
|
||||
$v1: math.div($v1, $ratio);
|
||||
$value: $v1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,6 +12,8 @@
|
||||
/// // Output
|
||||
/// $dimension: 10;
|
||||
|
||||
@use "sass:math";
|
||||
|
||||
@function strip-unit($value) {
|
||||
@return ($value / ($value * 0 + 1));
|
||||
@return math.div($value, $value * 0 + 1);
|
||||
}
|
||||
|
||||
@ -55,25 +55,25 @@
|
||||
|
||||
@if $direction == "up" {
|
||||
border-color: transparent transparent $color;
|
||||
border-width: 0 ($width / 2) $height;
|
||||
border-width: 0 ($width * 0.5) $height;
|
||||
} @else if $direction == "up-right" {
|
||||
border-color: transparent $color transparent transparent;
|
||||
border-width: 0 $width $width 0;
|
||||
} @else if $direction == "right" {
|
||||
border-color: transparent transparent transparent $color;
|
||||
border-width: ($height / 2) 0 ($height / 2) $width;
|
||||
border-width: ($height * 0.5) 0 ($height * 0.5) $width;
|
||||
} @else if $direction == "down-right" {
|
||||
border-color: transparent transparent $color;
|
||||
border-width: 0 0 $width $width;
|
||||
} @else if $direction == "down" {
|
||||
border-color: $color transparent transparent;
|
||||
border-width: $height ($width / 2) 0;
|
||||
border-width: $height ($width * 0.5) 0;
|
||||
} @else if $direction == "down-left" {
|
||||
border-color: transparent transparent transparent $color;
|
||||
border-width: $width 0 0 $width;
|
||||
} @else if $direction == "left" {
|
||||
border-color: transparent $color transparent transparent;
|
||||
border-width: ($height / 2) $width ($height / 2) 0;
|
||||
border-width: ($height * 0.5) $width ($height * 0.5) 0;
|
||||
} @else if $direction == "up-left" {
|
||||
border-color: $color transparent transparent;
|
||||
border-width: $width $width 0 0;
|
||||
|
||||
@ -19,13 +19,15 @@
|
||||
///
|
||||
/// @access private
|
||||
|
||||
@use "sass:math";
|
||||
|
||||
@function _contrast-ratio($color-1, $color-2) {
|
||||
$-local-lightness-1: _lightness($color-1) + 0.05;
|
||||
$-local-lightness-2: _lightness($color-2) + 0.05;
|
||||
|
||||
@if $-local-lightness-1 > $-local-lightness-2 {
|
||||
@return $-local-lightness-1 / $-local-lightness-2;
|
||||
@return math.div($-local-lightness-1, $-local-lightness-2);
|
||||
} @else {
|
||||
@return $-local-lightness-2 / $-local-lightness-1;
|
||||
@return math.div($-local-lightness-2, $-local-lightness-1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,11 +11,13 @@
|
||||
///
|
||||
/// @access private
|
||||
|
||||
@use "sass:math";
|
||||
|
||||
@function _gamma($channel) {
|
||||
@if $channel < 0.03928 {
|
||||
@return $channel / 12.92;
|
||||
@return math.div($channel, 12.92);
|
||||
} @else {
|
||||
$c: ($channel + 0.055) / 1.055;
|
||||
@return (133 * $c * $c * $c + 155 * $c * $c) / 288;
|
||||
$c: math.div($channel + 0.055, 1.055);
|
||||
@return math.div(133 * $c * $c * $c + 155 * $c * $c, 288);
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,14 +11,16 @@
|
||||
///
|
||||
/// @access private
|
||||
|
||||
@use "sass:math";
|
||||
|
||||
@function _lightness($hex-color) {
|
||||
$-local-red-raw: red(rgba($hex-color, 1));
|
||||
$-local-green-raw: green(rgba($hex-color, 1));
|
||||
$-local-blue-raw: blue(rgba($hex-color, 1));
|
||||
|
||||
$-local-red: _gamma($-local-red-raw / 255);
|
||||
$-local-green: _gamma($-local-green-raw / 255);
|
||||
$-local-blue: _gamma($-local-blue-raw / 255);
|
||||
$-local-red: _gamma(math.div($-local-red-raw, 255));
|
||||
$-local-green: _gamma(math.div($-local-green-raw, 255));
|
||||
$-local-blue: _gamma(math.div($-local-blue-raw, 255));
|
||||
|
||||
@return $-local-red * 0.2126 + $-local-green * 0.7152 + $-local-blue * 0.0722;
|
||||
}
|
||||
|
||||
@ -1,5 +1,20 @@
|
||||
"use strict";
|
||||
|
||||
import {Socket} from "phoenix";
|
||||
import LiveSocket from "phoenix_live_view";
|
||||
|
||||
let csrfToken = document
|
||||
.querySelector("meta[name='csrf-token']").getAttribute("content");
|
||||
let liveSocket = new LiveSocket(
|
||||
"/live",
|
||||
Socket,
|
||||
{params: {_csrf_token: csrfToken}}
|
||||
);
|
||||
|
||||
liveSocket.connect()
|
||||
|
||||
window.liveSocket = liveSocket
|
||||
|
||||
import "@babel/polyfill";
|
||||
import "phoenix_html";
|
||||
|
||||
@ -7,8 +22,6 @@ import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import { createStore } from "redux";
|
||||
|
||||
import css from "../css/app.scss";
|
||||
|
||||
import Game from "./components/game";
|
||||
import OpponentFinder from "./components/opponent-finder";
|
||||
import chessBoardReducer from "./reducers/chess-board";
|
||||
|
||||
@ -3,18 +3,19 @@
|
||||
"description": " ",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"deploy": "webpack --mode production",
|
||||
"watch": "webpack --mode development --watch-stdin"
|
||||
"deploy": "cd .. && mix assets.deploy && rm -f _build/esbuild"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "^7.12.1",
|
||||
"axios": "^0.21.0",
|
||||
"classnames": "^2.3.1",
|
||||
"esbuild": "^0.14.21",
|
||||
"gettext.js": "^1.0.0",
|
||||
"immutable": "^3.8.2",
|
||||
"lodash": "^4.17.21",
|
||||
"phoenix": "file:../deps/phoenix",
|
||||
"phoenix_html": "file:../deps/phoenix_html",
|
||||
"phoenix": "^1.7.0-rc.2",
|
||||
"phoenix_html": "^3.2.0",
|
||||
"phoenix_live_view": "^0.18.11",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-redux": "^7.2.4",
|
||||
@ -23,18 +24,7 @@
|
||||
"url-loader": "^4.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.14.6",
|
||||
"@babel/preset-env": "^7.14.7",
|
||||
"@babel/preset-react": "^7.14.5",
|
||||
"babel-loader": "^8.2.2",
|
||||
"copy-webpack-plugin": "^9.0.0",
|
||||
"css-loader": "^5.2.6",
|
||||
"mini-css-extract-plugin": "^1.6.0",
|
||||
"node-sass": "^6.0.0",
|
||||
"optimize-css-assets-webpack-plugin": "^6.0.1",
|
||||
"sass-loader": "^12.1.0",
|
||||
"terser-webpack-plugin": "^5.1.3",
|
||||
"webpack": "^5.40.0",
|
||||
"webpack-cli": "^4.7.2"
|
||||
"mini-css-extract-plugin": "^1.6.0"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,56 +0,0 @@
|
||||
const path = require("path");
|
||||
const glob = require("glob");
|
||||
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
||||
const TerserPlugin = require("terser-webpack-plugin");
|
||||
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
|
||||
const CopyWebpackPlugin = require("copy-webpack-plugin");
|
||||
|
||||
module.exports = (env, options) => {
|
||||
const devMode = options.mode !== "production";
|
||||
|
||||
return {
|
||||
optimization: {
|
||||
minimizer: [new TerserPlugin(), new OptimizeCSSAssetsPlugin({})],
|
||||
},
|
||||
entry: {
|
||||
app: glob.sync("./vendor/**/*.js").concat(["./js/app.js"]),
|
||||
},
|
||||
output: {
|
||||
filename: "[name].js",
|
||||
path: path.resolve(__dirname, "../priv/static/js"),
|
||||
publicPath: "/js/",
|
||||
},
|
||||
devtool: devMode ? "eval-cheap-module-source-map" : undefined,
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: "babel-loader",
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.s?css$/,
|
||||
use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpg|gif|svg)$/i,
|
||||
use: [
|
||||
{
|
||||
loader: "url-loader",
|
||||
options: {
|
||||
limit: 8192,
|
||||
name: "[name].[hash:7].[ext]",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new MiniCssExtractPlugin({ filename: "../css/app.css" }),
|
||||
new CopyWebpackPlugin({ patterns: [{ from: "static/", to: "../" }] }),
|
||||
],
|
||||
};
|
||||
};
|
||||
734
assets/yarn-error.log
Normal file
734
assets/yarn-error.log
Normal file
@ -0,0 +1,734 @@
|
||||
Arguments:
|
||||
/Users/danbarber/.asdf/installs/nodejs/16.14.0/bin/node /Users/danbarber/.asdf/installs/yarn/1.22.10/bin/yarn.js add phoenix phoenix_html phoenix_live_view
|
||||
|
||||
PATH:
|
||||
/Users/danbarber/.asdf/plugins/nodejs/shims:/Users/danbarber/.asdf/installs/nodejs/16.14.0/bin:/Users/danbarber/.asdf/installs/yarn/1.22.10/bin:/Users/danbarber/.bin:/usr/local/share/npm/bin:.git/safe/../../bin:/Users/danbarber/.asdf/shims:/opt/homebrew/bin/../opt/asdf/libexec/bin:/usr/local/sbin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Applications/VMware Fusion.app/Contents/Public:/usr/local/zfs/bin:/opt/X11/bin:/Library/Apple/usr/bin:/Applications/Wireshark.app/Contents/MacOS:/Applications/kitty.app/Contents/MacOS:/Users/danbarber/.fzf/bin
|
||||
|
||||
Yarn version:
|
||||
1.22.10
|
||||
|
||||
Node version:
|
||||
16.14.0
|
||||
|
||||
Platform:
|
||||
darwin arm64
|
||||
|
||||
Trace:
|
||||
Error: ENOENT: no such file or directory, open '/Users/danbarber/Library/Caches/Yarn/v6/npm-esbuild-linux-arm64-0.14.21-e05599ea6253b58394157da162d856f3ead62f9e-integrity/node_modules/esbuild-linux-arm64/.yarn-metadata.json'
|
||||
|
||||
npm manifest:
|
||||
{
|
||||
"repository": {},
|
||||
"description": " ",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"deploy": "cd .. && mix assets.deploy && rm -f _build/esbuild"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "^7.12.1",
|
||||
"axios": "^0.21.0",
|
||||
"classnames": "^2.3.1",
|
||||
"esbuild": "^0.14.21",
|
||||
"gettext.js": "^1.0.0",
|
||||
"immutable": "^3.8.2",
|
||||
"lodash": "^4.17.21",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-redux": "^7.2.4",
|
||||
"redux": "^4.1.0",
|
||||
"redux-watch": "^1.2.0",
|
||||
"url-loader": "^4.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"css-loader": "^5.2.6",
|
||||
"mini-css-extract-plugin": "^1.6.0"
|
||||
}
|
||||
}
|
||||
|
||||
yarn manifest:
|
||||
No manifest
|
||||
|
||||
Lockfile:
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@babel/polyfill@^7.12.1":
|
||||
version "7.12.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/polyfill/-/polyfill-7.12.1.tgz#1f2d6371d1261bbd961f3c5d5909150e12d0bd96"
|
||||
integrity sha512-X0pi0V6gxLi6lFZpGmeNa4zxtwEmCs42isWLNjZZDE0Y8yVfgu0T2OAHlzBbdYlqbW/YXVvoBHpATEM+goCj8g==
|
||||
dependencies:
|
||||
core-js "^2.6.5"
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@^7.12.1", "@babel/runtime@^7.9.2":
|
||||
version "7.14.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d"
|
||||
integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@types/hoist-non-react-statics@^3.3.0":
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
|
||||
integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
|
||||
"@types/json-schema@^7.0.6":
|
||||
version "7.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
|
||||
integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==
|
||||
|
||||
"@types/prop-types@*":
|
||||
version "15.7.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
|
||||
integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
|
||||
|
||||
"@types/react-redux@^7.1.16":
|
||||
version "7.1.16"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.16.tgz#0fbd04c2500c12105494c83d4a3e45c084e3cb21"
|
||||
integrity sha512-f/FKzIrZwZk7YEO9E1yoxIuDNRiDducxkFlkw/GNMGEnK9n4K8wJzlJBghpSuOVDgEUHoDkDF7Gi9lHNQR4siw==
|
||||
dependencies:
|
||||
"@types/hoist-non-react-statics" "^3.3.0"
|
||||
"@types/react" "*"
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
redux "^4.0.0"
|
||||
|
||||
"@types/react@*":
|
||||
version "17.0.11"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.11.tgz#67fcd0ddbf5a0b083a0f94e926c7d63f3b836451"
|
||||
integrity sha512-yFRQbD+whVonItSk7ZzP/L+gPTJVBkL/7shLEF+i9GC/1cV3JmUxEQz6+9ylhUpWSDuqo1N9qEvqS6vTj4USUA==
|
||||
dependencies:
|
||||
"@types/prop-types" "*"
|
||||
"@types/scheduler" "*"
|
||||
csstype "^3.0.2"
|
||||
|
||||
"@types/scheduler@*":
|
||||
version "0.16.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.1.tgz#18845205e86ff0038517aab7a18a62a6b9f71275"
|
||||
integrity sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA==
|
||||
|
||||
ajv-keywords@^3.5.2:
|
||||
version "3.5.2"
|
||||
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
|
||||
integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
|
||||
|
||||
ajv@^6.12.5:
|
||||
version "6.12.6"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
|
||||
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
|
||||
dependencies:
|
||||
fast-deep-equal "^3.1.1"
|
||||
fast-json-stable-stringify "^2.0.0"
|
||||
json-schema-traverse "^0.4.1"
|
||||
uri-js "^4.2.2"
|
||||
|
||||
ansi-styles@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.0.0.tgz#cb102df1c56f5123eab8b67cd7b98027a0279178"
|
||||
integrity sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg=
|
||||
|
||||
axios@^0.21.0:
|
||||
version "0.21.1"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
|
||||
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
|
||||
dependencies:
|
||||
follow-redirects "^1.10.0"
|
||||
|
||||
big.js@^5.2.2:
|
||||
version "5.2.2"
|
||||
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
|
||||
integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
|
||||
|
||||
chalk@~0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.4.0.tgz#5199a3ddcd0c1efe23bc08c1b027b06176e0c64f"
|
||||
integrity sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=
|
||||
dependencies:
|
||||
ansi-styles "~1.0.0"
|
||||
has-color "~0.1.0"
|
||||
strip-ansi "~0.1.0"
|
||||
|
||||
classnames@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e"
|
||||
integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==
|
||||
|
||||
colorette@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
|
||||
integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==
|
||||
|
||||
core-js@^2.6.5:
|
||||
version "2.6.12"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
|
||||
integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
|
||||
|
||||
css-loader@^5.2.6:
|
||||
version "5.2.6"
|
||||
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.6.tgz#c3c82ab77fea1f360e587d871a6811f4450cc8d1"
|
||||
integrity sha512-0wyN5vXMQZu6BvjbrPdUJvkCzGEO24HC7IS7nW4llc6BBFC+zwR9CKtYGv63Puzsg10L/o12inMY5/2ByzfD6w==
|
||||
dependencies:
|
||||
icss-utils "^5.1.0"
|
||||
loader-utils "^2.0.0"
|
||||
postcss "^8.2.15"
|
||||
postcss-modules-extract-imports "^3.0.0"
|
||||
postcss-modules-local-by-default "^4.0.0"
|
||||
postcss-modules-scope "^3.0.0"
|
||||
postcss-modules-values "^4.0.0"
|
||||
postcss-value-parser "^4.1.0"
|
||||
schema-utils "^3.0.0"
|
||||
semver "^7.3.5"
|
||||
|
||||
cssesc@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
|
||||
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
|
||||
|
||||
csstype@^3.0.2:
|
||||
version "3.0.8"
|
||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.8.tgz#d2266a792729fb227cd216fb572f43728e1ad340"
|
||||
integrity sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==
|
||||
|
||||
emojis-list@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
|
||||
integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
|
||||
|
||||
encoding@^0.1.11:
|
||||
version "0.1.13"
|
||||
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9"
|
||||
integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==
|
||||
dependencies:
|
||||
iconv-lite "^0.6.2"
|
||||
|
||||
esbuild-android-arm64@0.14.21:
|
||||
version "0.14.21"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.21.tgz#8842d0c3b7c81fbe2dc46ddb416ffd6eb822184b"
|
||||
integrity sha512-Bqgld1TY0wZv8TqiQmVxQFgYzz8ZmyzT7clXBDZFkOOdRybzsnj8AZuK1pwcLVA7Ya6XncHgJqIao7NFd3s0RQ==
|
||||
|
||||
esbuild-darwin-64@0.14.21:
|
||||
version "0.14.21"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.21.tgz#ec7df02ad88ecf7f8fc23a3ed7917e07dea0c9c9"
|
||||
integrity sha512-j+Eg+e13djzyYINVvAbOo2/zvZ2DivuJJTaBrJnJHSD7kUNuGHRkHoSfFjbI80KHkn091w350wdmXDNSgRjfYQ==
|
||||
|
||||
esbuild-darwin-arm64@0.14.21:
|
||||
version "0.14.21"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.21.tgz#0c2a977edec1ef54097ee56a911518c820d4e5e4"
|
||||
integrity sha512-nDNTKWDPI0RuoPj5BhcSB2z5EmZJJAyRtZLIjyXSqSpAyoB8eyAKXl4lB8U2P78Fnh4Lh1le/fmpewXE04JhBQ==
|
||||
|
||||
esbuild-freebsd-64@0.14.21:
|
||||
version "0.14.21"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.21.tgz#f5b5fc1d031286c3a0949d1bda7db774b7d0404e"
|
||||
integrity sha512-zIurkCHXhxELiDZtLGiexi8t8onQc2LtuE+S7457H/pP0g0MLRKMrsn/IN4LDkNe6lvBjuoZZi2OfelOHn831g==
|
||||
|
||||
esbuild-freebsd-arm64@0.14.21:
|
||||
version "0.14.21"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.21.tgz#a05cab908013e4992b31a675850b8c44eb468c0c"
|
||||
integrity sha512-wdxMmkJfbwcN+q85MpeUEamVZ40FNsBa9mPq8tAszDn8TRT2HoJvVRADPIIBa9SWWwlDChIMjkDKAnS3KS/sPA==
|
||||
|
||||
esbuild-linux-32@0.14.21:
|
||||
version "0.14.21"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.21.tgz#638d244cc58b951f447addb4bade628d126ef84b"
|
||||
integrity sha512-fmxvyzOPPh2xiEHojpCeIQP6pXcoKsWbz3ryDDIKLOsk4xp3GbpHIEAWP0xTeuhEbendmvBDVKbAVv3PnODXLg==
|
||||
|
||||
esbuild-linux-64@0.14.21:
|
||||
version "0.14.21"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.21.tgz#8eb634abee928be7e35b985fafbfef2f2e31397f"
|
||||
integrity sha512-edZyNOv1ql+kpmlzdqzzDjRQYls+tSyi4QFi+PdBhATJFUqHsnNELWA9vMSzAaInPOEaVUTA5Ml28XFChcy4DA==
|
||||
|
||||
esbuild-linux-arm64@0.14.21:
|
||||
version "0.14.21"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.21.tgz#e05599ea6253b58394157da162d856f3ead62f9e"
|
||||
integrity sha512-t5qxRkq4zdQC0zXpzSB2bTtfLgOvR0C6BXYaRE/6/k8/4SrkZcTZBeNu+xGvwCU4b5dU9ST9pwIWkK6T1grS8g==
|
||||
|
||||
esbuild-linux-arm@0.14.21:
|
||||
version "0.14.21"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.21.tgz#1ae1078231cf689d3ba894a32d3723c0be9b91fd"
|
||||
integrity sha512-aSU5pUueK6afqmLQsbU+QcFBT62L+4G9hHMJDHWfxgid6hzhSmfRH9U/f+ymvxsSTr/HFRU4y7ox8ZyhlVl98w==
|
||||
|
||||
esbuild-linux-mips64le@0.14.21:
|
||||
version "0.14.21"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.21.tgz#f05be62d126764e99b37edcac5bb49b78c7a8890"
|
||||
integrity sha512-jLZLQGCNlUsmIHtGqNvBs3zN+7a4D9ckf0JZ+jQTwHdZJ1SgV9mAjbB980OFo66LoY+WeM7t3WEnq3FjI1zw4A==
|
||||
|
||||
esbuild-linux-ppc64le@0.14.21:
|
||||
version "0.14.21"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.21.tgz#592c98d82dad7982268ef8deed858c4566f07ab1"
|
||||
integrity sha512-4TWxpK391en2UBUw6GSrukToTDu6lL9vkm3Ll40HrI08WG3qcnJu7bl8e1+GzelDsiw1QmfAY/nNvJ6iaHRpCQ==
|
||||
|
||||
esbuild-linux-riscv64@0.14.21:
|
||||
version "0.14.21"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.21.tgz#0db7bd6f10d8f9afea973a7d6bf87b449b864b7b"
|
||||
integrity sha512-fElngqOaOfTsF+u+oetDLHsPG74vB2ZaGZUqmGefAJn3a5z9Z2pNa4WpVbbKgHpaAAy5tWM1m1sbGohj6Ki6+Q==
|
||||
|
||||
esbuild-linux-s390x@0.14.21:
|
||||
version "0.14.21"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.21.tgz#254a9354d34c9d1b41a3e21d2ec9269cbbb2c5df"
|
||||
integrity sha512-brleZ6R5fYv0qQ7ZBwenQmP6i9TdvJCB092c/3D3pTLQHBGHJb5zWgKxOeS7bdHzmLy6a6W7GbFk6QKpjyD6QA==
|
||||
|
||||
esbuild-netbsd-64@0.14.21:
|
||||
version "0.14.21"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.21.tgz#4cb783d060b02bf3b897a9a12cce2b3b547726f8"
|
||||
integrity sha512-nCEgsLCQ8RoFWVV8pVI+kX66ICwbPP/M9vEa0NJGIEB/Vs5sVGMqkf67oln90XNSkbc0bPBDuo4G6FxlF7PN8g==
|
||||
|
||||
esbuild-openbsd-64@0.14.21:
|
||||
version "0.14.21"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.21.tgz#f886b93feefddbe573528fa4b421c9c6e2bc969b"
|
||||
integrity sha512-h9zLMyVD0T73MDTVYIb/qUTokwI6EJH9O6wESuTNq6+XpMSr6C5aYZ4fvFKdNELW+Xsod+yDS2hV2JTUAbFrLA==
|
||||
|
||||
esbuild-sunos-64@0.14.21:
|
||||
version "0.14.21"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.21.tgz#3829e4d57d4cb6950837fe90b0b67cdfb37cf13a"
|
||||
integrity sha512-Kl+7Cot32qd9oqpLdB1tEGXEkjBlijrIxMJ0+vlDFaqsODutif25on0IZlFxEBtL2Gosd4p5WCV1U7UskNQfXA==
|
||||
|
||||
esbuild-windows-32@0.14.21:
|
||||
version "0.14.21"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.21.tgz#b858a22d1a82e53cdc59310cd56294133f7a95e7"
|
||||
integrity sha512-V7vnTq67xPBUCk/9UtlolmQ798Ecjdr1ZoI1vcSgw7M82aSSt0eZdP6bh5KAFZU8pxDcx3qoHyWQfHYr11f22A==
|
||||
|
||||
esbuild-windows-64@0.14.21:
|
||||
version "0.14.21"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.21.tgz#7bb5a027d5720cf9caf18a4bedd11327208f1f12"
|
||||
integrity sha512-kDgHjKOHwjfJDCyRGELzVxiP/RBJBTA+wyspf78MTTJQkyPuxH2vChReNdWc+dU2S4gIZFHMdP1Qrl/k22ZmaA==
|
||||
|
||||
esbuild-windows-arm64@0.14.21:
|
||||
version "0.14.21"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.21.tgz#25df54521ad602c826b262ea2e7cc1fe80f5c2f5"
|
||||
integrity sha512-8Sbo0zpzgwWrwjQYLmHF78f7E2xg5Ve63bjB2ng3V2aManilnnTGaliq2snYg+NOX60+hEvJHRdVnuIAHW0lVw==
|
||||
|
||||
esbuild@^0.14.21:
|
||||
version "0.14.21"
|
||||
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.21.tgz#b3e05f900f1c4394f596d60d63d9816468f0f671"
|
||||
integrity sha512-7WEoNMBJdLN993dr9h0CpFHPRc3yFZD+EAVY9lg6syJJ12gc5fHq8d75QRExuhnMkT2DaRiIKFThRvDWP+fO+A==
|
||||
optionalDependencies:
|
||||
esbuild-android-arm64 "0.14.21"
|
||||
esbuild-darwin-64 "0.14.21"
|
||||
esbuild-darwin-arm64 "0.14.21"
|
||||
esbuild-freebsd-64 "0.14.21"
|
||||
esbuild-freebsd-arm64 "0.14.21"
|
||||
esbuild-linux-32 "0.14.21"
|
||||
esbuild-linux-64 "0.14.21"
|
||||
esbuild-linux-arm "0.14.21"
|
||||
esbuild-linux-arm64 "0.14.21"
|
||||
esbuild-linux-mips64le "0.14.21"
|
||||
esbuild-linux-ppc64le "0.14.21"
|
||||
esbuild-linux-riscv64 "0.14.21"
|
||||
esbuild-linux-s390x "0.14.21"
|
||||
esbuild-netbsd-64 "0.14.21"
|
||||
esbuild-openbsd-64 "0.14.21"
|
||||
esbuild-sunos-64 "0.14.21"
|
||||
esbuild-windows-32 "0.14.21"
|
||||
esbuild-windows-64 "0.14.21"
|
||||
esbuild-windows-arm64 "0.14.21"
|
||||
|
||||
fast-deep-equal@^3.1.1:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
||||
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
|
||||
|
||||
fast-json-stable-stringify@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
|
||||
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
|
||||
|
||||
follow-redirects@^1.10.0:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.1.tgz#d9114ded0a1cfdd334e164e6662ad02bfd91ff43"
|
||||
integrity sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==
|
||||
|
||||
gettext-parser@1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/gettext-parser/-/gettext-parser-1.1.0.tgz#2c5a6638d893934b9b55037d0ad82cb7004b2679"
|
||||
integrity sha1-LFpmONiTk0ubVQN9CtgstwBLJnk=
|
||||
dependencies:
|
||||
encoding "^0.1.11"
|
||||
|
||||
gettext.js@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/gettext.js/-/gettext.js-1.0.0.tgz#7fefb01512c134759c51166ab4d3db26a585ae1a"
|
||||
integrity sha512-cpnxNL5C9SlD7ms/NSCuGsQdaVQmwCYn9MILWpYjSPMAkX4aD/5/qC+QgH4GCRY0OMEcSiVBsqgWMEoTcETggQ==
|
||||
dependencies:
|
||||
po2json "^0.4.0"
|
||||
|
||||
has-color@~0.1.0:
|
||||
version "0.1.7"
|
||||
resolved "https://registry.yarnpkg.com/has-color/-/has-color-0.1.7.tgz#67144a5260c34fc3cca677d041daf52fe7b78b2f"
|
||||
integrity sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8=
|
||||
|
||||
hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
|
||||
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
|
||||
dependencies:
|
||||
react-is "^16.7.0"
|
||||
|
||||
iconv-lite@^0.6.2:
|
||||
version "0.6.3"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
|
||||
integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
|
||||
dependencies:
|
||||
safer-buffer ">= 2.1.2 < 3.0.0"
|
||||
|
||||
icss-utils@^5.0.0, icss-utils@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae"
|
||||
integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==
|
||||
|
||||
immutable@^3.8.2:
|
||||
version "3.8.2"
|
||||
resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3"
|
||||
integrity sha1-wkOZUUVbs5kT2vKBN28VMOEErfM=
|
||||
|
||||
"js-tokens@^3.0.0 || ^4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
||||
|
||||
json-schema-traverse@^0.4.1:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
|
||||
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
|
||||
|
||||
json5@^2.1.2:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3"
|
||||
integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==
|
||||
dependencies:
|
||||
minimist "^1.2.5"
|
||||
|
||||
loader-utils@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0"
|
||||
integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==
|
||||
dependencies:
|
||||
big.js "^5.2.2"
|
||||
emojis-list "^3.0.0"
|
||||
json5 "^2.1.2"
|
||||
|
||||
lodash@^4.17.21:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
|
||||
loose-envify@^1.1.0, loose-envify@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
|
||||
dependencies:
|
||||
js-tokens "^3.0.0 || ^4.0.0"
|
||||
|
||||
lru-cache@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
|
||||
integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
|
||||
dependencies:
|
||||
yallist "^4.0.0"
|
||||
|
||||
mime-db@1.48.0:
|
||||
version "1.48.0"
|
||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.48.0.tgz#e35b31045dd7eada3aaad537ed88a33afbef2d1d"
|
||||
integrity sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==
|
||||
|
||||
mime-types@^2.1.27:
|
||||
version "2.1.31"
|
||||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.31.tgz#a00d76b74317c61f9c2db2218b8e9f8e9c5c9e6b"
|
||||
integrity sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==
|
||||
dependencies:
|
||||
mime-db "1.48.0"
|
||||
|
||||
mini-css-extract-plugin@^1.6.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.0.tgz#b4db2525af2624899ed64a23b0016e0036411893"
|
||||
integrity sha512-nPFKI7NSy6uONUo9yn2hIfb9vyYvkFu95qki0e21DQ9uaqNKDP15DGpK0KnV6wDroWxPHtExrdEwx/yDQ8nVRw==
|
||||
dependencies:
|
||||
loader-utils "^2.0.0"
|
||||
schema-utils "^3.0.0"
|
||||
webpack-sources "^1.1.0"
|
||||
|
||||
minimist@^1.2.5:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
||||
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
||||
|
||||
nanoid@^3.1.23:
|
||||
version "3.1.23"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81"
|
||||
integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==
|
||||
|
||||
nomnom@1.8.1:
|
||||
version "1.8.1"
|
||||
resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.8.1.tgz#2151f722472ba79e50a76fc125bb8c8f2e4dc2a7"
|
||||
integrity sha1-IVH3Ikcrp55Qp2/BJbuMjy5Nwqc=
|
||||
dependencies:
|
||||
chalk "~0.4.0"
|
||||
underscore "~1.6.0"
|
||||
|
||||
object-assign@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
|
||||
|
||||
object-path@^0.11.5:
|
||||
version "0.11.5"
|
||||
resolved "https://registry.yarnpkg.com/object-path/-/object-path-0.11.5.tgz#d4e3cf19601a5140a55a16ad712019a9c50b577a"
|
||||
integrity sha512-jgSbThcoR/s+XumvGMTMf81QVBmah+/Q7K7YduKeKVWL7N111unR2d6pZZarSk6kY/caeNxUDyxOvMWyzoU2eg==
|
||||
|
||||
"phoenix@file:../deps/phoenix":
|
||||
version "1.6.6"
|
||||
|
||||
"phoenix_html@file:../deps/phoenix_html":
|
||||
<<<<<<< HEAD
|
||||
version "3.2.0"
|
||||
=======
|
||||
version "2.14.3"
|
||||
|
||||
"phoenix_live_view@file:../deps/phoenix_live_view":
|
||||
version "0.15.3"
|
||||
|
||||
picomatch@^2.2.1, picomatch@^2.2.3:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972"
|
||||
integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==
|
||||
"phoenix_live_view@file:../deps/phoenix_live_view":
|
||||
version "0.15.3"
|
||||
|
||||
pify@^2.0.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
|
||||
integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw=
|
||||
|
||||
pinkie-promise@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
|
||||
integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o=
|
||||
dependencies:
|
||||
pinkie "^2.0.0"
|
||||
|
||||
pinkie@^2.0.0:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
|
||||
integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA=
|
||||
|
||||
pkg-dir@^4.1.0, pkg-dir@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3"
|
||||
integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==
|
||||
dependencies:
|
||||
find-up "^4.0.0"
|
||||
>>>>>>> 7af5f8c (Install LiveView)
|
||||
|
||||
po2json@^0.4.0:
|
||||
version "0.4.5"
|
||||
resolved "https://registry.yarnpkg.com/po2json/-/po2json-0.4.5.tgz#47bb2952da32d58a1be2f256a598eebc0b745118"
|
||||
integrity sha1-R7spUtoy1Yob4vJWpZjuvAt0URg=
|
||||
dependencies:
|
||||
gettext-parser "1.1.0"
|
||||
nomnom "1.8.1"
|
||||
|
||||
postcss-modules-extract-imports@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d"
|
||||
integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==
|
||||
|
||||
postcss-modules-local-by-default@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c"
|
||||
integrity sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==
|
||||
dependencies:
|
||||
icss-utils "^5.0.0"
|
||||
postcss-selector-parser "^6.0.2"
|
||||
postcss-value-parser "^4.1.0"
|
||||
|
||||
postcss-modules-scope@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06"
|
||||
integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==
|
||||
dependencies:
|
||||
postcss-selector-parser "^6.0.4"
|
||||
|
||||
postcss-modules-values@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c"
|
||||
integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==
|
||||
dependencies:
|
||||
icss-utils "^5.0.0"
|
||||
|
||||
postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4:
|
||||
version "6.0.6"
|
||||
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz#2c5bba8174ac2f6981ab631a42ab0ee54af332ea"
|
||||
integrity sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==
|
||||
dependencies:
|
||||
cssesc "^3.0.0"
|
||||
util-deprecate "^1.0.2"
|
||||
|
||||
postcss-value-parser@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb"
|
||||
integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==
|
||||
|
||||
postcss@^8.2.15:
|
||||
version "8.3.5"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.5.tgz#982216b113412bc20a86289e91eb994952a5b709"
|
||||
integrity sha512-NxTuJocUhYGsMiMFHDUkmjSKT3EdH4/WbGF6GCi1NDGk+vbcUTun4fpbOqaPtD8IIsztA2ilZm2DhYCuyN58gA==
|
||||
dependencies:
|
||||
colorette "^1.2.2"
|
||||
nanoid "^3.1.23"
|
||||
source-map-js "^0.6.2"
|
||||
|
||||
prop-types@^15.7.2:
|
||||
version "15.7.2"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
|
||||
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
|
||||
dependencies:
|
||||
loose-envify "^1.4.0"
|
||||
object-assign "^4.1.1"
|
||||
react-is "^16.8.1"
|
||||
|
||||
punycode@^2.1.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
||||
|
||||
react-dom@^17.0.2:
|
||||
version "17.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
|
||||
integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
scheduler "^0.20.2"
|
||||
|
||||
react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.1:
|
||||
version "16.13.1"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||
|
||||
react-redux@^7.2.4:
|
||||
version "7.2.4"
|
||||
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.4.tgz#1ebb474032b72d806de2e0519cd07761e222e225"
|
||||
integrity sha512-hOQ5eOSkEJEXdpIKbnRyl04LhaWabkDPV+Ix97wqQX3T3d2NQ8DUblNXXtNMavc7DpswyQM6xfaN4HQDKNY2JA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.1"
|
||||
"@types/react-redux" "^7.1.16"
|
||||
hoist-non-react-statics "^3.3.2"
|
||||
loose-envify "^1.4.0"
|
||||
prop-types "^15.7.2"
|
||||
react-is "^16.13.1"
|
||||
|
||||
react@^17.0.2:
|
||||
version "17.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
|
||||
integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
redux-watch@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/redux-watch/-/redux-watch-1.2.0.tgz#b3a745e15855ef72db0e06a60a4b2fe3676d79a7"
|
||||
integrity sha512-Ws4Q+e5zFGMyy1H709c1Ws8apSd6MqoJRIzBDHbI4nikome/IZWVTYXdQNz+VJxPjyX/h2E+lYEo41fXgjCF8g==
|
||||
dependencies:
|
||||
object-path "^0.11.5"
|
||||
|
||||
redux@^4.0.0, redux@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/redux/-/redux-4.1.0.tgz#eb049679f2f523c379f1aff345c8612f294c88d4"
|
||||
integrity sha512-uI2dQN43zqLWCt6B/BMGRMY6db7TTY4qeHHfGeKb3EOhmOKjU3KdWvNLJyqaHRksv/ErdNH7cFZWg9jXtewy4g==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
|
||||
regenerator-runtime@^0.13.4:
|
||||
version "0.13.7"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
|
||||
integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==
|
||||
|
||||
"safer-buffer@>= 2.1.2 < 3.0.0":
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
|
||||
scheduler@^0.20.2:
|
||||
version "0.20.2"
|
||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91"
|
||||
integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
schema-utils@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.0.0.tgz#67502f6aa2b66a2d4032b4279a2944978a0913ef"
|
||||
integrity sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==
|
||||
dependencies:
|
||||
"@types/json-schema" "^7.0.6"
|
||||
ajv "^6.12.5"
|
||||
ajv-keywords "^3.5.2"
|
||||
|
||||
semver@^7.3.5:
|
||||
version "7.3.5"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
|
||||
integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
source-list-map@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
|
||||
integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
|
||||
|
||||
source-map-js@^0.6.2:
|
||||
version "0.6.2"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e"
|
||||
integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==
|
||||
|
||||
source-map@~0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||
|
||||
strip-ansi@~0.1.0:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-0.1.1.tgz#39e8a98d044d150660abe4a6808acf70bb7bc991"
|
||||
integrity sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE=
|
||||
|
||||
underscore@~1.6.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8"
|
||||
integrity sha1-izixDKze9jM3uLJOT/htRa6lKag=
|
||||
|
||||
uri-js@^4.2.2:
|
||||
version "4.4.1"
|
||||
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
|
||||
integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
|
||||
dependencies:
|
||||
punycode "^2.1.0"
|
||||
|
||||
url-loader@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-4.1.1.tgz#28505e905cae158cf07c92ca622d7f237e70a4e2"
|
||||
integrity sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==
|
||||
dependencies:
|
||||
loader-utils "^2.0.0"
|
||||
mime-types "^2.1.27"
|
||||
schema-utils "^3.0.0"
|
||||
|
||||
util-deprecate@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||
|
||||
webpack-sources@^1.1.0:
|
||||
version "1.4.3"
|
||||
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933"
|
||||
integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==
|
||||
dependencies:
|
||||
source-list-map "^2.0.0"
|
||||
source-map "~0.6.1"
|
||||
|
||||
yallist@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
|
||||
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
|
||||
3994
assets/yarn.lock
3994
assets/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -3,5 +3,5 @@
|
||||
echo "Building assets"
|
||||
cd assets
|
||||
yarn install
|
||||
yarn run build
|
||||
yarn run deploy
|
||||
cd ..
|
||||
|
||||
@ -6,8 +6,8 @@ source /dev/stdin <<< "$(curl -sSL https://raw.githubusercontent.com/codeship/sc
|
||||
echo "Installing Elixir"
|
||||
source /dev/stdin <<< "$(curl -sSL https://raw.githubusercontent.com/codeship/scripts/master/languages/elixir.sh)"
|
||||
|
||||
echo "Installing PhantomJS"
|
||||
curl -sSL https://raw.githubusercontent.com/codeship/scripts/master/packages/phantomjs.sh | bash -s
|
||||
echo "Installing ChromeDriver"
|
||||
curl -sSL https://raw.githubusercontent.com/codeship/scripts/master/packages/chromedriver.sh | bash -s
|
||||
|
||||
echo "Installing NodeJS"
|
||||
nvm install $NODE_VERSION
|
||||
|
||||
18
codeship-services.yml
Normal file
18
codeship-services.yml
Normal file
@ -0,0 +1,18 @@
|
||||
# Docker Compose-like syntax, see here for details:
|
||||
# https://documentation.codeship.com/pro/builds-and-configuration/services/
|
||||
app:
|
||||
build:
|
||||
image: danbee/chess
|
||||
dockerfile: Dockerfile
|
||||
environment:
|
||||
MIX_ENV: test
|
||||
PGHOST: db
|
||||
PGUSER: postgres
|
||||
PGPASSWORD: password
|
||||
depends_on:
|
||||
- db
|
||||
db:
|
||||
image: healthcheck/postgres:alpine
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: password
|
||||
9
codeship-steps.yml
Normal file
9
codeship-steps.yml
Normal file
@ -0,0 +1,9 @@
|
||||
# This is where we define the test steps that would be run in a Codeship Pro build.
|
||||
# The build passes as long as every test step returns with a non-zero exit code.
|
||||
# See here for more: https://documentation.codeship.com/pro/builds-and-configuration/steps/
|
||||
- name: build_assets
|
||||
command: bin/codeship_assets
|
||||
service: app
|
||||
- name: run_mix_test
|
||||
command: /bin/bash -c 'mix ecto.create && mix test'
|
||||
service: app
|
||||
@ -3,7 +3,7 @@
|
||||
#
|
||||
# This configuration file is loaded before any dependency and
|
||||
# is restricted to this project.
|
||||
use Mix.Config
|
||||
import Config
|
||||
|
||||
config :phoenix, :json_library, Jason
|
||||
|
||||
@ -16,7 +16,8 @@ config :chess, ChessWeb.Endpoint,
|
||||
url: [host: "localhost"],
|
||||
secret_key_base: "iiTDTKorCWTFoeBgAkr35XZp22cNIM2RsmnHiHdzKAuSHXUGXx42z7lawAwiu1B1",
|
||||
render_errors: [view: ChessWeb.ErrorView, accepts: ~w(html json)],
|
||||
pubsub_server: Chess.PubSub
|
||||
pubsub_server: Chess.PubSub,
|
||||
live_view: [signing_salt: "R3JjvjiRi64kjFCHqCJCfk7EY8hohfadgLqO/VUFRO8="]
|
||||
|
||||
# Configures Elixir's Logger
|
||||
config :logger, :console,
|
||||
@ -30,8 +31,27 @@ config :chess, Chess.Auth.Guardian,
|
||||
secret_key: "vd2vXkrYTTFKSKmNMoS2/Hk4Fxn8BkyzsVArRkxJazdQ3mr6bI4YgAC6f8ODiWlM"
|
||||
|
||||
config :formulator,
|
||||
translate_error_module: ChessWeb.ErrorHelpers
|
||||
validate: false,
|
||||
translate_error_module: ChessWeb.ErrorHelpers
|
||||
|
||||
# Configure esbuild (the version is required)
|
||||
config :esbuild,
|
||||
version: "0.17.5",
|
||||
default: [
|
||||
args:
|
||||
~w(js/app.js --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/* --loader:.js=jsx),
|
||||
cd: Path.expand("../assets", __DIR__),
|
||||
env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
|
||||
]
|
||||
|
||||
# Configure dart_sass
|
||||
config :dart_sass,
|
||||
version: "1.58.0",
|
||||
default: [
|
||||
args: ~w(css/app.scss ../priv/static/assets/app.css),
|
||||
cd: Path.expand("../assets", __DIR__)
|
||||
]
|
||||
|
||||
# Import environment specific config. This must remain at the bottom
|
||||
# of this file so it overrides the configuration defined above.
|
||||
import_config "#{Mix.env}.exs"
|
||||
import_config "#{config_env()}.exs"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use Mix.Config
|
||||
import Config
|
||||
|
||||
# For development, we disable any cache and enable
|
||||
# debugging and code reloading.
|
||||
@ -10,16 +10,7 @@ config :chess, ChessWeb.Endpoint,
|
||||
http: [port: 4000],
|
||||
debug_errors: true,
|
||||
code_reloader: true,
|
||||
check_origin: false,
|
||||
watchers: [
|
||||
node: [
|
||||
"node_modules/webpack/bin/webpack.js",
|
||||
"--mode",
|
||||
"development",
|
||||
"--watch",
|
||||
cd: Path.expand("../assets", __DIR__)
|
||||
]
|
||||
]
|
||||
check_origin: false
|
||||
|
||||
# Watch static and templates for browser reloading.
|
||||
config :chess, ChessWeb.Endpoint,
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
use Mix.Config
|
||||
import Config
|
||||
|
||||
config :chess, ChessWeb.Endpoint,
|
||||
cache_static_manifest: "priv/static/cache_manifest.json",
|
||||
check_origin: ["https://chess.danbarber.me", "https://64squares.club"],
|
||||
check_origin: ["https://chess.danbee.in", "https://64squares.club"],
|
||||
http: [port: {:system, "PORT"}],
|
||||
root: "./assets",
|
||||
secret_key_base: System.get_env("SECRET_KEY_BASE"),
|
||||
|
||||
70
config/runtime.exs
Normal file
70
config/runtime.exs
Normal file
@ -0,0 +1,70 @@
|
||||
import Config
|
||||
|
||||
# config/runtime.exs is executed for all environments, including
|
||||
# during releases. It is executed after compilation and before the
|
||||
# system starts, so it is typically used to load production configuration
|
||||
# and secrets from environment variables or elsewhere. Do not define
|
||||
# any compile-time configuration in here, as it won't be applied.
|
||||
# The block below contains prod specific runtime configuration.
|
||||
|
||||
# Start the phoenix server if environment is set and running in a release
|
||||
if System.get_env("PHX_SERVER") && System.get_env("RELEASE_NAME") do
|
||||
config :chess, ChessWeb.Endpoint, server: true
|
||||
end
|
||||
|
||||
if config_env() == :prod do
|
||||
# The secret key base is used to sign/encrypt cookies and other secrets.
|
||||
# A default value is used in config/dev.exs and config/test.exs but you
|
||||
# want to use a different value for prod and you most likely don't want
|
||||
# to check this value into version control, so we use an environment
|
||||
# variable instead.
|
||||
secret_key_base =
|
||||
System.get_env("SECRET_KEY_BASE") ||
|
||||
raise """
|
||||
environment variable SECRET_KEY_BASE is missing.
|
||||
You can generate one by calling: mix phx.gen.secret
|
||||
"""
|
||||
|
||||
host = System.get_env("PHX_HOST") || "example.com"
|
||||
port = String.to_integer(System.get_env("PORT") || "4000")
|
||||
|
||||
config :chess, ChessWeb.Endpoint,
|
||||
url: [host: host, port: 443],
|
||||
http: [
|
||||
# Enable IPv6 and bind on all interfaces.
|
||||
# Set it to {0, 0, 0, 0, 0, 0, 0, 1} for local network only access.
|
||||
# See the documentation on https://hexdocs.pm/plug_cowboy/Plug.Cowboy.html
|
||||
# for details about using IPv6 vs IPv4 and loopback vs public addresses.
|
||||
ip: {0, 0, 0, 0, 0, 0, 0, 0},
|
||||
port: port
|
||||
],
|
||||
secret_key_base: secret_key_base
|
||||
|
||||
# ## Using releases
|
||||
#
|
||||
# If you are doing OTP releases, you need to instruct Phoenix
|
||||
# to start each relevant endpoint:
|
||||
#
|
||||
# config :<%= @web_app_name %>, <%= @endpoint_module %>, server: true
|
||||
#
|
||||
# Then you can assemble a release by calling `mix release`.
|
||||
# See `mix help release` for more information.<%= if @mailer do %>
|
||||
|
||||
# ## Configuring the mailer
|
||||
#
|
||||
# In production you need to configure the mailer to use a different adapter.
|
||||
# Also, you may need to configure the Swoosh API client of your choice if you
|
||||
# are not using SMTP. Here is an example of the configuration:
|
||||
#
|
||||
# config :<%= @app_name %>, <%= @app_module %>.Mailer,
|
||||
# adapter: Swoosh.Adapters.Mailgun,
|
||||
# api_key: System.get_env("MAILGUN_API_KEY"),
|
||||
# domain: System.get_env("MAILGUN_DOMAIN")
|
||||
#
|
||||
# For this example you need include a HTTP client required by Swoosh API client.
|
||||
# Swoosh supports Hackney and Finch out of the box:
|
||||
#
|
||||
# config :swoosh, :api_client, Swoosh.ApiClient.Hackney
|
||||
#
|
||||
# See https://hexdocs.pm/swoosh/Swoosh.html#module-installation for details.<% end %>
|
||||
end
|
||||
@ -1,4 +1,4 @@
|
||||
use Mix.Config
|
||||
import Config
|
||||
|
||||
# We don't run a server during test. If one is required,
|
||||
# you can enable the server option below.
|
||||
@ -18,7 +18,7 @@ config :chess, Chess.Mailer, adapter: Bamboo.TestAdapter
|
||||
config :chess, Chess.Repo,
|
||||
adapter: Ecto.Adapters.Postgres,
|
||||
database: "chess_test",
|
||||
hostname: "localhost",
|
||||
hostname: System.get_env("PGHOST") || "localhost",
|
||||
port: System.get_env("POSTGRES_PORT") || "5432",
|
||||
pool: Ecto.Adapters.SQL.Sandbox
|
||||
|
||||
|
||||
19
docker-compose.yml
Normal file
19
docker-compose.yml
Normal file
@ -0,0 +1,19 @@
|
||||
version: '3'
|
||||
services:
|
||||
app:
|
||||
build: .
|
||||
command: /bin/bash -c 'mix ecto.create && mix phx.server'
|
||||
ports:
|
||||
- "4000:4000"
|
||||
environment:
|
||||
MIX_ENV: dev
|
||||
PGHOST: db
|
||||
PGUSER: postgres
|
||||
PGPASSWORD: password
|
||||
links:
|
||||
- db
|
||||
db:
|
||||
image: healthcheck/postgres:alpine
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: password
|
||||
@ -1,5 +1,5 @@
|
||||
# Erlang version
|
||||
erlang_version=23.2.3
|
||||
erlang_version=OTP-24.2.1
|
||||
|
||||
# Elixir version
|
||||
elixir_version=1.11.3
|
||||
elixir_version=1.14.1
|
||||
|
||||
@ -11,7 +11,7 @@ defmodule Chess do
|
||||
{Phoenix.PubSub, [name: Chess.PubSub, adapter: Phoenix.PubSub.PG2]},
|
||||
{Chess.Repo, []},
|
||||
{ChessWeb.Endpoint, []},
|
||||
{ChessWeb.Presence, []},
|
||||
{ChessWeb.Presence, []}
|
||||
]
|
||||
|
||||
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
|
||||
|
||||
@ -4,7 +4,6 @@ defmodule Chess.Auth do
|
||||
"""
|
||||
|
||||
import Ecto.Query, warn: false
|
||||
alias Comeonin.Argon2
|
||||
alias Chess.Repo
|
||||
|
||||
alias Chess.Store.User
|
||||
@ -41,11 +40,13 @@ defmodule Chess.Auth do
|
||||
|
||||
@doc false
|
||||
def authenticate_user(email, password) do
|
||||
query = from u in User,
|
||||
where: u.email == ^email
|
||||
query =
|
||||
from(u in User,
|
||||
where: u.email == ^email
|
||||
)
|
||||
|
||||
query
|
||||
|> Repo.one
|
||||
|> Repo.one()
|
||||
|> Argon2.check_pass(password)
|
||||
end
|
||||
end
|
||||
|
||||
@ -12,6 +12,7 @@ defmodule Chess.Auth.ErrorHandler do
|
||||
|> put_flash(:info, "You must be logged in")
|
||||
|> redirect(to: "/")
|
||||
|> halt()
|
||||
|
||||
"json" ->
|
||||
conn
|
||||
|> put_status(403)
|
||||
|
||||
@ -10,8 +10,9 @@ defmodule Chess.Auth.Guardian do
|
||||
end
|
||||
|
||||
def resource_from_claims(claims) do
|
||||
user = claims["sub"]
|
||||
|> Auth.get_user!
|
||||
user =
|
||||
claims["sub"]
|
||||
|> Auth.get_user!()
|
||||
|
||||
{:ok, user}
|
||||
end
|
||||
|
||||
@ -7,11 +7,11 @@ defmodule Chess.Auth.Pipeline do
|
||||
module: Chess.Auth.Guardian
|
||||
|
||||
# If there is a session token, validate it
|
||||
plug Guardian.Plug.VerifySession, claims: %{"typ" => "access"}
|
||||
plug(Guardian.Plug.VerifySession, claims: %{"typ" => "access"})
|
||||
|
||||
# If there is an authorization header, validate it
|
||||
plug Guardian.Plug.VerifyHeader, claims: %{"typ" => "access"}
|
||||
plug(Guardian.Plug.VerifyHeader, claims: %{"typ" => "access"})
|
||||
|
||||
# Load the user if either of the verifications worked
|
||||
plug Guardian.Plug.LoadResource, allow_blank: true
|
||||
plug(Guardian.Plug.LoadResource, allow_blank: true)
|
||||
end
|
||||
|
||||
@ -2,8 +2,8 @@ defmodule Chess.Board do
|
||||
@moduledoc false
|
||||
|
||||
def transform(board) do
|
||||
Enum.map(0..7, fn (rank) ->
|
||||
Enum.map(0..7, fn (file) ->
|
||||
Enum.map(0..7, fn rank ->
|
||||
Enum.map(0..7, fn file ->
|
||||
board
|
||||
|> piece({file, rank})
|
||||
end)
|
||||
@ -12,7 +12,7 @@ defmodule Chess.Board do
|
||||
|
||||
def search(board, %{"type" => type, "colour" => colour}) do
|
||||
board
|
||||
|> Enum.filter(fn({_index, piece}) ->
|
||||
|> Enum.filter(fn {_index, piece} ->
|
||||
match?(%{"type" => ^type, "colour" => ^colour}, piece)
|
||||
end)
|
||||
|> indexes_to_tuples
|
||||
@ -20,7 +20,7 @@ defmodule Chess.Board do
|
||||
|
||||
def search(board, %{"colour" => colour}) do
|
||||
board
|
||||
|> Enum.filter(fn({_index, piece}) ->
|
||||
|> Enum.filter(fn {_index, piece} ->
|
||||
match?(%{"colour" => ^colour}, piece)
|
||||
end)
|
||||
|> indexes_to_tuples
|
||||
@ -31,9 +31,9 @@ defmodule Chess.Board do
|
||||
end
|
||||
|
||||
def move_piece(board, %{
|
||||
"from" => [from_file, from_rank],
|
||||
"to" => [to_file, to_rank]
|
||||
}) do
|
||||
"from" => [from_file, from_rank],
|
||||
"to" => [to_file, to_rank]
|
||||
}) do
|
||||
{piece, board} = Map.pop(board, to_index({from_file, from_rank}))
|
||||
{piece_captured, board} = Map.pop(board, to_index({to_file, to_rank}))
|
||||
board = Map.put(board, to_index({to_file, to_rank}), piece)
|
||||
@ -42,8 +42,8 @@ defmodule Chess.Board do
|
||||
if castling_move?(piece, from_file, to_file) do
|
||||
board
|
||||
|> castling_move(%{
|
||||
"from" => [from_file, from_rank],
|
||||
"to" => [to_file, to_rank]
|
||||
from: {from_file, from_rank},
|
||||
to: {to_file, to_rank}
|
||||
})
|
||||
|> Map.get(:board)
|
||||
else
|
||||
@ -55,39 +55,40 @@ defmodule Chess.Board do
|
||||
to: %{"file" => to_file, "rank" => to_rank},
|
||||
board: board,
|
||||
piece: piece,
|
||||
piece_captured: piece_captured,
|
||||
piece_captured: piece_captured
|
||||
}
|
||||
end
|
||||
|
||||
def castling_move?(%{"type" => "king"}, 4, to_file) do
|
||||
to_file == 2 || to_file == 6
|
||||
end
|
||||
|
||||
def castling_move?(_, _, _), do: false
|
||||
|
||||
def castling_move(board, %{"from" => [4, rank], "to" => [2, _rank]}) do
|
||||
def castling_move(board, %{from: {4, rank}, to: {2, _rank}}) do
|
||||
move_piece(board, %{
|
||||
"from" => [0, rank],
|
||||
"to" => [3, rank],
|
||||
"to" => [3, rank]
|
||||
})
|
||||
end
|
||||
|
||||
def castling_move(board, %{"from" => [4, rank], "to" => [6, _rank]}) do
|
||||
move_piece(board, %{
|
||||
"from" => [7, rank],
|
||||
"to" => [5, rank],
|
||||
"to" => [5, rank]
|
||||
})
|
||||
end
|
||||
|
||||
def default do
|
||||
%{
|
||||
"0,7" => %{"type" => "rook", "colour" => "black"},
|
||||
"0,7" => %{"type" => "rook", "colour" => "black"},
|
||||
"1,7" => %{"type" => "knight", "colour" => "black"},
|
||||
"2,7" => %{"type" => "bishop", "colour" => "black"},
|
||||
"3,7" => %{"type" => "queen", "colour" => "black"},
|
||||
"4,7" => %{"type" => "king", "colour" => "black"},
|
||||
"3,7" => %{"type" => "queen", "colour" => "black"},
|
||||
"4,7" => %{"type" => "king", "colour" => "black"},
|
||||
"5,7" => %{"type" => "bishop", "colour" => "black"},
|
||||
"6,7" => %{"type" => "knight", "colour" => "black"},
|
||||
"7,7" => %{"type" => "rook", "colour" => "black"},
|
||||
|
||||
"7,7" => %{"type" => "rook", "colour" => "black"},
|
||||
"0,6" => %{"type" => "pawn", "colour" => "black"},
|
||||
"1,6" => %{"type" => "pawn", "colour" => "black"},
|
||||
"2,6" => %{"type" => "pawn", "colour" => "black"},
|
||||
@ -96,7 +97,6 @@ defmodule Chess.Board do
|
||||
"5,6" => %{"type" => "pawn", "colour" => "black"},
|
||||
"6,6" => %{"type" => "pawn", "colour" => "black"},
|
||||
"7,6" => %{"type" => "pawn", "colour" => "black"},
|
||||
|
||||
"0,1" => %{"type" => "pawn", "colour" => "white"},
|
||||
"1,1" => %{"type" => "pawn", "colour" => "white"},
|
||||
"2,1" => %{"type" => "pawn", "colour" => "white"},
|
||||
@ -105,15 +105,14 @@ defmodule Chess.Board do
|
||||
"5,1" => %{"type" => "pawn", "colour" => "white"},
|
||||
"6,1" => %{"type" => "pawn", "colour" => "white"},
|
||||
"7,1" => %{"type" => "pawn", "colour" => "white"},
|
||||
|
||||
"0,0" => %{"type" => "rook", "colour" => "white"},
|
||||
"0,0" => %{"type" => "rook", "colour" => "white"},
|
||||
"1,0" => %{"type" => "knight", "colour" => "white"},
|
||||
"2,0" => %{"type" => "bishop", "colour" => "white"},
|
||||
"3,0" => %{"type" => "queen", "colour" => "white"},
|
||||
"4,0" => %{"type" => "king", "colour" => "white"},
|
||||
"3,0" => %{"type" => "queen", "colour" => "white"},
|
||||
"4,0" => %{"type" => "king", "colour" => "white"},
|
||||
"5,0" => %{"type" => "bishop", "colour" => "white"},
|
||||
"6,0" => %{"type" => "knight", "colour" => "white"},
|
||||
"7,0" => %{"type" => "rook", "colour" => "white"}
|
||||
"7,0" => %{"type" => "rook", "colour" => "white"}
|
||||
}
|
||||
end
|
||||
|
||||
@ -123,13 +122,13 @@ defmodule Chess.Board do
|
||||
|
||||
defp indexes_to_tuples(list) do
|
||||
list
|
||||
|> Enum.map(fn({index, _piece}) -> index_to_tuple(index) end)
|
||||
|> Enum.map(fn {index, _piece} -> index_to_tuple(index) end)
|
||||
end
|
||||
|
||||
defp index_to_tuple(index) do
|
||||
index
|
||||
|> String.split(",")
|
||||
|> Enum.map(&(String.to_integer(&1)))
|
||||
|> List.to_tuple
|
||||
|> Enum.map(&String.to_integer(&1))
|
||||
|> List.to_tuple()
|
||||
end
|
||||
end
|
||||
|
||||
@ -12,9 +12,7 @@ defmodule Chess.Emails do
|
||||
new_email()
|
||||
|> to(game.opponent)
|
||||
|> from({"64squares", "games@64squares.club"})
|
||||
|> subject(
|
||||
"[64squares] #{game.user.name} has invited you to play a game of chess."
|
||||
)
|
||||
|> subject("[64squares] #{game.user.name} has invited you to play a game of chess.")
|
||||
|> text_body("""
|
||||
Game link: #{Helpers.game_url(conn, :show, game)}
|
||||
""")
|
||||
@ -27,9 +25,7 @@ defmodule Chess.Emails do
|
||||
new_email()
|
||||
|> to(opponent)
|
||||
|> from({"64squares", "games@64squares.club"})
|
||||
|> subject(
|
||||
"[64squares] #{user.name} has moved."
|
||||
)
|
||||
|> subject("[64squares] #{user.name} has moved.")
|
||||
|> text_body("""
|
||||
Game link: #{Helpers.game_url(socket, :show, game)}
|
||||
""")
|
||||
|
||||
@ -14,11 +14,15 @@ defmodule Chess.GameState do
|
||||
cond do
|
||||
player_checkmated?(board, colour) ->
|
||||
"checkmate"
|
||||
|
||||
player_stalemated?(board, colour) ->
|
||||
"stalemate"
|
||||
|
||||
king_in_check?(board, colour) ->
|
||||
"check"
|
||||
true -> nil
|
||||
|
||||
true ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
@ -36,7 +40,7 @@ defmodule Chess.GameState do
|
||||
king =
|
||||
board
|
||||
|> Board.search(%{"type" => "king", "colour" => colour})
|
||||
|> List.first
|
||||
|> List.first()
|
||||
|
||||
if is_nil(king) do
|
||||
raise "There is no #{colour} king!"
|
||||
@ -49,7 +53,7 @@ defmodule Chess.GameState do
|
||||
def player_cannot_move?(board, colour) do
|
||||
board
|
||||
|> Board.search(%{"colour" => colour})
|
||||
|> Enum.all?(fn({file, rank}) ->
|
||||
|> Enum.all?(fn {file, rank} ->
|
||||
board
|
||||
|> piece_cannot_move?({file, rank})
|
||||
end)
|
||||
@ -62,9 +66,9 @@ defmodule Chess.GameState do
|
||||
|
||||
board
|
||||
|> Moves.available({file, rank})
|
||||
|> Enum.all?(fn({to_file, to_rank}) ->
|
||||
|> Enum.all?(fn {to_file, to_rank} ->
|
||||
board
|
||||
|> Board.move_piece(%{"from" => [file, rank], "to" => [to_file, to_rank]})
|
||||
|> Board.move_piece(%{from: {file, rank}, to: {to_file, to_rank}})
|
||||
|> Map.get(:board)
|
||||
|> king_in_check?(piece["colour"])
|
||||
end)
|
||||
|
||||
@ -5,7 +5,7 @@ defmodule Chess.MoveList do
|
||||
|
||||
def transform(moves) do
|
||||
moves
|
||||
|> Enum.map(&(Move.transform(&1)))
|
||||
|> Enum.map(&Move.transform(&1))
|
||||
|> Enum.chunk_every(2)
|
||||
end
|
||||
end
|
||||
|
||||
@ -19,10 +19,10 @@ defmodule Chess.Moves do
|
||||
game.board
|
||||
|> Board.move_piece(move_params)
|
||||
|
||||
Multi.new
|
||||
Multi.new()
|
||||
|> Multi.update(:game, Game.move_changeset(game, params))
|
||||
|> Multi.insert(:move, Ecto.build_assoc(game, :moves, params))
|
||||
|> Repo.transaction
|
||||
|> Repo.transaction()
|
||||
end
|
||||
|
||||
def available(board, {file, rank}, move_list \\ []) do
|
||||
@ -33,14 +33,19 @@ defmodule Chess.Moves do
|
||||
case piece do
|
||||
%{"type" => "pawn"} ->
|
||||
Pawn.moves(board, {file, rank})
|
||||
|
||||
%{"type" => "rook"} ->
|
||||
Rook.moves(board, {file, rank})
|
||||
|
||||
%{"type" => "bishop"} ->
|
||||
Bishop.moves(board, {file, rank})
|
||||
|
||||
%{"type" => "knight"} ->
|
||||
Knight.moves(board, {file, rank})
|
||||
|
||||
%{"type" => "king"} ->
|
||||
King.moves(board, {file, rank}, move_list)
|
||||
|
||||
%{"type" => "queen"} ->
|
||||
Queen.moves(board, {file, rank})
|
||||
end
|
||||
|
||||
@ -22,13 +22,17 @@ defmodule Chess.Moves.Generator do
|
||||
defp _moves(_colour, _board, {_file, 0}, {_, -1}), do: []
|
||||
defp _moves(_colour, _board, {7, _rank}, {1, _}), do: []
|
||||
defp _moves(_colour, _board, {_file, 7}, {_, 1}), do: []
|
||||
|
||||
defp _moves(colour, board, {file, rank}, {fv, rv}) do
|
||||
next_square = {file + fv, rank + rv}
|
||||
|
||||
cond do
|
||||
can_capture_piece?(colour, board, next_square) ->
|
||||
[next_square]
|
||||
|
||||
obstruction?(colour, board, next_square) ->
|
||||
[]
|
||||
|
||||
true ->
|
||||
[next_square | _moves(colour, board, next_square, {fv, rv})]
|
||||
end
|
||||
@ -36,14 +40,18 @@ defmodule Chess.Moves.Generator do
|
||||
|
||||
# Move generation for pieces that follow a pattern
|
||||
defp _moves(_colour, _board, {_file, _rank}, []), do: []
|
||||
|
||||
defp _moves(colour, board, {file, rank}, [{fv, rv} | moves]) do
|
||||
move_square = {file + fv, rank + rv}
|
||||
|
||||
cond do
|
||||
outside_board?(move_square) ||
|
||||
obstruction?(colour, board, move_square) ->
|
||||
obstruction?(colour, board, move_square) ->
|
||||
_moves(colour, board, {file, rank}, moves)
|
||||
|
||||
can_capture_piece?(colour, board, move_square) ->
|
||||
[move_square | _moves(colour, board, {file, rank}, moves)]
|
||||
|
||||
true ->
|
||||
[move_square | _moves(colour, board, {file, rank}, moves)]
|
||||
end
|
||||
@ -66,6 +74,7 @@ defmodule Chess.Moves.Generator do
|
||||
piece =
|
||||
board
|
||||
|> Board.piece({file, rank})
|
||||
|
||||
piece && piece["colour"] == colour
|
||||
end
|
||||
end
|
||||
|
||||
@ -28,7 +28,7 @@ defmodule Chess.Moves.Piece do
|
||||
end
|
||||
|
||||
defp attacked_by_knight?(board, {file, rank}) do
|
||||
_attacked?(board, {file, rank}, Knight.pattern, "knight")
|
||||
_attacked?(board, {file, rank}, Knight.pattern(), "knight")
|
||||
end
|
||||
|
||||
defp attacked_by_pawn?(board, {file, rank}) do
|
||||
@ -46,22 +46,24 @@ defmodule Chess.Moves.Piece do
|
||||
board
|
||||
|> Generator.moves({file, rank}, pattern)
|
||||
|
||||
Enum.any?(moves, &(match_piece(board, &1, "pawn")))
|
||||
Enum.any?(moves, &match_piece(board, &1, "pawn"))
|
||||
end
|
||||
|
||||
defp _attacked?(_board, {0, _rank}, {-1, _}, _), do: false
|
||||
defp _attacked?(_board, {_file, 0}, {_, -1}, _), do: false
|
||||
defp _attacked?(_board, {7, _rank}, {1, _}, _), do: false
|
||||
defp _attacked?(_board, {_file, 7}, {_, 1}, _), do: false
|
||||
|
||||
defp _attacked?(board, {file, rank}, {fv, rv}, pieces) do
|
||||
board
|
||||
|> Generator.moves({file, rank}, {fv, rv})
|
||||
|> List.last
|
||||
|> List.last()
|
||||
|> case do
|
||||
{file, rank} ->
|
||||
piece = board["#{file},#{rank}"]
|
||||
|
||||
Enum.any?(pieces, &(match?(%{"type" => ^&1}, piece)))
|
||||
Enum.any?(pieces, &match?(%{"type" => ^&1}, piece))
|
||||
|
||||
nil ->
|
||||
false
|
||||
end
|
||||
@ -72,7 +74,7 @@ defmodule Chess.Moves.Piece do
|
||||
board
|
||||
|> Generator.moves({file, rank}, pattern)
|
||||
|
||||
Enum.any?(moves, &(match_piece(board, &1, piece_type)))
|
||||
Enum.any?(moves, &match_piece(board, &1, piece_type))
|
||||
end
|
||||
|
||||
defp match_piece(board, {file, rank}, piece_type) do
|
||||
|
||||
@ -18,12 +18,13 @@ defmodule Chess.Moves.Pieces.King.Castling do
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
def moves(_board, _piece, _move_list), do: []
|
||||
|
||||
def _moves(board, _rank, colour, move_list) do
|
||||
board
|
||||
|> Board.search(%{"type" => "rook", "colour" => colour})
|
||||
|> Enum.map(fn ({file, rank}) ->
|
||||
|> Enum.map(fn {file, rank} ->
|
||||
case file do
|
||||
0 -> queen_side_move(board, rank, colour, move_list)
|
||||
7 -> king_side_move(board, rank, colour, move_list)
|
||||
@ -35,44 +36,50 @@ defmodule Chess.Moves.Pieces.King.Castling do
|
||||
|
||||
defp king_has_moved?(move_list, colour) do
|
||||
move_list
|
||||
|> Enum.any?(fn (move) ->
|
||||
match?(%Move{
|
||||
piece: %{"type" => "king", "colour" => ^colour}
|
||||
}, move)
|
||||
|> Enum.any?(fn move ->
|
||||
match?(
|
||||
%Move{
|
||||
piece: %{"type" => "king", "colour" => ^colour}
|
||||
},
|
||||
move
|
||||
)
|
||||
end)
|
||||
end
|
||||
|
||||
defp queen_side_move(board, rank, colour, move_list) do
|
||||
if queen_side_squares_empty?(board, rank) &&
|
||||
!queen_side_in_check?(board, rank, colour) &&
|
||||
!rook_has_moved?(0, move_list, colour) do
|
||||
!queen_side_in_check?(board, rank, colour) &&
|
||||
!rook_has_moved?(0, move_list, colour) do
|
||||
{2, rank}
|
||||
end
|
||||
end
|
||||
|
||||
defp king_side_move(board, rank, colour, move_list) do
|
||||
if king_side_squares_empty?(board, rank) &&
|
||||
!king_side_in_check?(board, rank, colour) &&
|
||||
!rook_has_moved?(7, move_list, colour) do
|
||||
!king_side_in_check?(board, rank, colour) &&
|
||||
!rook_has_moved?(7, move_list, colour) do
|
||||
{6, rank}
|
||||
end
|
||||
end
|
||||
|
||||
defp rook_has_moved?(file, move_list, colour) do
|
||||
move_list
|
||||
|> Enum.any?(fn (move) ->
|
||||
match?(%Move{
|
||||
piece: %{"type" => "rook", "colour" => ^colour},
|
||||
from: %{"file" => ^file},
|
||||
}, move)
|
||||
|> Enum.any?(fn move ->
|
||||
match?(
|
||||
%Move{
|
||||
piece: %{"type" => "rook", "colour" => ^colour},
|
||||
from: %{"file" => ^file}
|
||||
},
|
||||
move
|
||||
)
|
||||
end)
|
||||
end
|
||||
|
||||
defp queen_side_in_check?(board, rank, colour) do
|
||||
[{2, rank}, {3, rank}]
|
||||
|> Enum.any?(fn ({to_file, to_rank}) ->
|
||||
|> Enum.any?(fn {to_file, to_rank} ->
|
||||
board
|
||||
|> Board.move_piece(%{"from" => [4, rank], "to" => [to_file, to_rank]})
|
||||
|> Board.move_piece(%{from: {4, rank}, to: {to_file, to_rank}})
|
||||
|> Map.get(:board)
|
||||
|> GameState.king_in_check?(colour)
|
||||
end)
|
||||
@ -80,9 +87,9 @@ defmodule Chess.Moves.Pieces.King.Castling do
|
||||
|
||||
defp king_side_in_check?(board, rank, colour) do
|
||||
[{5, rank}, {6, rank}]
|
||||
|> Enum.any?(fn ({to_file, to_rank}) ->
|
||||
|> Enum.any?(fn {to_file, to_rank} ->
|
||||
board
|
||||
|> Board.move_piece(%{"from" => [4, rank], "to" => [to_file, to_rank]})
|
||||
|> Board.move_piece(%{from: {4, rank}, to: {to_file, to_rank}})
|
||||
|> Map.get(:board)
|
||||
|> GameState.king_in_check?(colour)
|
||||
end)
|
||||
|
||||
@ -37,8 +37,10 @@ defmodule Chess.Moves.Pieces.Pawn do
|
||||
cond do
|
||||
obstruction?(board, {file, rank + 1}) ->
|
||||
[]
|
||||
|
||||
rank == 1 ->
|
||||
[{file, rank + 1} | _moves("white", board, {file, rank + 1})]
|
||||
|
||||
true ->
|
||||
[{file, rank + 1}]
|
||||
end
|
||||
@ -48,16 +50,20 @@ defmodule Chess.Moves.Pieces.Pawn do
|
||||
cond do
|
||||
obstruction?(board, {file, rank - 1}) ->
|
||||
[]
|
||||
|
||||
rank == 6 ->
|
||||
[{file, rank - 1} | _moves("black", board, {file, rank - 1})]
|
||||
|
||||
true ->
|
||||
[{file, rank - 1}]
|
||||
end
|
||||
end
|
||||
|
||||
def _capture_moves(_colour, _board, {_file, _rank}, []), do: []
|
||||
|
||||
def _capture_moves(colour, board, {file, rank}, [{fv, rv} | moves]) do
|
||||
move_square = {file + fv, rank + rv}
|
||||
|
||||
if can_capture_piece?(colour, board, move_square) do
|
||||
[move_square | _capture_moves(colour, board, {file, rank}, moves)]
|
||||
else
|
||||
|
||||
28
lib/chess/release.ex
Normal file
28
lib/chess/release.ex
Normal file
@ -0,0 +1,28 @@
|
||||
defmodule Chess.Release do
|
||||
@moduledoc """
|
||||
Used for executing DB release tasks when run in production without Mix
|
||||
installed.
|
||||
"""
|
||||
@app :chess
|
||||
|
||||
def migrate do
|
||||
load_app()
|
||||
|
||||
for repo <- repos() do
|
||||
{:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :up, all: true))
|
||||
end
|
||||
end
|
||||
|
||||
def rollback(repo, version) do
|
||||
load_app()
|
||||
{:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :down, to: version))
|
||||
end
|
||||
|
||||
defp repos do
|
||||
Application.fetch_env!(@app, :ecto_repos)
|
||||
end
|
||||
|
||||
defp load_app do
|
||||
Application.load(@app)
|
||||
end
|
||||
end
|
||||
@ -23,7 +23,7 @@ defmodule Chess.Repo.Queries do
|
||||
|
||||
def opponents(user, query_string) do
|
||||
user
|
||||
|> User.opponents
|
||||
|> User.opponents()
|
||||
|> User.matches(query_string)
|
||||
end
|
||||
end
|
||||
|
||||
@ -14,14 +14,14 @@ defmodule Chess.Store.Game do
|
||||
alias Chess.Store.User
|
||||
|
||||
schema "games" do
|
||||
field :board, :map, default: Board.default()
|
||||
field :turn, :string, default: "white"
|
||||
field :state, :string
|
||||
field(:board, :map, default: Board.default())
|
||||
field(:turn, :string, default: "white")
|
||||
field(:state, :string)
|
||||
|
||||
belongs_to :user, User
|
||||
belongs_to :opponent, User, references: :id
|
||||
belongs_to(:user, User)
|
||||
belongs_to(:opponent, User, references: :id)
|
||||
|
||||
has_many :moves, Move
|
||||
has_many(:moves, Move)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
@ -55,15 +55,17 @@ defmodule Chess.Store.Game do
|
||||
end
|
||||
|
||||
def for_user_id(user_id) do
|
||||
from game in Game,
|
||||
from(game in Game,
|
||||
where: game.user_id == ^user_id,
|
||||
or_where: game.opponent_id == ^user_id
|
||||
)
|
||||
end
|
||||
|
||||
def check_game_state(changeset) do
|
||||
changeset
|
||||
|> put_change(
|
||||
:state, GameState.state(changeset.changes.board, changeset.changes.turn)
|
||||
:state,
|
||||
GameState.state(changeset.changes.board, changeset.changes.turn)
|
||||
)
|
||||
end
|
||||
|
||||
@ -78,6 +80,7 @@ defmodule Chess.Store.Game do
|
||||
changeset
|
||||
end
|
||||
end
|
||||
|
||||
def validate_king_in_check(changeset, _, _), do: changeset
|
||||
|
||||
def ordered(query) do
|
||||
|
||||
@ -8,13 +8,13 @@ defmodule Chess.Store.Move do
|
||||
alias Chess.Store.Game
|
||||
|
||||
schema "moves" do
|
||||
field :from, :map
|
||||
field :to, :map
|
||||
field(:from, :map)
|
||||
field(:to, :map)
|
||||
|
||||
field :piece, :map
|
||||
field :piece_captured, :map
|
||||
field(:piece, :map)
|
||||
field(:piece_captured, :map)
|
||||
|
||||
belongs_to :game, Game
|
||||
belongs_to(:game, Game)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
@ -31,7 +31,7 @@ defmodule Chess.Store.Move do
|
||||
piece: move.piece,
|
||||
piece_captured: move.piece_captured,
|
||||
from: <<97 + move.from["file"], 49 + move.from["rank"]>>,
|
||||
to: <<97 + move.to["file"], 49 + move.to["rank"]>>,
|
||||
to: <<97 + move.to["file"], 49 + move.to["rank"]>>
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
@ -6,16 +6,14 @@ defmodule Chess.Store.User do
|
||||
import Ecto.Changeset
|
||||
import Ecto.Query
|
||||
|
||||
alias Comeonin.Argon2
|
||||
|
||||
schema "users" do
|
||||
field :name, :string
|
||||
field :email, :string
|
||||
field :password, :string, virtual: true
|
||||
field :password_hash, :string
|
||||
field(:name, :string)
|
||||
field(:email, :string)
|
||||
field(:password, :string, virtual: true)
|
||||
field(:password_hash, :string)
|
||||
|
||||
has_many :games, Chess.Store.Game
|
||||
has_many :games_as_opponent, Chess.Store.Game, foreign_key: :opponent_id
|
||||
has_many(:games, Chess.Store.Game)
|
||||
has_many(:games_as_opponent, Chess.Store.Game, foreign_key: :opponent_id)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
@ -53,26 +51,32 @@ defmodule Chess.Store.User do
|
||||
end
|
||||
|
||||
def find_by_name(name) do
|
||||
from user in __MODULE__,
|
||||
from(user in __MODULE__,
|
||||
where: user.name == ^name
|
||||
)
|
||||
end
|
||||
|
||||
def opponents(user) do
|
||||
from user in __MODULE__,
|
||||
from(user in __MODULE__,
|
||||
where: user.id != ^user.id
|
||||
)
|
||||
end
|
||||
|
||||
def matches(query, query_string) do
|
||||
from user in query,
|
||||
where: ilike(user.name, ^"%#{query_string}%")
|
||||
or user.email == ^query_string
|
||||
from(user in query,
|
||||
where:
|
||||
ilike(user.name, ^"%#{query_string}%") or
|
||||
user.email == ^query_string
|
||||
)
|
||||
end
|
||||
|
||||
defp hash_password(changeset) do
|
||||
password = get_change(changeset, :password)
|
||||
|
||||
if password do
|
||||
changeset
|
||||
|> change(Argon2.add_hash(password))
|
||||
|> change(password: nil)
|
||||
else
|
||||
changeset
|
||||
end
|
||||
|
||||
@ -19,6 +19,7 @@ defmodule ChessWeb do
|
||||
def controller do
|
||||
quote do
|
||||
use Phoenix.Controller, namespace: ChessWeb
|
||||
import Phoenix.LiveView.Controller
|
||||
|
||||
alias Chess.Repo
|
||||
import Ecto
|
||||
@ -31,12 +32,14 @@ defmodule ChessWeb do
|
||||
|
||||
def view do
|
||||
quote do
|
||||
use Phoenix.View, root: "lib/chess_web/templates",
|
||||
namespace: ChessWeb
|
||||
use Phoenix.View,
|
||||
root: "lib/chess_web/templates",
|
||||
namespace: ChessWeb
|
||||
|
||||
# Import convenience functions from controllers
|
||||
import Phoenix.Controller,
|
||||
only: [get_csrf_token: 0, get_flash: 2, view_module: 1]
|
||||
import Phoenix.LiveView.Helpers
|
||||
|
||||
# Use all HTML functionality (forms, tags, etc)
|
||||
use Phoenix.HTML
|
||||
@ -49,9 +52,45 @@ defmodule ChessWeb do
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def live_view do
|
||||
quote do
|
||||
use Phoenix.LiveView,
|
||||
layout: {ChessWeb.LayoutView, "live.html"}
|
||||
|
||||
unquote(view_helpers())
|
||||
end
|
||||
end
|
||||
|
||||
def live_component do
|
||||
quote do
|
||||
use Phoenix.LiveComponent
|
||||
|
||||
unquote(view_helpers())
|
||||
end
|
||||
end
|
||||
|
||||
defp view_helpers do
|
||||
quote do
|
||||
# Use all HTML functionality (forms, tags, etc)
|
||||
use Phoenix.HTML
|
||||
|
||||
# Import LiveView helpers (live_render, live_component, live_patch, etc)
|
||||
import Phoenix.LiveView.Helpers
|
||||
|
||||
# Import basic rendering functionality (render, render_layout, etc)
|
||||
import Phoenix.View
|
||||
|
||||
import ChessWeb.ErrorHelpers
|
||||
import ChessWeb.Gettext
|
||||
alias ChessWeb.Router.Helpers, as: Routes
|
||||
end
|
||||
end
|
||||
|
||||
def router do
|
||||
quote do
|
||||
use Phoenix.Router
|
||||
import Phoenix.LiveView.Router
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -32,7 +32,7 @@ defmodule ChessWeb.GameChannel do
|
||||
board: Board.transform(game.board),
|
||||
turn: game.turn,
|
||||
state: game.state,
|
||||
moves: MoveList.transform(game.moves),
|
||||
moves: MoveList.transform(game.moves)
|
||||
}
|
||||
|
||||
socket
|
||||
@ -57,6 +57,7 @@ defmodule ChessWeb.GameChannel do
|
||||
update_opponent(socket, game)
|
||||
|
||||
{:noreply, socket}
|
||||
|
||||
{:error, :game, changeset, _} ->
|
||||
{message, _} = changeset.errors[:board]
|
||||
|
||||
@ -65,21 +66,26 @@ defmodule ChessWeb.GameChannel do
|
||||
end
|
||||
|
||||
def handle_in(
|
||||
"game:get_available_moves",
|
||||
%{"square" => [file, rank]},
|
||||
socket
|
||||
) do
|
||||
"game:get_available_moves",
|
||||
%{"square" => [file, rank]},
|
||||
socket
|
||||
) do
|
||||
game =
|
||||
socket.assigns.user_id
|
||||
|> Queries.game_with_moves(socket.assigns.game_id)
|
||||
|
||||
moves = Moves.available(game.board, {
|
||||
String.to_integer(file),
|
||||
String.to_integer(rank)
|
||||
}, game.moves)
|
||||
moves =
|
||||
Moves.available(
|
||||
game.board,
|
||||
{
|
||||
String.to_integer(file),
|
||||
String.to_integer(rank)
|
||||
},
|
||||
game.moves
|
||||
)
|
||||
|
||||
reply = %{
|
||||
moves: Enum.map(moves, &(Tuple.to_list(&1)))
|
||||
moves: Enum.map(moves, &Tuple.to_list(&1))
|
||||
}
|
||||
|
||||
{:reply, {:ok, reply}, socket}
|
||||
@ -88,27 +94,29 @@ defmodule ChessWeb.GameChannel do
|
||||
def update_opponent(socket, game) do
|
||||
opponent_id =
|
||||
opponent(game, socket.assigns.user_id).id
|
||||
|> Integer.to_string
|
||||
|> Integer.to_string()
|
||||
|
||||
send_update(socket)
|
||||
|
||||
"game:#{game.id}"
|
||||
|> Presence.list
|
||||
|> Presence.list()
|
||||
|> case do
|
||||
%{^opponent_id => _} ->
|
||||
nil
|
||||
|
||||
_ ->
|
||||
socket
|
||||
|> Emails.opponent_moved_email(game)
|
||||
|> Mailer.deliver_later
|
||||
|> Mailer.deliver_later()
|
||||
end
|
||||
end
|
||||
|
||||
def track_presence(socket) do
|
||||
{:ok, _} = Presence.track(socket, socket.assigns.user_id, %{
|
||||
user_id: socket.assigns.user_id,
|
||||
online_at: inspect(System.system_time(:second))
|
||||
})
|
||||
{:ok, _} =
|
||||
Presence.track(socket, socket.assigns.user_id, %{
|
||||
user_id: socket.assigns.user_id,
|
||||
online_at: inspect(System.system_time(:second))
|
||||
})
|
||||
|
||||
socket
|
||||
|> push("presence_state", Presence.list(socket))
|
||||
@ -116,8 +124,8 @@ defmodule ChessWeb.GameChannel do
|
||||
|
||||
def convert_params(%{"from" => from, "to" => to}) do
|
||||
%{
|
||||
"from" => Enum.map(from, &(String.to_integer(&1))),
|
||||
"to" => Enum.map(to, &(String.to_integer(&1))),
|
||||
"from" => Enum.map(from, &String.to_integer(&1)),
|
||||
"to" => Enum.map(to, &String.to_integer(&1))
|
||||
}
|
||||
end
|
||||
|
||||
@ -130,7 +138,7 @@ defmodule ChessWeb.GameChannel do
|
||||
board: Board.transform(game.board),
|
||||
turn: game.turn,
|
||||
state: game.state,
|
||||
moves: MoveList.transform(game.moves),
|
||||
moves: MoveList.transform(game.moves)
|
||||
}
|
||||
|
||||
ChessWeb.Endpoint.broadcast("game:#{game.id}", "game:update", payload)
|
||||
|
||||
@ -68,6 +68,7 @@ defmodule ChessWeb.Presence do
|
||||
information, while maintaining the required `:metas` field from the
|
||||
original presence data.
|
||||
"""
|
||||
use Phoenix.Presence, otp_app: :chess,
|
||||
pubsub_server: Chess.PubSub
|
||||
use Phoenix.Presence,
|
||||
otp_app: :chess,
|
||||
pubsub_server: Chess.PubSub
|
||||
end
|
||||
|
||||
@ -4,7 +4,7 @@ defmodule ChessWeb.UserSocket do
|
||||
alias Phoenix.Token
|
||||
|
||||
## Channels
|
||||
channel "game:*", ChessWeb.GameChannel
|
||||
channel("game:*", ChessWeb.GameChannel)
|
||||
|
||||
# Socket params are passed from the client and can
|
||||
# be used to verify and authenticate a user. After
|
||||
@ -21,10 +21,12 @@ defmodule ChessWeb.UserSocket do
|
||||
case Token.verify(socket, "game socket", token, max_age: 1_209_600) do
|
||||
{:ok, user_id} ->
|
||||
{:ok, assign(socket, :user_id, user_id)}
|
||||
|
||||
{:error, _reason} ->
|
||||
:error
|
||||
end
|
||||
end
|
||||
|
||||
def connect(%{}, _socket), do: :error
|
||||
|
||||
# Socket id's are topics that allow you to identify all sockets for a given user:
|
||||
|
||||
@ -10,8 +10,8 @@ defmodule ChessWeb.Api.OpponentsController do
|
||||
conn
|
||||
|> current_user()
|
||||
|> Queries.opponents(query_string)
|
||||
|> Repo.all
|
||||
|> Repo.all()
|
||||
|
||||
render conn, "index.json", %{opponents: opponents}
|
||||
render(conn, "index.json", %{opponents: opponents})
|
||||
end
|
||||
end
|
||||
|
||||
@ -15,9 +15,9 @@ defmodule ChessWeb.GameController do
|
||||
conn
|
||||
|> current_user()
|
||||
|> Game.for_user()
|
||||
|> Game.ordered
|
||||
|> Game.ordered()
|
||||
|> preload([:user, :opponent])
|
||||
|> Repo.all
|
||||
|> Repo.all()
|
||||
|
||||
conn
|
||||
|> render("index.html", games: games, changeset: changeset)
|
||||
@ -42,7 +42,7 @@ defmodule ChessWeb.GameController do
|
||||
|> Repo.preload(:user)
|
||||
|> Repo.preload(:opponent)
|
||||
)
|
||||
|> Mailer.deliver_later
|
||||
|> Mailer.deliver_later()
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "Game created successfully.")
|
||||
@ -53,7 +53,7 @@ defmodule ChessWeb.GameController do
|
||||
conn
|
||||
|> current_user()
|
||||
|> User.opponents()
|
||||
|> Repo.all
|
||||
|> Repo.all()
|
||||
|
||||
conn
|
||||
|> render("new.html", changeset: changeset, opponents: opponents)
|
||||
|
||||
@ -22,6 +22,7 @@ defmodule ChessWeb.PasswordController do
|
||||
conn
|
||||
|> put_flash(:info, gettext("Password updated successfully."))
|
||||
|> redirect(to: page_path(conn, :index))
|
||||
|
||||
{:error, changeset} ->
|
||||
render(conn, "edit.html", changeset: changeset)
|
||||
end
|
||||
|
||||
@ -22,6 +22,7 @@ defmodule ChessWeb.ProfileController do
|
||||
conn
|
||||
|> put_flash(:info, gettext("Profile updated successfully."))
|
||||
|> redirect(to: page_path(conn, :index))
|
||||
|
||||
{:error, changeset} ->
|
||||
render(conn, "edit.html", changeset: changeset)
|
||||
end
|
||||
|
||||
@ -18,6 +18,7 @@ defmodule ChessWeb.RegistrationController do
|
||||
|> Guardian.Plug.sign_in(user)
|
||||
|> put_flash(:info, "Registered successfully.")
|
||||
|> redirect(to: page_path(conn, :index))
|
||||
|
||||
{:error, changeset} ->
|
||||
render(conn, "new.html", changeset: changeset)
|
||||
end
|
||||
|
||||
@ -11,17 +11,19 @@ defmodule ChessWeb.SessionController do
|
||||
end
|
||||
|
||||
def create(
|
||||
conn,
|
||||
%{"user" => %{"email" => email, "password" => password}}
|
||||
) do
|
||||
conn,
|
||||
%{"user" => %{"email" => email, "password" => password}}
|
||||
) do
|
||||
case Auth.authenticate_user(email, password) do
|
||||
{:ok, user} ->
|
||||
conn
|
||||
|> Guardian.Plug.sign_in(user)
|
||||
|> put_flash(:info, "You are logged in")
|
||||
|> redirect(to: game_path(conn, :index))
|
||||
|
||||
{:error, _error} ->
|
||||
changeset = User.changeset(%User{})
|
||||
|
||||
conn
|
||||
|> put_flash(:error, "Bad email or password")
|
||||
|> render("new.html", changeset: changeset)
|
||||
|
||||
@ -1,12 +1,20 @@
|
||||
defmodule ChessWeb.Endpoint do
|
||||
use Phoenix.Endpoint, otp_app: :chess
|
||||
|
||||
if sandbox = Application.get_env(:chess, :sandbox) do
|
||||
@session_options [
|
||||
store: :cookie,
|
||||
key: "_chess_key",
|
||||
signing_salt: "9LqUhZTU"
|
||||
]
|
||||
|
||||
if sandbox = Application.compile_env(:chess, :sandbox) do
|
||||
plug(Phoenix.Ecto.SQL.Sandbox, sandbox: sandbox)
|
||||
end
|
||||
|
||||
socket("/socket", ChessWeb.UserSocket)
|
||||
|
||||
socket("/live", Phoenix.LiveView.Socket, websocket: [connect_info: [session: @session_options]])
|
||||
|
||||
# Serve at "/" the static files from "priv/static" directory.
|
||||
#
|
||||
# You should set gzip to true if you are running phoenix.digest
|
||||
@ -45,11 +53,7 @@ defmodule ChessWeb.Endpoint do
|
||||
# The session will be stored in the cookie and signed,
|
||||
# this means its contents can be read but not tampered with.
|
||||
# Set :encryption_salt if you would also like to encrypt it.
|
||||
plug(Plug.Session,
|
||||
store: :cookie,
|
||||
key: "_chess_key",
|
||||
signing_salt: "9LqUhZTU"
|
||||
)
|
||||
plug(Plug.Session, @session_options)
|
||||
|
||||
plug(ChessWeb.Router)
|
||||
end
|
||||
|
||||
@ -4,57 +4,53 @@ defmodule ChessWeb.Router do
|
||||
alias Phoenix.Token
|
||||
|
||||
pipeline :browser do
|
||||
plug :accepts, ["html"]
|
||||
plug :fetch_session
|
||||
plug :fetch_flash
|
||||
plug :protect_from_forgery
|
||||
plug :put_secure_browser_headers
|
||||
plug(:accepts, ["html"])
|
||||
plug(:fetch_session)
|
||||
plug(:fetch_live_flash)
|
||||
plug(:protect_from_forgery)
|
||||
plug(:put_secure_browser_headers)
|
||||
end
|
||||
|
||||
pipeline :auth do
|
||||
plug Chess.Auth.Pipeline
|
||||
plug(Chess.Auth.Pipeline)
|
||||
end
|
||||
|
||||
pipeline :ensure_auth do
|
||||
plug Guardian.Plug.EnsureAuthenticated
|
||||
plug :put_user_token
|
||||
plug(Guardian.Plug.EnsureAuthenticated)
|
||||
plug(:put_user_token)
|
||||
end
|
||||
|
||||
pipeline :api do
|
||||
plug :fetch_session
|
||||
plug :accepts, ["json"]
|
||||
plug(:fetch_session)
|
||||
plug(:accepts, ["json"])
|
||||
end
|
||||
|
||||
scope "/", ChessWeb do
|
||||
pipe_through [:browser, :auth] # Use the default browser stack
|
||||
# Use the default browser stack
|
||||
pipe_through([:browser, :auth])
|
||||
|
||||
get "/", PageController, :index
|
||||
resources "/session", SessionController,
|
||||
only: [:new, :create, :delete], singleton: true
|
||||
resources "/registration", RegistrationController,
|
||||
only: [:new, :create], singleton: true
|
||||
get("/", PageController, :index)
|
||||
resources("/session", SessionController, only: [:new, :create, :delete], singleton: true)
|
||||
resources("/registration", RegistrationController, only: [:new, :create], singleton: true)
|
||||
end
|
||||
|
||||
scope "/", ChessWeb do
|
||||
pipe_through [:browser, :auth, :ensure_auth]
|
||||
pipe_through([:browser, :auth, :ensure_auth])
|
||||
|
||||
resources "/games", GameController,
|
||||
only: [:index, :new, :create, :show, :delete]
|
||||
resources "/profile", ProfileController,
|
||||
only: [:edit, :update], singleton: true
|
||||
resources "/password", PasswordController,
|
||||
only: [:edit, :update], singleton: true
|
||||
resources("/games", GameController, only: [:index, :new, :create, :show, :delete])
|
||||
resources("/profile", ProfileController, only: [:edit, :update], singleton: true)
|
||||
resources("/password", PasswordController, only: [:edit, :update], singleton: true)
|
||||
end
|
||||
|
||||
# Other scopes may use custom stacks.
|
||||
scope "/api", as: :api do
|
||||
pipe_through [:api, :auth, :ensure_auth]
|
||||
pipe_through([:api, :auth, :ensure_auth])
|
||||
|
||||
resources "/opponents", ChessWeb.Api.OpponentsController, only: [:index]
|
||||
resources("/opponents", ChessWeb.Api.OpponentsController, only: [:index])
|
||||
end
|
||||
|
||||
if Mix.env == :dev do
|
||||
forward "/sent_emails", Bamboo.SentEmailViewerPlug
|
||||
if Mix.env() == :dev do
|
||||
forward("/sent_emails", Bamboo.SentEmailViewerPlug)
|
||||
end
|
||||
|
||||
defp put_user_token(conn, _) do
|
||||
|
||||
43
lib/chess_web/templates/game/board.html.leex
Normal file
43
lib/chess_web/templates/game/board.html.leex
Normal file
@ -0,0 +1,43 @@
|
||||
<div class="board board--<%= @game.turn %>-to-move board--player-is-<%= player_colour(@user, @game) %>">
|
||||
<div class="board__rank-labels">
|
||||
<div class="board__label">1</div>
|
||||
<div class="board__label">2</div>
|
||||
<div class="board__label">3</div>
|
||||
<div class="board__label">4</div>
|
||||
<div class="board__label">5</div>
|
||||
<div class="board__label">6</div>
|
||||
<div class="board__label">7</div>
|
||||
<div class="board__label">8</div>
|
||||
</div>
|
||||
|
||||
<div class="board__file-labels">
|
||||
<div class="board__label">a</div>
|
||||
<div class="board__label">b</div>
|
||||
<div class="board__label">c</div>
|
||||
<div class="board__label">d</div>
|
||||
<div class="board__label">e</div>
|
||||
<div class="board__label">f</div>
|
||||
<div class="board__label">g</div>
|
||||
<div class="board__label">h</div>
|
||||
</div>
|
||||
|
||||
<div class="board__body">
|
||||
<%= for {rank, row} <- @board do %>
|
||||
<div class="board__row">
|
||||
<%= for {file, piece} <- row do %>
|
||||
<%= render ChessWeb.SquareView,
|
||||
"square.html",
|
||||
rank: rank,
|
||||
file: file,
|
||||
piece: piece,
|
||||
selected: {file, rank} == @selected,
|
||||
available: {file, rank} in @available %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="game-state game-state--<%= @game.state %>">
|
||||
<%= states[@game.state] %>
|
||||
</div>
|
||||
</div>
|
||||
6
lib/chess_web/templates/game/game_info.html.leex
Normal file
6
lib/chess_web/templates/game/game_info.html.leex
Normal file
@ -0,0 +1,6 @@
|
||||
<div class="game-info">
|
||||
<p>
|
||||
Playing <%= opponent(@game, @user.id).name %>
|
||||
<img class="game-info__opponent-status" src="/images/eye-closed.svg" alt="offline">
|
||||
</p>
|
||||
</div>
|
||||
@ -6,7 +6,7 @@
|
||||
<tr class="<%= turn_class(@conn, game) %>">
|
||||
<td>
|
||||
<span class="games-list__player-indicator">
|
||||
<img src="images/pawn_<%= player_colour(@conn, game) %>.svg">
|
||||
<img src="images/pawn_<%= player_colour(current_user(@conn), game) %>.svg">
|
||||
</span>
|
||||
<%= link gettext(
|
||||
"Game with %{name}",
|
||||
|
||||
@ -1,2 +1,33 @@
|
||||
<div id="game" data-game-id="<%= @game.id %>">
|
||||
<div data-game-id="<%= @game.id %>">
|
||||
<div class="game-grid">
|
||||
<%= live_render(
|
||||
@conn,
|
||||
ChessWeb.BoardLive,
|
||||
session: %{"user_id" => current_user(@conn).id, "game_id" => @game.id}
|
||||
) %>
|
||||
|
||||
<%= live_render(
|
||||
@conn,
|
||||
ChessWeb.GameInfoLive,
|
||||
session: %{"user_id" => current_user(@conn).id, "game_id" => @game.id}
|
||||
) %>
|
||||
|
||||
<div class="move-list">
|
||||
<table class="table table--condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="move-list__line-number"><span class="visually-hidden">Move no.</span></th>
|
||||
<th class="move-list__header--white">White</th>
|
||||
<th class="move-list__header--black">Black</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row" class="move-list__line-number">1.</th>
|
||||
<td class="move-list__move move-list__move--white">e4</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
<%= csrf_meta_tag() %>
|
||||
|
||||
<title><%= gettext "64squares" %></title>
|
||||
<link href="https://fonts.googleapis.com/css?family=Enriqueta:400,700" rel="stylesheet">
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<div class="form">
|
||||
<h2><%= gettext "Password" %></h2>
|
||||
|
||||
<%= form_for @changeset, password_path(@conn, :update), [class: "update-password"], fn f -> %>
|
||||
<%= form_for @changeset, password_path(@conn, :update), [class: "update-password", novalidate: true], fn f -> %>
|
||||
<%= if @changeset.action do %>
|
||||
<div class="alert alert-danger">
|
||||
<p>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<div class="form">
|
||||
<h2><%= gettext "Profile" %></h2>
|
||||
|
||||
<%= form_for @changeset, profile_path(@conn, :update), [class: "update-profile"], fn f -> %>
|
||||
<%= form_for @changeset, profile_path(@conn, :update), [class: "update-profile", novalidate: true], fn f -> %>
|
||||
<%= if @changeset.action do %>
|
||||
<div class="alert alert-danger">
|
||||
<p>
|
||||
|
||||
7
lib/chess_web/templates/square/square.html.leex
Normal file
7
lib/chess_web/templates/square/square.html.leex
Normal file
@ -0,0 +1,7 @@
|
||||
<div
|
||||
id="f<%= @file %>-r<%= @rank %>"
|
||||
phx-click="click"
|
||||
phx-value-rank="<%= @rank %>"
|
||||
phx-value-file="<%= @file %>"
|
||||
class="<%= classes(@file, @rank, @piece, @selected, @available) %>"
|
||||
></div>
|
||||
@ -3,16 +3,17 @@ defmodule ChessWeb.Api.OpponentsView do
|
||||
|
||||
def render("index.json", %{opponents: opponents}) do
|
||||
%{
|
||||
opponents: Enum.map(opponents, fn opponent ->
|
||||
opponent_attrs(opponent)
|
||||
end)
|
||||
opponents:
|
||||
Enum.map(opponents, fn opponent ->
|
||||
opponent_attrs(opponent)
|
||||
end)
|
||||
}
|
||||
end
|
||||
|
||||
def opponent_attrs(opponent) do
|
||||
%{
|
||||
id: opponent.id,
|
||||
name: opponent.name,
|
||||
name: opponent.name
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@ -10,7 +10,7 @@ defmodule ChessWeb.ErrorHelpers do
|
||||
"""
|
||||
def error_tag(form, field) do
|
||||
if error = form.errors[field] do
|
||||
content_tag :span, translate_error(error), class: "help-block"
|
||||
content_tag(:span, translate_error(error), class: "help-block")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -2,16 +2,16 @@ defmodule ChessWeb.ErrorView do
|
||||
use ChessWeb, :view
|
||||
|
||||
def render("404.html", _assigns) do
|
||||
gettext "Page not found"
|
||||
gettext("Page not found")
|
||||
end
|
||||
|
||||
def render("500.html", _assigns) do
|
||||
gettext "Internal server error"
|
||||
gettext("Internal server error")
|
||||
end
|
||||
|
||||
# In case no render clause matches or no
|
||||
# template is found, let's render it as 500
|
||||
def template_not_found(_template, assigns) do
|
||||
render "500.html", assigns
|
||||
render("500.html", assigns)
|
||||
end
|
||||
end
|
||||
|
||||
@ -7,8 +7,8 @@ defmodule ChessWeb.GameView do
|
||||
|
||||
def won_lost(conn, game) do
|
||||
if game_over?(game) && game.state == "checkmate" do
|
||||
your_turn?(conn, game) &&
|
||||
gettext("You lost") ||
|
||||
(your_turn?(conn, game) &&
|
||||
gettext("You lost")) ||
|
||||
gettext("You won")
|
||||
end
|
||||
end
|
||||
@ -21,9 +21,12 @@ defmodule ChessWeb.GameView do
|
||||
cond do
|
||||
GameState.game_over?(game) ->
|
||||
states()[game.state]
|
||||
|
||||
your_turn?(conn, game) ->
|
||||
gettext("Your turn")
|
||||
true -> nil
|
||||
|
||||
true ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
@ -34,11 +37,26 @@ defmodule ChessWeb.GameView do
|
||||
end
|
||||
|
||||
def your_turn?(conn, game) do
|
||||
player_colour(conn, game) == game.turn
|
||||
conn
|
||||
|> current_user()
|
||||
|> player_colour(game) == game.turn
|
||||
end
|
||||
|
||||
def player_colour(conn, game) do
|
||||
current_user(conn).id == game.user_id && "white" || "black"
|
||||
(current_user(conn).id == game.user_id && "white") || "black"
|
||||
end
|
||||
|
||||
def files(conn, game) do
|
||||
ranks(conn, game)
|
||||
|> Enum.reverse()
|
||||
end
|
||||
|
||||
def ranks(conn, game) do
|
||||
if game.user_id == current_user(conn).id do
|
||||
7..0
|
||||
else
|
||||
0..7
|
||||
end
|
||||
end
|
||||
|
||||
def player(game, user_id) do
|
||||
@ -61,7 +79,7 @@ defmodule ChessWeb.GameView do
|
||||
%{
|
||||
"checkmate" => gettext("Checkmate!"),
|
||||
"stalemate" => gettext("Stalemate"),
|
||||
"check" => gettext("Check"),
|
||||
"check" => gettext("Check")
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
118
lib/chess_web/views/live/board_live.ex
Normal file
118
lib/chess_web/views/live/board_live.ex
Normal file
@ -0,0 +1,118 @@
|
||||
defmodule ChessWeb.BoardLive do
|
||||
use Phoenix.LiveView, container: {:div, class: "board__container"}
|
||||
|
||||
alias Chess.Store.User
|
||||
alias Chess.Store.Game
|
||||
alias Chess.Repo
|
||||
alias Chess.Board
|
||||
alias Chess.Moves
|
||||
|
||||
import Chess.Auth, only: [get_user!: 1]
|
||||
|
||||
def render(assigns) do
|
||||
Phoenix.View.render(ChessWeb.GameView, "board.html", assigns)
|
||||
end
|
||||
|
||||
def mount(_params, %{"user_id" => user_id, "game_id" => game_id}, socket) do
|
||||
ChessWeb.Endpoint.subscribe("game:#{game_id}")
|
||||
|
||||
user = Repo.get!(User, user_id)
|
||||
|
||||
game =
|
||||
Game.for_user(user)
|
||||
|> Repo.get!(game_id)
|
||||
|
||||
{:ok, assign(socket, default_assigns(game, user))}
|
||||
end
|
||||
|
||||
def handle_event("click", %{"rank" => rank, "file" => file}, socket) do
|
||||
{
|
||||
:noreply,
|
||||
socket |> handle_click(String.to_integer(file), String.to_integer(rank))
|
||||
}
|
||||
end
|
||||
|
||||
defp default_assigns(game, user) do
|
||||
%{
|
||||
board: Board.transform(game.board),
|
||||
game: game,
|
||||
user: user,
|
||||
selected: nil,
|
||||
available: []
|
||||
}
|
||||
end
|
||||
|
||||
def handle_info(%{event: "move", payload: state}, socket) do
|
||||
{:noreply, assign(socket, state)}
|
||||
end
|
||||
|
||||
defp handle_click(socket, file, rank) do
|
||||
game = socket.assigns[:game]
|
||||
board = game.board
|
||||
user = socket.assigns[:user]
|
||||
|
||||
colour = ChessWeb.GameView.player_colour(user, game)
|
||||
|
||||
assigns =
|
||||
if colour == game.turn do
|
||||
case socket.assigns do
|
||||
%{selected: nil} ->
|
||||
handle_selection(board, colour, file, rank)
|
||||
|
||||
_ ->
|
||||
handle_move(socket.assigns, file, rank)
|
||||
end
|
||||
end
|
||||
|
||||
assign(socket, assigns)
|
||||
end
|
||||
|
||||
defp handle_selection(board, colour, file, rank) do
|
||||
case Board.piece(board, {file, rank}) do
|
||||
%{"colour" => ^colour} ->
|
||||
[
|
||||
{:selected, {file, rank}},
|
||||
{:available, Moves.available(board, {file, rank})}
|
||||
]
|
||||
|
||||
_ ->
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
defp handle_move(
|
||||
%{game: game, available: available, selected: selected},
|
||||
file,
|
||||
rank
|
||||
) do
|
||||
if {file, rank} in available do
|
||||
new_game =
|
||||
game
|
||||
|> Moves.make_move(%{from: selected, to: {file, rank}})
|
||||
|> case do
|
||||
{:ok, %{game: game}} ->
|
||||
board = Board.transform(game.board)
|
||||
|
||||
broadcast_move(game, board)
|
||||
|
||||
[
|
||||
{:selected, nil},
|
||||
{:available, []},
|
||||
{:board, board},
|
||||
{:game, game}
|
||||
]
|
||||
end
|
||||
else
|
||||
[{:selected, nil}, {:available, []}]
|
||||
end
|
||||
end
|
||||
|
||||
defp broadcast_move(game, board) do
|
||||
ChessWeb.Endpoint.broadcast_from(
|
||||
self(),
|
||||
"game:#{game.id}",
|
||||
"move",
|
||||
%{game: game, board: board}
|
||||
)
|
||||
end
|
||||
end
|
||||
21
lib/chess_web/views/live/game_info_live.ex
Normal file
21
lib/chess_web/views/live/game_info_live.ex
Normal file
@ -0,0 +1,21 @@
|
||||
defmodule ChessWeb.GameInfoLive do
|
||||
use Phoenix.LiveView
|
||||
|
||||
alias Chess.Store.User
|
||||
alias Chess.Store.Game
|
||||
alias Chess.Repo
|
||||
|
||||
def render(assigns) do
|
||||
Phoenix.View.render(ChessWeb.GameView, "game_info.html", assigns)
|
||||
end
|
||||
|
||||
def mount(_params, %{"game_id" => game_id, "user_id" => user_id}, socket) do
|
||||
user = Repo.get!(User, user_id)
|
||||
|
||||
game =
|
||||
Game.for_user(user)
|
||||
|> Repo.get!(game_id)
|
||||
|
||||
{:ok, assign(socket, game: game, user: user)}
|
||||
end
|
||||
end
|
||||
39
lib/chess_web/views/square_view.ex
Normal file
39
lib/chess_web/views/square_view.ex
Normal file
@ -0,0 +1,39 @@
|
||||
defmodule ChessWeb.SquareView do
|
||||
use ChessWeb, :view
|
||||
|
||||
def classes(_file, _rank, piece, selected, available) do
|
||||
square_class()
|
||||
|> add_piece_classes(piece)
|
||||
|> add_selected_class(selected)
|
||||
|> add_available_class(available)
|
||||
|> Enum.join(" ")
|
||||
end
|
||||
|
||||
defp square_class do
|
||||
["square"]
|
||||
end
|
||||
|
||||
defp add_piece_classes(classes, piece) do
|
||||
if piece != nil do
|
||||
classes ++ ["square--#{piece["type"]}", "square--#{piece["colour"]}"]
|
||||
else
|
||||
classes
|
||||
end
|
||||
end
|
||||
|
||||
defp add_selected_class(classes, selected) do
|
||||
if selected do
|
||||
classes ++ ["square--selected"]
|
||||
else
|
||||
classes
|
||||
end
|
||||
end
|
||||
|
||||
defp add_available_class(classes, available) do
|
||||
if available do
|
||||
classes ++ ["square--available"]
|
||||
else
|
||||
classes
|
||||
end
|
||||
end
|
||||
end
|
||||
41
mix.exs
41
mix.exs
@ -5,9 +5,9 @@ defmodule Chess.Mixfile do
|
||||
[
|
||||
app: :chess,
|
||||
version: "0.2.0",
|
||||
elixir: "~> 1.11.3",
|
||||
elixir: "~> 1.14.1",
|
||||
elixirc_paths: elixirc_paths(Mix.env()),
|
||||
compilers: [:phoenix, :gettext] ++ Mix.compilers(),
|
||||
compilers: [:phoenix] ++ Mix.compilers(),
|
||||
build_embedded: Mix.env() == :prod,
|
||||
start_permanent: Mix.env() == :prod,
|
||||
aliases: aliases(),
|
||||
@ -21,7 +21,7 @@ defmodule Chess.Mixfile do
|
||||
def application do
|
||||
[
|
||||
mod: {Chess, []},
|
||||
extra_applications: [:logger]
|
||||
extra_applications: [:logger, :ssl]
|
||||
]
|
||||
end
|
||||
|
||||
@ -34,25 +34,29 @@ defmodule Chess.Mixfile do
|
||||
# Type `mix help deps` for examples and options.
|
||||
defp deps do
|
||||
[
|
||||
{:argon2_elixir, "~> 1.3"},
|
||||
{:bamboo, "~> 1.0"},
|
||||
{:comeonin, "~> 4.0"},
|
||||
{:cowboy, "~> 2.1"},
|
||||
{:plug_cowboy, "~> 2.1"},
|
||||
{:argon2_elixir, "~> 3.0"},
|
||||
{:bamboo, "~> 2.0"},
|
||||
{:comeonin, "~> 5.0"},
|
||||
{:cowboy, "~> 2.0"},
|
||||
{:credo, "~> 1.0", only: [:dev, :test]},
|
||||
{:dart_sass, "~> 0.5", runtime: Mix.env() == :dev},
|
||||
{:ecto_sql, "~> 3.0"},
|
||||
{:formulator, "~> 0.1.6"},
|
||||
{:gettext, "~> 0.16.0"},
|
||||
{:guardian, "~> 1.0"},
|
||||
{:floki, "~> 0.34", only: :test},
|
||||
{:esbuild, "~> 0.6", runtime: Mix.env() == :dev},
|
||||
{:formulator, "~> 0.4.0"},
|
||||
{:gettext, "~> 0.22.0"},
|
||||
{:guardian, "~> 2.0"},
|
||||
{:jason, "~> 1.0"},
|
||||
{:phoenix, "~> 1.5.7"},
|
||||
{:phoenix, "~> 1.6.0"},
|
||||
{:phoenix_ecto, "~> 4.0"},
|
||||
{:phoenix_html, "~> 2.0"},
|
||||
{:phoenix_html, "~> 3.2.0"},
|
||||
{:phoenix_live_reload, "~> 1.0", only: :dev},
|
||||
{:phoenix_live_view, "~> 0.18"},
|
||||
{:phoenix_pubsub, "~> 2.0"},
|
||||
{:postgrex, ">= 0.15.0"},
|
||||
{:plug_cowboy, "~> 2.0"},
|
||||
{:postgrex, "~> 0.16.0"},
|
||||
{:secure_random, "~> 0.5"},
|
||||
{:wallaby, "~> 0.28.0", [runtime: false, only: :test]}
|
||||
{:wallaby, "~> 0.30.0", [runtime: false, only: :test]}
|
||||
]
|
||||
end
|
||||
|
||||
@ -66,7 +70,12 @@ defmodule Chess.Mixfile do
|
||||
[
|
||||
"ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
|
||||
"ecto.reset": ["ecto.drop", "ecto.setup"],
|
||||
test: ["ecto.create --quiet", "ecto.migrate", "test"]
|
||||
test: ["ecto.create --quiet", "ecto.migrate", "test"],
|
||||
"assets.deploy": [
|
||||
"esbuild default --minify",
|
||||
"sass default --no-source-map --style=compressed",
|
||||
"phx.digest"
|
||||
]
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
78
mix.lock
78
mix.lock
@ -1,53 +1,61 @@
|
||||
%{
|
||||
"argon2_elixir": {:hex, :argon2_elixir, "1.3.3", "487ffa071ef78c51d9b16e50ff3cf30cf8204e0aa4bdc8afd3765fdd8195e213", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "0ca19822ba4b85fe6dab75d1ed36bd9257a88cc4385bc32be177d861a1f3ec31"},
|
||||
"bamboo": {:hex, :bamboo, "1.6.0", "adfb583bef028923aae6f22deaea6667290561da1246058556ecaeb0fec5a175", [:mix], [{:hackney, ">= 1.13.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "454e67feacbc9b6e00553ce1d2fba003c861e0035600d59b09d6159985b17f9b"},
|
||||
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"},
|
||||
"certifi": {:hex, :certifi, "2.6.1", "dbab8e5e155a0763eea978c913ca280a6b544bfa115633fa20249c3d396d9493", [:rebar3], [], "hexpm", "524c97b4991b3849dd5c17a631223896272c6b0af446778ba4675a1dff53bb7e"},
|
||||
"argon2_elixir": {:hex, :argon2_elixir, "3.0.0", "fd4405f593e77b525a5c667282172dd32772d7c4fa58cdecdaae79d2713b6c5f", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "8b753b270af557d51ba13fcdebc0f0ab27a2a6792df72fd5a6cf9cfaffcedc57"},
|
||||
"bamboo": {:hex, :bamboo, "2.3.0", "d2392a2cabe91edf488553d3c70638b532e8db7b76b84b0a39e3dfe492ffd6fc", [:mix], [{:hackney, ">= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.4 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "dd0037e68e108fd04d0e8773921512c940e35d981e097b5793543e3b2f9cd3f6"},
|
||||
"bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"},
|
||||
"castore": {:hex, :castore, "0.1.22", "4127549e411bedd012ca3a308dede574f43819fe9394254ca55ab4895abfa1a2", [:mix], [], "hexpm", "c17576df47eb5aa1ee40cc4134316a99f5cad3e215d5c77b8dd3cfef12a22cac"},
|
||||
"certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"},
|
||||
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
|
||||
"comeonin": {:hex, :comeonin, "4.1.2", "3eb5620fd8e35508991664b4c2b04dd41e52f1620b36957be837c1d7784b7592", [: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", "d8700a0ca4dbb616c22c9b3f6dd539d88deaafec3efe66869d6370c9a559b3e9"},
|
||||
"comeonin": {:hex, :comeonin, "5.3.3", "2c564dac95a35650e9b6acfe6d2952083d8a08e4a89b93a481acb552b325892e", [:mix], [], "hexpm", "3e38c9c2cb080828116597ca8807bb482618a315bfafd98c90bc22a821cc84df"},
|
||||
"connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"},
|
||||
"cowboy": {:hex, :cowboy, "2.8.0", "f3dc62e35797ecd9ac1b50db74611193c29815401e53bac9a5c0577bd7bc667d", [:rebar3], [{:cowlib, "~> 2.9.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "4643e4fba74ac96d4d152c75803de6fad0b3fa5df354c71afdd6cbeeb15fac8a"},
|
||||
"cowboy_telemetry": {:hex, :cowboy_telemetry, "0.3.1", "ebd1a1d7aff97f27c66654e78ece187abdc646992714164380d8a041eda16754", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3a6efd3366130eab84ca372cbd4a7d3c3a97bdfcfb4911233b035d117063f0af"},
|
||||
"cowlib": {:hex, :cowlib, "2.9.1", "61a6c7c50cf07fdd24b2f45b89500bb93b6686579b069a89f88cb211e1125c78", [:rebar3], [], "hexpm", "e4175dc240a70d996156160891e1c62238ede1729e45740bdd38064dad476170"},
|
||||
"credo": {:hex, :credo, "1.5.4", "9914180105b438e378e94a844ec3a5088ae5875626fc945b7c1462b41afc3198", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cf51af45eadc0a3f39ba13b56fdac415c91b34f7b7533a13dc13550277141bc4"},
|
||||
"db_connection": {:hex, :db_connection, "2.3.1", "4c9f3ed1ef37471cbdd2762d6655be11e38193904d9c5c1c9389f1b891a3088e", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "abaab61780dde30301d840417890bd9f74131041afd02174cf4e10635b3a63f5"},
|
||||
"cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"},
|
||||
"cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"},
|
||||
"cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"},
|
||||
"credo": {:hex, :credo, "1.6.7", "323f5734350fd23a456f2688b9430e7d517afb313fbd38671b8a4449798a7854", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "41e110bfb007f7eda7f897c10bf019ceab9a0b269ce79f015d54b0dcf4fc7dd3"},
|
||||
"dart_sass": {:hex, :dart_sass, "0.5.1", "d45f20a8e324313689fb83287d4702352793ce8c9644bc254155d12656ade8b6", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "24f8a1c67e8b5267c51a33cbe6c0b5ebf12c2c83ace88b5ac04947d676b4ec81"},
|
||||
"db_connection": {:hex, :db_connection, "2.4.3", "3b9aac9f27347ec65b271847e6baeb4443d8474289bd18c1d6f4de655b70c94d", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c127c15b0fa6cfb32eed07465e05da6c815b032508d4ed7c116122871df73c12"},
|
||||
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
|
||||
"ecto": {:hex, :ecto, "3.5.5", "48219a991bb86daba6e38a1e64f8cea540cded58950ff38fbc8163e062281a07", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "98dd0e5e1de7f45beca6130d13116eae675db59adfa055fb79612406acf6f6f1"},
|
||||
"ecto_sql": {:hex, :ecto_sql, "3.5.3", "1964df0305538364b97cc4661a2bd2b6c89d803e66e5655e4e55ff1571943efd", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.5.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d2f53592432ce17d3978feb8f43e8dc0705e288b0890caf06d449785f018061c"},
|
||||
"elixir_make": {:hex, :elixir_make, "0.6.2", "7dffacd77dec4c37b39af867cedaabb0b59f6a871f89722c25b28fcd4bd70530", [:mix], [], "hexpm", "03e49eadda22526a7e5279d53321d1cced6552f344ba4e03e619063de75348d9"},
|
||||
"ecto": {:hex, :ecto, "3.9.4", "3ee68e25dbe0c36f980f1ba5dd41ee0d3eb0873bccae8aeaf1a2647242bffa35", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "de5f988c142a3aa4ec18b85a4ec34a2390b65b24f02385c1144252ff6ff8ee75"},
|
||||
"ecto_sql": {:hex, :ecto_sql, "3.9.2", "34227501abe92dba10d9c3495ab6770e75e79b836d114c41108a4bf2ce200ad5", [:mix], [{:db_connection, "~> 2.5 or ~> 2.4.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1eb5eeb4358fdbcd42eac11c1fbd87e3affd7904e639d77903c1358b2abd3f70"},
|
||||
"elixir_make": {:hex, :elixir_make, "0.7.3", "c37fdae1b52d2cc51069713a58c2314877c1ad40800a57efb213f77b078a460d", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "24ada3e3996adbed1fa024ca14995ef2ba3d0d17b678b0f3f2b1f66e6ce2b274"},
|
||||
"esbuild": {:hex, :esbuild, "0.6.0", "9ba6ead054abd43cb3d7b14946a0cdd1493698ccd8e054e0e5d6286d7f0f509c", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "30f9a05d4a5bab0d3e37398f312f80864e1ee1a081ca09149d06d474318fd040"},
|
||||
"expo": {:hex, :expo, "0.3.0", "13127c1d5f653b2927f2616a4c9ace5ae372efd67c7c2693b87fd0fdc30c6feb", [:mix], [], "hexpm", "fb3cd4bf012a77bc1608915497dae2ff684a06f0fa633c7afa90c4d72b881823"},
|
||||
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
|
||||
"formulator": {:hex, :formulator, "0.1.8", "eb43e1646828edb6614c2cf9c269d55b3d970414684f1e0290734d22c4936af4", [:mix], [{:gettext, ">= 0.11.0", [hex: :gettext, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.4", [hex: :phoenix_html, repo: "hexpm", optional: false]}], "hexpm", "dae3ff05743db0e31ef722e6e6ef7ed32836a65c422979e4c09b7c2f0494cf2c"},
|
||||
"gettext": {:hex, :gettext, "0.16.1", "e2130b25eebcbe02bb343b119a07ae2c7e28bd4b146c4a154da2ffb2b3507af2", [:mix], [], "hexpm", "dd3a7ea5e3e87ee9df29452dd9560709b4c7cc8141537d0b070155038d92bdf1"},
|
||||
"guardian": {:hex, :guardian, "1.2.1", "bdc8dd3dbf0fb7216cb6f91c11831faa1a64d39cdaed9a611e37f2413e584983", [:mix], [{:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.3", [hex: :phoenix, repo: "hexpm", optional: true]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "723fc404edfb7bd5cba4cd83329b352037f102aa97468f44e58ac7f47c136a98"},
|
||||
"hackney": {:hex, :hackney, "1.17.4", "99da4674592504d3fb0cfef0db84c3ba02b4508bae2dff8c0108baa0d6e0977c", [:rebar3], [{:certifi, "~>2.6.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "de16ff4996556c8548d512f4dbe22dd58a587bf3332e7fd362430a7ef3986b16"},
|
||||
"httpoison": {:hex, :httpoison, "1.8.0", "6b85dea15820b7804ef607ff78406ab449dd78bed923a49c7160e1886e987a3d", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "28089eaa98cf90c66265b6b5ad87c59a3729bea2e74e9d08f9b51eb9729b3c3a"},
|
||||
"floki": {:hex, :floki, "0.34.0", "002d0cc194b48794d74711731db004fafeb328fe676976f160685262d43706a8", [:mix], [], "hexpm", "9c3a9f43f40dde00332a589bd9d389b90c1f518aef500364d00636acc5ebc99c"},
|
||||
"formulator": {:hex, :formulator, "0.4.0", "43094c3a63e1ee077a01e79425f823d46e031cf7bc9f74045edcda695601efed", [:mix], [{:gettext, ">= 0.11.0", [hex: :gettext, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.4 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}], "hexpm", "15c34113d6a797bb03d089a0b976b0cf918d64e24d2096986b61735fffaf1980"},
|
||||
"gettext": {:hex, :gettext, "0.22.0", "a25d71ec21b1848957d9207b81fd61cb25161688d282d58bdafef74c2270bdc4", [:mix], [{:expo, "~> 0.3.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "cb0675141576f73720c8e49b4f0fd3f2c69f0cd8c218202724d4aebab8c70ace"},
|
||||
"guardian": {:hex, :guardian, "2.3.1", "2b2d78dc399a7df182d739ddc0e566d88723299bfac20be36255e2d052fd215d", [:mix], [{:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "bbe241f9ca1b09fad916ad42d6049d2600bbc688aba5b3c4a6c82592a54274c3"},
|
||||
"hackney": {:hex, :hackney, "1.18.1", "f48bf88f521f2a229fc7bae88cf4f85adc9cd9bcf23b5dc8eb6a1788c662c4f6", [:rebar3], [{:certifi, "~>2.9.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a4ecdaff44297e9b5894ae499e9a070ea1888c84afdd1fd9b7b2bc384950128e"},
|
||||
"httpoison": {:hex, :httpoison, "1.8.2", "9eb9c63ae289296a544842ef816a85d881d4a31f518a0fec089aaa744beae290", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "2bb350d26972e30c96e2ca74a1aaf8293d61d0742ff17f01e0279fef11599921"},
|
||||
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
|
||||
"jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"},
|
||||
"jose": {:hex, :jose, "1.11.1", "59da64010c69aad6cde2f5b9248b896b84472e99bd18f246085b7b9fe435dcdb", [:mix, :rebar3], [], "hexpm", "078f6c9fb3cd2f4cfafc972c814261a7d1e8d2b3685c0a76eb87e158efff1ac5"},
|
||||
"jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"},
|
||||
"jose": {:hex, :jose, "1.11.5", "3bc2d75ffa5e2c941ca93e5696b54978323191988eb8d225c2e663ddfefd515e", [:mix, :rebar3], [], "hexpm", "dcd3b215bafe02ea7c5b23dafd3eb8062a5cd8f2d904fd9caa323d37034ab384"},
|
||||
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
|
||||
"mime": {:hex, :mime, "1.5.0", "203ef35ef3389aae6d361918bf3f952fa17a09e8e43b5aa592b93eba05d0fb8d", [:mix], [], "hexpm", "55a94c0f552249fc1a3dd9cd2d3ab9de9d3c89b559c2bd01121f824834f24746"},
|
||||
"mime": {:hex, :mime, "2.0.3", "3676436d3d1f7b81b5a2d2bd8405f412c677558c81b1c92be58c00562bb59095", [:mix], [], "hexpm", "27a30bf0db44d25eecba73755acf4068cbfe26a4372f9eb3e4ea3a45956bff6b"},
|
||||
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
|
||||
"parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
|
||||
"phoenix": {:hex, :phoenix, "1.5.7", "2923bb3af924f184459fe4fa4b100bd25fa6468e69b2803dfae82698269aa5e0", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.13", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.1.2 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "774cd64417c5a3788414fdbb2be2eb9bcd0c048d9e6ad11a0c1fd67b7c0d0978"},
|
||||
"phoenix_ecto": {:hex, :phoenix_ecto, "4.2.1", "13f124cf0a3ce0f1948cf24654c7b9f2347169ff75c1123f44674afee6af3b03", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 2.15", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "478a1bae899cac0a6e02be1deec7e2944b7754c04e7d4107fc5a517f877743c0"},
|
||||
"phoenix_html": {:hex, :phoenix_html, "2.14.3", "51f720d0d543e4e157ff06b65de38e13303d5778a7919bcc696599e5934271b8", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "efd697a7fff35a13eeeb6b43db884705cba353a1a41d127d118fda5f90c8e80f"},
|
||||
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.3.0", "f35f61c3f959c9a01b36defaa1f0624edd55b87e236b606664a556d6f72fd2e7", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "02c1007ae393f2b76ec61c1a869b1e617179877984678babde131d716f95b582"},
|
||||
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.0.0", "a1ae76717bb168cdeb10ec9d92d1480fec99e3080f011402c0a2d68d47395ffb", [:mix], [], "hexpm", "c52d948c4f261577b9c6fa804be91884b381a7f8f18450c5045975435350f771"},
|
||||
"plug": {:hex, :plug, "1.11.0", "f17217525597628298998bc3baed9f8ea1fa3f1160aa9871aee6df47a6e4d38e", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2d9c633f0499f9dc5c2fd069161af4e2e7756890b81adcbb2ceaa074e8308876"},
|
||||
"plug_cowboy": {:hex, :plug_cowboy, "2.4.1", "779ba386c0915027f22e14a48919a9545714f849505fa15af2631a0d298abf0f", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d72113b6dff7b37a7d9b2a5b68892808e3a9a752f2bf7e503240945385b70507"},
|
||||
"plug_crypto": {:hex, :plug_crypto, "1.2.0", "1cb20793aa63a6c619dd18bb33d7a3aa94818e5fd39ad357051a67f26dfa2df6", [:mix], [], "hexpm", "a48b538ae8bf381ffac344520755f3007cc10bd8e90b240af98ea29b69683fc2"},
|
||||
"phoenix": {:hex, :phoenix, "1.6.15", "0a1d96bbc10747fd83525370d691953cdb6f3ccbac61aa01b4acb012474b047d", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d70ab9fbf6b394755ea88b644d34d79d8b146e490973151f248cacd122d20672"},
|
||||
"phoenix_ecto": {:hex, :phoenix_ecto, "4.4.0", "0672ed4e4808b3fbed494dded89958e22fb882de47a97634c0b13e7b0b5f7720", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "09864e558ed31ee00bd48fcc1d4fc58ae9678c9e81649075431e69dbabb43cc1"},
|
||||
"phoenix_html": {:hex, :phoenix_html, "3.2.0", "1c1219d4b6cb22ac72f12f73dc5fad6c7563104d083f711c3fcd8551a1f4ae11", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "36ec97ba56d25c0136ef1992c37957e4246b649d620958a1f9fa86165f8bc54f"},
|
||||
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.4.1", "2aff698f5e47369decde4357ba91fc9c37c6487a512b41732818f2204a8ef1d3", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "9bffb834e7ddf08467fe54ae58b5785507aaba6255568ae22b4d46e2bb3615ab"},
|
||||
"phoenix_live_view": {:hex, :phoenix_live_view, "0.18.11", "c50eac83dae6b5488859180422dfb27b2c609de87f4aa5b9c926ecd0501cd44f", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "76c99a0ffb47cd95bf06a917e74f282a603f3e77b00375f3c2dd95110971b102"},
|
||||
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.1", "ba04e489ef03763bf28a17eb2eaddc2c20c6d217e2150a61e3298b0f4c2012b5", [:mix], [], "hexpm", "81367c6d1eea5878ad726be80808eb5a787a23dee699f96e72b1109c57cdd8d9"},
|
||||
"phoenix_template": {:hex, :phoenix_template, "1.0.1", "85f79e3ad1b0180abb43f9725973e3b8c2c3354a87245f91431eec60553ed3ef", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "157dc078f6226334c91cb32c1865bf3911686f8bcd6bcff86736f6253e6993ee"},
|
||||
"phoenix_view": {:hex, :phoenix_view, "2.0.2", "6bd4d2fd595ef80d33b439ede6a19326b78f0f1d8d62b9a318e3d9c1af351098", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "a929e7230ea5c7ee0e149ffcf44ce7cf7f4b6d2bfe1752dd7c084cdff152d36f"},
|
||||
"plug": {:hex, :plug, "1.14.0", "ba4f558468f69cbd9f6b356d25443d0b796fbdc887e03fa89001384a9cac638f", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "bf020432c7d4feb7b3af16a0c2701455cbbbb95e5b6866132cb09eb0c29adc14"},
|
||||
"plug_cowboy": {:hex, :plug_cowboy, "2.6.0", "d1cf12ff96a1ca4f52207c5271a6c351a4733f413803488d75b70ccf44aebec2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "073cf20b753ce6682ed72905cd62a2d4bd9bad1bf9f7feb02a1b8e525bd94fa6"},
|
||||
"plug_crypto": {:hex, :plug_crypto, "1.2.3", "8f77d13aeb32bfd9e654cb68f0af517b371fb34c56c9f2b58fe3df1235c1251a", [:mix], [], "hexpm", "b5672099c6ad5c202c45f5a403f21a3411247f164e4a8fab056e5cd8a290f4a2"},
|
||||
"poison": {:hex, :poison, "4.0.1", "bcb755a16fac91cad79bfe9fc3585bb07b9331e50cfe3420a24bcc2d735709ae", [:mix], [], "hexpm", "ba8836feea4b394bb718a161fc59a288fe0109b5006d6bdf97b6badfcf6f0f25"},
|
||||
"poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"},
|
||||
"postgrex": {:hex, :postgrex, "0.15.7", "724410acd48abac529d0faa6c2a379fb8ae2088e31247687b16cacc0e0883372", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "88310c010ff047cecd73d5ceca1d99205e4b1ab1b9abfdab7e00f5c9d20ef8f9"},
|
||||
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"},
|
||||
"postgrex": {:hex, :postgrex, "0.16.5", "fcc4035cc90e23933c5d69a9cd686e329469446ef7abba2cf70f08e2c4b69810", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "edead639dc6e882618c01d8fc891214c481ab9a3788dfe38dd5e37fd1d5fb2e8"},
|
||||
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
|
||||
"secure_random": {:hex, :secure_random, "0.5.1", "c5532b37c89d175c328f5196a0c2a5680b15ebce3e654da37129a9fe40ebf51b", [:mix], [], "hexpm", "1b9754f15e3940a143baafd19da12293f100044df69ea12db5d72878312ae6ab"},
|
||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
|
||||
"telemetry": {:hex, :telemetry, "0.4.2", "2808c992455e08d6177322f14d3bdb6b625fbcfd233a73505870d8738a2f4599", [:rebar3], [], "hexpm", "2d1419bd9dda6a206d7b5852179511722e2b18812310d304620c7bd92a13fcef"},
|
||||
"tesla": {:hex, :tesla, "1.3.3", "26ae98627af5c406584aa6755ab5fc96315d70d69a24dd7f8369cfcb75094a45", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "2648f1c276102f9250299e0b7b57f3071c67827349d9173f34c281756a1b124c"},
|
||||
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
|
||||
"tesla": {:hex, :tesla, "1.5.0", "7ee3616be87024a2b7231ae14474310c9b999c3abb1f4f8dbc70f86bd9678eef", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "1d0385e41fbd76af3961809088aef15dec4c2fdaab97b1c93c6484cb3695a122"},
|
||||
"timex": {:hex, :timex, "3.6.3", "58ce6c9eda8ed47fc80c24dde09d481465838d3bcfc230949287fc1b0b0041c1", [: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 or ~> 1.0.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "6d69f4f95fcf5684102a9cb3cf92c5ba6545bd60ed8d8a6a93cd2a4a4fb0d9ec"},
|
||||
"timex_ecto": {:hex, :timex_ecto, "3.4.0", "7871043345626a591bfa3e313aa271df4a4eda79f51eb69e83f326f0b8f3181c", [:mix], [{:ecto, "~> 2.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:timex, "~> 3.6", [hex: :timex, repo: "hexpm", optional: false]}], "hexpm", "4237fa971c8fb9baeeb901b3ae4efee663e0da335e291228f215d99cf8a64799"},
|
||||
"tzdata": {:hex, :tzdata, "1.0.5", "69f1ee029a49afa04ad77801febaf69385f3d3e3d1e4b56b9469025677b89a28", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "55519aa2a99e5d2095c1e61cc74c9be69688f8ab75c27da724eb8279ff402a5a"},
|
||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"},
|
||||
"wallaby": {:hex, :wallaby, "0.28.0", "2ff217c0f245cadb3e5d91748ebcf0102873ceb9ef8a3507717c8bdd73915668", [:mix], [{:ecto_sql, ">= 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}, {:httpoison, "~> 0.12 or ~> 1.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix_ecto, ">= 3.0.0", [hex: :phoenix_ecto, repo: "hexpm", optional: true]}, {:web_driver_client, "~> 0.1.0", [hex: :web_driver_client, repo: "hexpm", optional: false]}], "hexpm", "e58112650d0b51e81714a626eab7d486d7a77342c9bbc2ba262b6653f9b22558"},
|
||||
"web_driver_client": {:hex, :web_driver_client, "0.1.0", "19466a989c76b7ec803c796cec0fec4611a64f445fd5120ce50c9e3817e09c2c", [:mix], [{:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:tesla, "~> 1.3.0", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm", "c9c031ca915e8fc75b5e24ac93503244f3cc406dd7f53047087a45aa62d60e9e"},
|
||||
"wallaby": {:hex, :wallaby, "0.30.1", "81342a34080867ab359aca23de4d1d8c6bbdeb35d8ce2a8c42e42b758d539963", [:mix], [{:ecto_sql, ">= 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}, {:httpoison, "~> 0.12 or ~> 1.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix_ecto, ">= 3.0.0", [hex: :phoenix_ecto, repo: "hexpm", optional: true]}, {:web_driver_client, "~> 0.2.0", [hex: :web_driver_client, repo: "hexpm", optional: false]}], "hexpm", "457251df6a94ff80816524136edbce6400cb1ee979586c90224ff634e9543d78"},
|
||||
"web_driver_client": {:hex, :web_driver_client, "0.2.0", "63b76cd9eb3b0716ec5467a0f8bead73d3d9612e63f7560d21357f03ad86e31a", [:mix], [{:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:tesla, "~> 1.3", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm", "83cc6092bc3e74926d1c8455f0ce927d5d1d36707b74d9a65e38c084aab0350f"},
|
||||
}
|
||||
|
||||
@ -1 +1,3 @@
|
||||
node_version=10.16.0
|
||||
node_version=14.15.4
|
||||
|
||||
yarn_version=1.22.10
|
||||
|
||||
@ -76,15 +76,13 @@ defmodule Chess.AuthTest do
|
||||
|
||||
test "authenticate_user/1 returns false on incorrect password " do
|
||||
user_fixture(email: "link@hyrule.com", password: "eyeofsheikah")
|
||||
assert {:error, message} =
|
||||
Auth.authenticate_user("link@hyrule.com", "shadowtemple")
|
||||
assert {:error, message} = Auth.authenticate_user("link@hyrule.com", "shadowtemple")
|
||||
assert message == "invalid password"
|
||||
end
|
||||
|
||||
test "authenticate_user/1 returns true on correct password " do
|
||||
user = user_fixture(email: "link@hyrule.com", password: "eyeofsheikah")
|
||||
assert {:ok, ^user} =
|
||||
Auth.authenticate_user("link@hyrule.com", "eyeofsheikah")
|
||||
assert {:ok, ^user} = Auth.authenticate_user("link@hyrule.com", "eyeofsheikah")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -6,35 +6,62 @@ defmodule Chess.BoardTest do
|
||||
alias Chess.Board
|
||||
|
||||
test "returns a piece from the board" do
|
||||
board = Board.default
|
||||
board = Board.default()
|
||||
|
||||
expected_piece = %{"type" => "pawn", "colour" => "white"}
|
||||
assert Board.piece(board, {4, 1}) == expected_piece
|
||||
end
|
||||
|
||||
test "finds pieces on the board" do
|
||||
board = Board.default
|
||||
board = Board.default()
|
||||
|
||||
piece = %{"type" => "pawn", "colour" => "white"}
|
||||
|
||||
expected_result = [
|
||||
{0, 1}, {1, 1}, {2, 1}, {3, 1}, {4, 1}, {5, 1}, {6, 1}, {7, 1},
|
||||
{0, 1},
|
||||
{1, 1},
|
||||
{2, 1},
|
||||
{3, 1},
|
||||
{4, 1},
|
||||
{5, 1},
|
||||
{6, 1},
|
||||
{7, 1}
|
||||
]
|
||||
|
||||
assert Board.search(board, piece) == expected_result
|
||||
end
|
||||
|
||||
test "finds pieces on the board with a partial search" do
|
||||
board = Board.default
|
||||
board = Board.default()
|
||||
|
||||
piece = %{"colour" => "white"}
|
||||
expected_result = [
|
||||
{0, 1}, {1, 1}, {2, 1}, {3, 1}, {4, 1}, {5, 1}, {6, 1}, {7, 1},
|
||||
{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}, {7, 0},
|
||||
] |> Enum.sort
|
||||
|
||||
expected_result =
|
||||
[
|
||||
{0, 1},
|
||||
{1, 1},
|
||||
{2, 1},
|
||||
{3, 1},
|
||||
{4, 1},
|
||||
{5, 1},
|
||||
{6, 1},
|
||||
{7, 1},
|
||||
{0, 0},
|
||||
{1, 0},
|
||||
{2, 0},
|
||||
{3, 0},
|
||||
{4, 0},
|
||||
{5, 0},
|
||||
{6, 0},
|
||||
{7, 0}
|
||||
]
|
||||
|> Enum.sort()
|
||||
|
||||
assert Board.search(board, piece) == expected_result
|
||||
end
|
||||
|
||||
test "finds a single piece on the board" do
|
||||
board = Board.default
|
||||
board = Board.default()
|
||||
|
||||
piece = %{"type" => "king", "colour" => "black"}
|
||||
assert Board.search(board, piece) == [{4, 7}]
|
||||
@ -42,44 +69,41 @@ defmodule Chess.BoardTest do
|
||||
|
||||
test "moves a piece" do
|
||||
board = %{
|
||||
"3,0" => %{"type" => "queen", "colour" => "white"},
|
||||
"3,0" => %{"type" => "queen", "colour" => "white"}
|
||||
}
|
||||
|
||||
%{board: new_board} =
|
||||
Board.move_piece(board, %{"from" => [3, 0], "to" => [5, 2]})
|
||||
%{board: new_board} = Board.move_piece(board, %{"from" => [3, 0], "to" => [5, 2]})
|
||||
|
||||
assert new_board == %{
|
||||
"5,2" => %{"type" => "queen", "colour" => "white"},
|
||||
}
|
||||
"5,2" => %{"type" => "queen", "colour" => "white"}
|
||||
}
|
||||
end
|
||||
|
||||
test "can perform a castling move on the kings side" do
|
||||
board = %{
|
||||
"4,0" => %{"type" => "king", "colour" => "white"},
|
||||
"7,0" => %{"type" => "rook", "colour" => "white"},
|
||||
"7,0" => %{"type" => "rook", "colour" => "white"}
|
||||
}
|
||||
|
||||
%{board: new_board} =
|
||||
Board.move_piece(board, %{"from" => [4, 0], "to" => [6, 0]})
|
||||
%{board: new_board} = Board.move_piece(board, %{"from" => [4, 0], "to" => [6, 0]})
|
||||
|
||||
assert new_board == %{
|
||||
"6,0" => %{"type" => "king", "colour" => "white"},
|
||||
"5,0" => %{"type" => "rook", "colour" => "white"},
|
||||
}
|
||||
"6,0" => %{"type" => "king", "colour" => "white"},
|
||||
"5,0" => %{"type" => "rook", "colour" => "white"}
|
||||
}
|
||||
end
|
||||
|
||||
test "can perform a castling move on the queens side" do
|
||||
board = %{
|
||||
"4,0" => %{"type" => "king", "colour" => "white"},
|
||||
"0,0" => %{"type" => "rook", "colour" => "white"},
|
||||
"0,0" => %{"type" => "rook", "colour" => "white"}
|
||||
}
|
||||
|
||||
%{board: new_board} =
|
||||
Board.move_piece(board, %{"from" => [4, 0], "to" => [2, 0]})
|
||||
%{board: new_board} = Board.move_piece(board, %{"from" => [4, 0], "to" => [2, 0]})
|
||||
|
||||
assert new_board == %{
|
||||
"2,0" => %{"type" => "king", "colour" => "white"},
|
||||
"3,0" => %{"type" => "rook", "colour" => "white"},
|
||||
}
|
||||
"2,0" => %{"type" => "king", "colour" => "white"},
|
||||
"3,0" => %{"type" => "rook", "colour" => "white"}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@ -8,7 +8,7 @@ defmodule Chess.GameStateTest do
|
||||
test "king is in check" do
|
||||
board = %{
|
||||
"4,0" => %{"type" => "king", "colour" => "white"},
|
||||
"4,7" => %{"type" => "queen", "colour" => "black"},
|
||||
"4,7" => %{"type" => "queen", "colour" => "black"}
|
||||
}
|
||||
|
||||
assert GameState.king_in_check?(board, "white")
|
||||
@ -17,7 +17,7 @@ defmodule Chess.GameStateTest do
|
||||
test "king is not in check" do
|
||||
board = %{
|
||||
"5,0" => %{"type" => "king", "colour" => "white"},
|
||||
"4,7" => %{"type" => "queen", "colour" => "black"},
|
||||
"4,7" => %{"type" => "queen", "colour" => "black"}
|
||||
}
|
||||
|
||||
refute GameState.king_in_check?(board, "white")
|
||||
@ -26,7 +26,7 @@ defmodule Chess.GameStateTest do
|
||||
test "king is in check by a knight" do
|
||||
board = %{
|
||||
"4,0" => %{"type" => "king", "colour" => "white"},
|
||||
"3,2" => %{"type" => "knight", "colour" => "black"},
|
||||
"3,2" => %{"type" => "knight", "colour" => "black"}
|
||||
}
|
||||
|
||||
assert GameState.king_in_check?(board, "white")
|
||||
@ -35,7 +35,7 @@ defmodule Chess.GameStateTest do
|
||||
test "king is in check by a pawn" do
|
||||
board = %{
|
||||
"4,0" => %{"type" => "king", "colour" => "white"},
|
||||
"3,1" => %{"type" => "pawn", "colour" => "black"},
|
||||
"3,1" => %{"type" => "pawn", "colour" => "black"}
|
||||
}
|
||||
|
||||
assert GameState.king_in_check?(board, "white")
|
||||
@ -45,7 +45,7 @@ defmodule Chess.GameStateTest do
|
||||
board = %{
|
||||
"0,0" => %{"type" => "king", "colour" => "white"},
|
||||
"0,4" => %{"type" => "queen", "colour" => "black"},
|
||||
"1,4" => %{"type" => "rook", "colour" => "black"},
|
||||
"1,4" => %{"type" => "rook", "colour" => "black"}
|
||||
}
|
||||
|
||||
assert GameState.player_checkmated?(board, "white")
|
||||
@ -54,7 +54,7 @@ defmodule Chess.GameStateTest do
|
||||
test "king is not in checkmate by a queen" do
|
||||
board = %{
|
||||
"0,0" => %{"type" => "king", "colour" => "white"},
|
||||
"0,4" => %{"type" => "queen", "colour" => "black"},
|
||||
"0,4" => %{"type" => "queen", "colour" => "black"}
|
||||
}
|
||||
|
||||
refute GameState.player_checkmated?(board, "white")
|
||||
@ -64,7 +64,7 @@ defmodule Chess.GameStateTest do
|
||||
board = %{
|
||||
"0,0" => %{"type" => "king", "colour" => "white"},
|
||||
"1,1" => %{"type" => "queen", "colour" => "black"},
|
||||
"2,3" => %{"type" => "knight", "colour" => "black"},
|
||||
"2,3" => %{"type" => "knight", "colour" => "black"}
|
||||
}
|
||||
|
||||
assert GameState.player_checkmated?(board, "white")
|
||||
@ -75,7 +75,7 @@ defmodule Chess.GameStateTest do
|
||||
"0,0" => %{"type" => "king", "colour" => "white"},
|
||||
"2,0" => %{"type" => "knight", "colour" => "white"},
|
||||
"0,5" => %{"type" => "queen", "colour" => "black"},
|
||||
"1,5" => %{"type" => "rook", "colour" => "black"},
|
||||
"1,5" => %{"type" => "rook", "colour" => "black"}
|
||||
}
|
||||
|
||||
refute GameState.player_checkmated?(board, "white")
|
||||
@ -86,7 +86,7 @@ defmodule Chess.GameStateTest do
|
||||
"0,0" => %{"type" => "king", "colour" => "white"},
|
||||
"2,3" => %{"type" => "bishop", "colour" => "white"},
|
||||
"0,5" => %{"type" => "queen", "colour" => "black"},
|
||||
"1,5" => %{"type" => "rook", "colour" => "black"},
|
||||
"1,5" => %{"type" => "rook", "colour" => "black"}
|
||||
}
|
||||
|
||||
refute GameState.player_checkmated?(board, "white")
|
||||
@ -96,7 +96,7 @@ defmodule Chess.GameStateTest do
|
||||
board = %{
|
||||
"0,0" => %{"type" => "king", "colour" => "white"},
|
||||
"1,2" => %{"type" => "rook", "colour" => "black"},
|
||||
"2,1" => %{"type" => "rook", "colour" => "black"},
|
||||
"2,1" => %{"type" => "rook", "colour" => "black"}
|
||||
}
|
||||
|
||||
assert GameState.player_stalemated?(board, "white")
|
||||
|
||||
@ -12,18 +12,18 @@ defmodule Chess.MoveListTest do
|
||||
%Move{
|
||||
piece: %{"type" => "pawn", "colour" => "white"},
|
||||
from: %{"file" => 4, "rank" => 1},
|
||||
to: %{"file" => 4, "rank" => 3},
|
||||
to: %{"file" => 4, "rank" => 3}
|
||||
},
|
||||
%Move{
|
||||
piece: %{"type" => "pawn", "colour" => "black"},
|
||||
from: %{"file" => 4, "rank" => 6},
|
||||
to: %{"file" => 4, "rank" => 4},
|
||||
to: %{"file" => 4, "rank" => 4}
|
||||
},
|
||||
%Move{
|
||||
piece: %{"type" => "knight", "colour" => "white"},
|
||||
from: %{"file" => 1, "rank" => 0},
|
||||
to: %{"file" => 2, "rank" => 2},
|
||||
},
|
||||
to: %{"file" => 2, "rank" => 2}
|
||||
}
|
||||
]
|
||||
|
||||
expected_result = [
|
||||
@ -51,7 +51,7 @@ defmodule Chess.MoveListTest do
|
||||
from: "b1",
|
||||
to: "c3"
|
||||
}
|
||||
],
|
||||
]
|
||||
]
|
||||
|
||||
assert MoveList.transform(moves) == expected_result
|
||||
|
||||
@ -6,7 +6,7 @@ defmodule Chess.Moves.PieceTest do
|
||||
test "piece is not being attacked" do
|
||||
board = %{
|
||||
"4,5" => %{"type" => "king", "colour" => "white"},
|
||||
"2,1" => %{"type" => "rook", "colour" => "black"},
|
||||
"2,1" => %{"type" => "rook", "colour" => "black"}
|
||||
}
|
||||
|
||||
refute Piece.attacked?(board, {4, 5})
|
||||
@ -15,7 +15,7 @@ defmodule Chess.Moves.PieceTest do
|
||||
test "piece on the edge of the board is not being attacked" do
|
||||
board = %{
|
||||
"4,0" => %{"type" => "king", "colour" => "white"},
|
||||
"2,7" => %{"type" => "rook", "colour" => "black"},
|
||||
"2,7" => %{"type" => "rook", "colour" => "black"}
|
||||
}
|
||||
|
||||
refute Piece.attacked?(board, {4, 0})
|
||||
@ -25,7 +25,7 @@ defmodule Chess.Moves.PieceTest do
|
||||
board = %{
|
||||
"4,0" => %{"type" => "king", "colour" => "white"},
|
||||
"4,1" => %{"type" => "pawn", "colour" => "white"},
|
||||
"7,3" => %{"type" => "bishop", "colour" => "black"},
|
||||
"7,3" => %{"type" => "bishop", "colour" => "black"}
|
||||
}
|
||||
|
||||
assert Piece.attacked?(board, {4, 0})
|
||||
@ -34,7 +34,7 @@ defmodule Chess.Moves.PieceTest do
|
||||
test "piece is not being attacked by piece of its own colour" do
|
||||
board = %{
|
||||
"4,5" => %{"type" => "king", "colour" => "white"},
|
||||
"2,5" => %{"type" => "rook", "colour" => "white"},
|
||||
"2,5" => %{"type" => "rook", "colour" => "white"}
|
||||
}
|
||||
|
||||
refute Piece.attacked?(board, {4, 5})
|
||||
@ -43,7 +43,7 @@ defmodule Chess.Moves.PieceTest do
|
||||
test "piece can be attacked by a rook" do
|
||||
board = %{
|
||||
"4,5" => %{"type" => "king", "colour" => "white"},
|
||||
"2,5" => %{"type" => "rook", "colour" => "black"},
|
||||
"2,5" => %{"type" => "rook", "colour" => "black"}
|
||||
}
|
||||
|
||||
assert Piece.attacked?(board, {4, 5})
|
||||
@ -52,7 +52,7 @@ defmodule Chess.Moves.PieceTest do
|
||||
test "piece can be attacked by a bishop" do
|
||||
board = %{
|
||||
"4,5" => %{"type" => "king", "colour" => "white"},
|
||||
"6,7" => %{"type" => "bishop", "colour" => "black"},
|
||||
"6,7" => %{"type" => "bishop", "colour" => "black"}
|
||||
}
|
||||
|
||||
assert Piece.attacked?(board, {4, 5})
|
||||
@ -61,7 +61,7 @@ defmodule Chess.Moves.PieceTest do
|
||||
test "piece can be attacked by a queen" do
|
||||
board = %{
|
||||
"4,5" => %{"type" => "king", "colour" => "white"},
|
||||
"6,7" => %{"type" => "queen", "colour" => "black"},
|
||||
"6,7" => %{"type" => "queen", "colour" => "black"}
|
||||
}
|
||||
|
||||
assert Piece.attacked?(board, {4, 5})
|
||||
@ -70,7 +70,7 @@ defmodule Chess.Moves.PieceTest do
|
||||
test "piece is not attacked by a knight" do
|
||||
board = %{
|
||||
"4,5" => %{"type" => "king", "colour" => "white"},
|
||||
"7,7" => %{"type" => "knight", "colour" => "black"},
|
||||
"7,7" => %{"type" => "knight", "colour" => "black"}
|
||||
}
|
||||
|
||||
refute Piece.attacked?(board, {4, 5})
|
||||
@ -79,7 +79,7 @@ defmodule Chess.Moves.PieceTest do
|
||||
test "piece can be attacked by a knight" do
|
||||
board = %{
|
||||
"4,5" => %{"type" => "king", "colour" => "white"},
|
||||
"5,7" => %{"type" => "knight", "colour" => "black"},
|
||||
"5,7" => %{"type" => "knight", "colour" => "black"}
|
||||
}
|
||||
|
||||
assert Piece.attacked?(board, {4, 5})
|
||||
@ -88,7 +88,7 @@ defmodule Chess.Moves.PieceTest do
|
||||
test "piece can be attacked by a pawn" do
|
||||
board = %{
|
||||
"4,5" => %{"type" => "king", "colour" => "white"},
|
||||
"5,6" => %{"type" => "pawn", "colour" => "black"},
|
||||
"5,6" => %{"type" => "pawn", "colour" => "black"}
|
||||
}
|
||||
|
||||
assert Piece.attacked?(board, {4, 5})
|
||||
@ -97,7 +97,7 @@ defmodule Chess.Moves.PieceTest do
|
||||
test "piece is not attacked by a pawn directly in front" do
|
||||
board = %{
|
||||
"4,5" => %{"type" => "king", "colour" => "white"},
|
||||
"4,6" => %{"type" => "pawn", "colour" => "black"},
|
||||
"4,6" => %{"type" => "pawn", "colour" => "black"}
|
||||
}
|
||||
|
||||
refute Piece.attacked?(board, {4, 5})
|
||||
|
||||
@ -7,10 +7,21 @@ defmodule Chess.Moves.Pieces.BishopTest do
|
||||
board = %{"4,5" => %{"type" => "bishop", "colour" => "white"}}
|
||||
moves = Moves.available(board, {4, 5})
|
||||
|
||||
expected_moves = Enum.sort([
|
||||
{0, 1}, {1, 2}, {2, 3}, {3, 4}, {5, 6}, {6, 7},
|
||||
{2, 7}, {3, 6}, {5, 4}, {6, 3}, {7, 2},
|
||||
])
|
||||
expected_moves =
|
||||
Enum.sort([
|
||||
{0, 1},
|
||||
{1, 2},
|
||||
{2, 3},
|
||||
{3, 4},
|
||||
{5, 6},
|
||||
{6, 7},
|
||||
{2, 7},
|
||||
{3, 6},
|
||||
{5, 4},
|
||||
{6, 3},
|
||||
{7, 2}
|
||||
])
|
||||
|
||||
assert Enum.sort(moves) == expected_moves
|
||||
end
|
||||
|
||||
@ -18,39 +29,60 @@ defmodule Chess.Moves.Pieces.BishopTest do
|
||||
board = %{"0,0" => %{"type" => "bishop", "colour" => "white"}}
|
||||
moves = Moves.available(board, {0, 0})
|
||||
|
||||
expected_moves = Enum.sort([
|
||||
{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {6, 6}, {7, 7},
|
||||
])
|
||||
expected_moves =
|
||||
Enum.sort([
|
||||
{1, 1},
|
||||
{2, 2},
|
||||
{3, 3},
|
||||
{4, 4},
|
||||
{5, 5},
|
||||
{6, 6},
|
||||
{7, 7}
|
||||
])
|
||||
|
||||
assert Enum.sort(moves) == expected_moves
|
||||
end
|
||||
|
||||
test "bishops are blocked by pieces of their own colour" do
|
||||
board = %{
|
||||
"0,0" => %{"type" => "bishop", "colour" => "white"},
|
||||
"5,5" => %{"type" => "king", "colour" => "white"},
|
||||
"5,5" => %{"type" => "king", "colour" => "white"}
|
||||
}
|
||||
|
||||
moves = Moves.available(board, {0, 0})
|
||||
|
||||
expected_moves = Enum.sort([
|
||||
{1, 1}, {2, 2}, {3, 3}, {4, 4},
|
||||
])
|
||||
expected_moves =
|
||||
Enum.sort([
|
||||
{1, 1},
|
||||
{2, 2},
|
||||
{3, 3},
|
||||
{4, 4}
|
||||
])
|
||||
|
||||
assert Enum.sort(moves) == expected_moves
|
||||
end
|
||||
|
||||
test "bishops can take an opponents piece" do
|
||||
board = %{
|
||||
"0,0" => %{"type" => "bishop", "colour" => "white"},
|
||||
"5,5" => %{"type" => "knight", "colour" => "black"},
|
||||
"5,5" => %{"type" => "knight", "colour" => "black"}
|
||||
}
|
||||
|
||||
moves = Moves.available(board, {0, 0})
|
||||
|
||||
expected_moves = Enum.sort([
|
||||
{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5},
|
||||
])
|
||||
expected_moves =
|
||||
Enum.sort([
|
||||
{1, 1},
|
||||
{2, 2},
|
||||
{3, 3},
|
||||
{4, 4},
|
||||
{5, 5}
|
||||
])
|
||||
|
||||
assert Enum.sort(moves) == expected_moves
|
||||
end
|
||||
|
||||
def board do
|
||||
Chess.Board.default
|
||||
Chess.Board.default()
|
||||
end
|
||||
end
|
||||
|
||||
@ -7,28 +7,42 @@ defmodule Chess.Moves.Pieces.King.CastlingTest do
|
||||
test "king can move two spaces to castle with the king side rook" do
|
||||
board = %{
|
||||
"4,0" => %{"type" => "king", "colour" => "white"},
|
||||
"7,0" => %{"type" => "rook", "colour" => "white"},
|
||||
"7,0" => %{"type" => "rook", "colour" => "white"}
|
||||
}
|
||||
|
||||
moves = Moves.available(board, {4, 0})
|
||||
|
||||
expected_moves = Enum.sort([
|
||||
{3, 0}, {5, 0}, {3, 1}, {4, 1}, {5, 1},
|
||||
{6, 0},
|
||||
])
|
||||
expected_moves =
|
||||
Enum.sort([
|
||||
{3, 0},
|
||||
{5, 0},
|
||||
{3, 1},
|
||||
{4, 1},
|
||||
{5, 1},
|
||||
{6, 0}
|
||||
])
|
||||
|
||||
assert Enum.sort(moves) == expected_moves
|
||||
end
|
||||
|
||||
test "king can move two spaces to castle with the queen side rook" do
|
||||
board = %{
|
||||
"4,0" => %{"type" => "king", "colour" => "white"},
|
||||
"0,0" => %{"type" => "rook", "colour" => "white"},
|
||||
"0,0" => %{"type" => "rook", "colour" => "white"}
|
||||
}
|
||||
|
||||
moves = Moves.available(board, {4, 0})
|
||||
|
||||
expected_moves = Enum.sort([
|
||||
{3, 0}, {5, 0}, {3, 1}, {4, 1}, {5, 1},
|
||||
{2, 0},
|
||||
])
|
||||
expected_moves =
|
||||
Enum.sort([
|
||||
{3, 0},
|
||||
{5, 0},
|
||||
{3, 1},
|
||||
{4, 1},
|
||||
{5, 1},
|
||||
{2, 0}
|
||||
])
|
||||
|
||||
assert Enum.sort(moves) == expected_moves
|
||||
end
|
||||
|
||||
@ -36,13 +50,19 @@ defmodule Chess.Moves.Pieces.King.CastlingTest do
|
||||
board = %{
|
||||
"4,0" => %{"type" => "king", "colour" => "white"},
|
||||
"3,0" => %{"type" => "queen", "colour" => "white"},
|
||||
"0,0" => %{"type" => "rook", "colour" => "white"},
|
||||
"0,0" => %{"type" => "rook", "colour" => "white"}
|
||||
}
|
||||
|
||||
moves = Moves.available(board, {4, 0})
|
||||
|
||||
expected_moves = Enum.sort([
|
||||
{5, 0}, {3, 1}, {4, 1}, {5, 1},
|
||||
])
|
||||
expected_moves =
|
||||
Enum.sort([
|
||||
{5, 0},
|
||||
{3, 1},
|
||||
{4, 1},
|
||||
{5, 1}
|
||||
])
|
||||
|
||||
assert Enum.sort(moves) == expected_moves
|
||||
end
|
||||
|
||||
@ -50,13 +70,20 @@ defmodule Chess.Moves.Pieces.King.CastlingTest do
|
||||
board = %{
|
||||
"4,0" => %{"type" => "king", "colour" => "white"},
|
||||
"0,0" => %{"type" => "rook", "colour" => "white"},
|
||||
"2,7" => %{"type" => "queen", "colour" => "black"},
|
||||
"2,7" => %{"type" => "queen", "colour" => "black"}
|
||||
}
|
||||
|
||||
moves = Moves.available(board, {4, 0})
|
||||
|
||||
expected_moves = Enum.sort([
|
||||
{3, 0}, {5, 0}, {3, 1}, {4, 1}, {5, 1},
|
||||
])
|
||||
expected_moves =
|
||||
Enum.sort([
|
||||
{3, 0},
|
||||
{5, 0},
|
||||
{3, 1},
|
||||
{4, 1},
|
||||
{5, 1}
|
||||
])
|
||||
|
||||
assert Enum.sort(moves) == expected_moves
|
||||
end
|
||||
|
||||
@ -64,88 +91,119 @@ defmodule Chess.Moves.Pieces.King.CastlingTest do
|
||||
board = %{
|
||||
"4,0" => %{"type" => "king", "colour" => "white"},
|
||||
"0,0" => %{"type" => "rook", "colour" => "white"},
|
||||
"3,7" => %{"type" => "queen", "colour" => "black"},
|
||||
"3,7" => %{"type" => "queen", "colour" => "black"}
|
||||
}
|
||||
|
||||
moves = Moves.available(board, {4, 0})
|
||||
|
||||
expected_moves = Enum.sort([
|
||||
{3, 0}, {5, 0}, {3, 1}, {4, 1}, {5, 1},
|
||||
])
|
||||
expected_moves =
|
||||
Enum.sort([
|
||||
{3, 0},
|
||||
{5, 0},
|
||||
{3, 1},
|
||||
{4, 1},
|
||||
{5, 1}
|
||||
])
|
||||
|
||||
assert Enum.sort(moves) == expected_moves
|
||||
end
|
||||
|
||||
test "cannot castle if the king has moved" do
|
||||
board = %{
|
||||
"4,0" => %{"type" => "king", "colour" => "white"},
|
||||
"0,0" => %{"type" => "rook", "colour" => "white"},
|
||||
"0,0" => %{"type" => "rook", "colour" => "white"}
|
||||
}
|
||||
|
||||
move_list = [
|
||||
%Move{
|
||||
from: %{"file" => 4, "rank" => 0},
|
||||
to: %{"file" => 4, "rank" => 1},
|
||||
piece: %{"type" => "king", "colour" => "white"}
|
||||
piece: %{"type" => "king", "colour" => "white"}
|
||||
},
|
||||
%Move{
|
||||
from: %{"file" => 4, "rank" => 1},
|
||||
to: %{"file" => 4, "rank" => 0},
|
||||
piece: %{"type" => "king", "colour" => "white"}
|
||||
},
|
||||
piece: %{"type" => "king", "colour" => "white"}
|
||||
}
|
||||
]
|
||||
|
||||
moves = Moves.available(board, {4, 0}, move_list)
|
||||
|
||||
expected_moves = Enum.sort([
|
||||
{3, 0}, {5, 0}, {3, 1}, {4, 1}, {5, 1},
|
||||
])
|
||||
expected_moves =
|
||||
Enum.sort([
|
||||
{3, 0},
|
||||
{5, 0},
|
||||
{3, 1},
|
||||
{4, 1},
|
||||
{5, 1}
|
||||
])
|
||||
|
||||
assert Enum.sort(moves) == expected_moves
|
||||
end
|
||||
|
||||
test "cannot castle if the queen side rook has moved" do
|
||||
board = %{
|
||||
"4,0" => %{"type" => "king", "colour" => "white"},
|
||||
"0,0" => %{"type" => "rook", "colour" => "white"},
|
||||
"0,0" => %{"type" => "rook", "colour" => "white"}
|
||||
}
|
||||
|
||||
move_list = [
|
||||
%Move{
|
||||
from: %{"file" => 0, "rank" => 0},
|
||||
to: %{"file" => 0, "rank" => 1},
|
||||
piece: %{"type" => "rook", "colour" => "white"}
|
||||
piece: %{"type" => "rook", "colour" => "white"}
|
||||
},
|
||||
%Move{
|
||||
from: %{"file" => 0, "rank" => 1},
|
||||
to: %{"file" => 0, "rank" => 0},
|
||||
piece: %{"type" => "rook", "colour" => "white"}
|
||||
},
|
||||
piece: %{"type" => "rook", "colour" => "white"}
|
||||
}
|
||||
]
|
||||
|
||||
moves = Moves.available(board, {4, 0}, move_list)
|
||||
|
||||
expected_moves = Enum.sort([
|
||||
{3, 0}, {5, 0}, {3, 1}, {4, 1}, {5, 1},
|
||||
])
|
||||
expected_moves =
|
||||
Enum.sort([
|
||||
{3, 0},
|
||||
{5, 0},
|
||||
{3, 1},
|
||||
{4, 1},
|
||||
{5, 1}
|
||||
])
|
||||
|
||||
assert Enum.sort(moves) == expected_moves
|
||||
end
|
||||
|
||||
test "cannot castle if the king side rook has moved" do
|
||||
board = %{
|
||||
"4,0" => %{"type" => "king", "colour" => "white"},
|
||||
"7,0" => %{"type" => "rook", "colour" => "white"},
|
||||
"7,0" => %{"type" => "rook", "colour" => "white"}
|
||||
}
|
||||
|
||||
move_list = [
|
||||
%Move{
|
||||
from: %{"file" => 7, "rank" => 0},
|
||||
to: %{"file" => 7, "rank" => 1},
|
||||
piece: %{"type" => "rook", "colour" => "white"}
|
||||
piece: %{"type" => "rook", "colour" => "white"}
|
||||
},
|
||||
%Move{
|
||||
from: %{"file" => 7, "rank" => 1},
|
||||
to: %{"file" => 7, "rank" => 0},
|
||||
piece: %{"type" => "rook", "colour" => "white"}
|
||||
},
|
||||
piece: %{"type" => "rook", "colour" => "white"}
|
||||
}
|
||||
]
|
||||
|
||||
moves = Moves.available(board, {4, 0}, move_list)
|
||||
|
||||
expected_moves = Enum.sort([
|
||||
{3, 0}, {5, 0}, {3, 1}, {4, 1}, {5, 1},
|
||||
])
|
||||
expected_moves =
|
||||
Enum.sort([
|
||||
{3, 0},
|
||||
{5, 0},
|
||||
{3, 1},
|
||||
{4, 1},
|
||||
{5, 1}
|
||||
])
|
||||
|
||||
assert Enum.sort(moves) == expected_moves
|
||||
end
|
||||
end
|
||||
|
||||
@ -7,9 +7,18 @@ defmodule Chess.Moves.Pieces.KingTest do
|
||||
board = %{"4,5" => %{"type" => "king", "colour" => "white"}}
|
||||
moves = Moves.available(board, {4, 5})
|
||||
|
||||
expected_moves = Enum.sort([
|
||||
{3, 4}, {4, 4}, {5, 4}, {5, 5}, {5, 6}, {4, 6}, {3, 6}, {3, 5},
|
||||
])
|
||||
expected_moves =
|
||||
Enum.sort([
|
||||
{3, 4},
|
||||
{4, 4},
|
||||
{5, 4},
|
||||
{5, 5},
|
||||
{5, 6},
|
||||
{4, 6},
|
||||
{3, 6},
|
||||
{3, 5}
|
||||
])
|
||||
|
||||
assert Enum.sort(moves) == expected_moves
|
||||
end
|
||||
|
||||
@ -17,26 +26,34 @@ defmodule Chess.Moves.Pieces.KingTest do
|
||||
board = %{"0,0" => %{"type" => "king", "colour" => "white"}}
|
||||
moves = Moves.available(board, {0, 0})
|
||||
|
||||
expected_moves = Enum.sort([
|
||||
{0, 1}, {1, 1}, {1, 0},
|
||||
])
|
||||
expected_moves =
|
||||
Enum.sort([
|
||||
{0, 1},
|
||||
{1, 1},
|
||||
{1, 0}
|
||||
])
|
||||
|
||||
assert Enum.sort(moves) == expected_moves
|
||||
end
|
||||
|
||||
test "kings are blocked by pieces of the same colour" do
|
||||
board = %{
|
||||
"0,0" => %{"type" => "king", "colour" => "white"},
|
||||
"1,1" => %{"type" => "rook", "colour" => "white"},
|
||||
"1,1" => %{"type" => "rook", "colour" => "white"}
|
||||
}
|
||||
|
||||
moves = Moves.available(board, {0, 0})
|
||||
|
||||
expected_moves = Enum.sort([
|
||||
{0, 1}, {1, 0},
|
||||
])
|
||||
expected_moves =
|
||||
Enum.sort([
|
||||
{0, 1},
|
||||
{1, 0}
|
||||
])
|
||||
|
||||
assert Enum.sort(moves) == expected_moves
|
||||
end
|
||||
|
||||
def board do
|
||||
Chess.Board.default
|
||||
Chess.Board.default()
|
||||
end
|
||||
end
|
||||
|
||||
@ -7,9 +7,18 @@ defmodule Chess.Moves.Pieces.KnightTest do
|
||||
board = %{"4,5" => %{"type" => "knight", "colour" => "white"}}
|
||||
moves = Moves.available(board, {4, 5})
|
||||
|
||||
expected_moves = Enum.sort([
|
||||
{3, 7}, {5, 7}, {6, 6}, {6, 4}, {5, 3}, {3, 3}, {2, 4}, {2, 6},
|
||||
])
|
||||
expected_moves =
|
||||
Enum.sort([
|
||||
{3, 7},
|
||||
{5, 7},
|
||||
{6, 6},
|
||||
{6, 4},
|
||||
{5, 3},
|
||||
{3, 3},
|
||||
{2, 4},
|
||||
{2, 6}
|
||||
])
|
||||
|
||||
assert Enum.sort(moves) == expected_moves
|
||||
end
|
||||
|
||||
@ -17,26 +26,32 @@ defmodule Chess.Moves.Pieces.KnightTest do
|
||||
board = %{"0,0" => %{"type" => "knight", "colour" => "white"}}
|
||||
moves = Moves.available(board, {0, 0})
|
||||
|
||||
expected_moves = Enum.sort([
|
||||
{1, 2}, {2, 1}
|
||||
])
|
||||
expected_moves =
|
||||
Enum.sort([
|
||||
{1, 2},
|
||||
{2, 1}
|
||||
])
|
||||
|
||||
assert Enum.sort(moves) == expected_moves
|
||||
end
|
||||
|
||||
test "knights are blocked by other pieces of the same colour" do
|
||||
board = %{
|
||||
"0,0" => %{"type" => "knight", "colour" => "white"},
|
||||
"1,2" => %{"type" => "king", "colour" => "white"},
|
||||
"1,2" => %{"type" => "king", "colour" => "white"}
|
||||
}
|
||||
|
||||
moves = Moves.available(board, {0, 0})
|
||||
|
||||
expected_moves = Enum.sort([
|
||||
{2, 1},
|
||||
])
|
||||
expected_moves =
|
||||
Enum.sort([
|
||||
{2, 1}
|
||||
])
|
||||
|
||||
assert Enum.sort(moves) == expected_moves
|
||||
end
|
||||
|
||||
def board do
|
||||
Chess.Board.default
|
||||
Chess.Board.default()
|
||||
end
|
||||
end
|
||||
|
||||
@ -36,8 +36,9 @@ defmodule Chess.Moves.Pieces.PawnTest do
|
||||
test "pawn is blocked from moving two squares by another piece" do
|
||||
board = %{
|
||||
"4,1" => %{"type" => "pawn", "colour" => "white"},
|
||||
"4,3" => %{"type" => "pawn", "colour" => "black"},
|
||||
"4,3" => %{"type" => "pawn", "colour" => "black"}
|
||||
}
|
||||
|
||||
moves = Moves.available(board, {4, 1})
|
||||
|
||||
expected_moves = [{4, 2}]
|
||||
@ -47,8 +48,9 @@ defmodule Chess.Moves.Pieces.PawnTest do
|
||||
test "pawn is blocked from moving one or two squares by another piece" do
|
||||
board = %{
|
||||
"4,1" => %{"type" => "pawn", "colour" => "white"},
|
||||
"4,2" => %{"type" => "pawn", "colour" => "black"},
|
||||
"4,2" => %{"type" => "pawn", "colour" => "black"}
|
||||
}
|
||||
|
||||
moves = Moves.available(board, {4, 1})
|
||||
|
||||
expected_moves = []
|
||||
@ -58,8 +60,9 @@ defmodule Chess.Moves.Pieces.PawnTest do
|
||||
test "pawn is blocked from moving one square by another piece" do
|
||||
board = %{
|
||||
"4,2" => %{"type" => "pawn", "colour" => "white"},
|
||||
"4,3" => %{"type" => "pawn", "colour" => "black"},
|
||||
"4,3" => %{"type" => "pawn", "colour" => "black"}
|
||||
}
|
||||
|
||||
moves = Moves.available(board, {4, 2})
|
||||
|
||||
expected_moves = []
|
||||
@ -69,8 +72,9 @@ defmodule Chess.Moves.Pieces.PawnTest do
|
||||
test "white pawn can take an opponents piece" do
|
||||
board = %{
|
||||
"4,2" => %{"type" => "pawn", "colour" => "white"},
|
||||
"5,3" => %{"type" => "pawn", "colour" => "black"},
|
||||
"5,3" => %{"type" => "pawn", "colour" => "black"}
|
||||
}
|
||||
|
||||
moves = Moves.available(board, {4, 2})
|
||||
|
||||
expected_moves = [{4, 3}, {5, 3}]
|
||||
@ -80,8 +84,9 @@ defmodule Chess.Moves.Pieces.PawnTest do
|
||||
test "black pawn can take an opponents piece" do
|
||||
board = %{
|
||||
"6,6" => %{"type" => "pawn", "colour" => "black"},
|
||||
"5,5" => %{"type" => "pawn", "colour" => "white"},
|
||||
"5,5" => %{"type" => "pawn", "colour" => "white"}
|
||||
}
|
||||
|
||||
moves = Moves.available(board, {6, 6})
|
||||
|
||||
expected_moves = [{6, 5}, {6, 4}, {5, 5}]
|
||||
@ -89,6 +94,6 @@ defmodule Chess.Moves.Pieces.PawnTest do
|
||||
end
|
||||
|
||||
def default_board do
|
||||
Chess.Board.default
|
||||
Chess.Board.default()
|
||||
end
|
||||
end
|
||||
|
||||
@ -7,12 +7,35 @@ defmodule Chess.Moves.Pieces.QueenTest do
|
||||
board = %{"4,5" => %{"type" => "queen", "colour" => "white"}}
|
||||
moves = Moves.available(board, {4, 5})
|
||||
|
||||
expected_moves = Enum.sort([
|
||||
{4, 0}, {4, 1}, {4, 2}, {4, 3}, {4, 4}, {4, 6}, {4, 7},
|
||||
{0, 5}, {1, 5}, {2, 5}, {3, 5}, {5, 5}, {6, 5}, {7, 5},
|
||||
{0, 1}, {1, 2}, {2, 3}, {3, 4}, {5, 6}, {6, 7},
|
||||
{2, 7}, {3, 6}, {5, 4}, {6, 3}, {7, 2},
|
||||
])
|
||||
expected_moves =
|
||||
Enum.sort([
|
||||
{4, 0},
|
||||
{4, 1},
|
||||
{4, 2},
|
||||
{4, 3},
|
||||
{4, 4},
|
||||
{4, 6},
|
||||
{4, 7},
|
||||
{0, 5},
|
||||
{1, 5},
|
||||
{2, 5},
|
||||
{3, 5},
|
||||
{5, 5},
|
||||
{6, 5},
|
||||
{7, 5},
|
||||
{0, 1},
|
||||
{1, 2},
|
||||
{2, 3},
|
||||
{3, 4},
|
||||
{5, 6},
|
||||
{6, 7},
|
||||
{2, 7},
|
||||
{3, 6},
|
||||
{5, 4},
|
||||
{6, 3},
|
||||
{7, 2}
|
||||
])
|
||||
|
||||
assert Enum.sort(moves) == expected_moves
|
||||
end
|
||||
|
||||
@ -20,15 +43,30 @@ defmodule Chess.Moves.Pieces.QueenTest do
|
||||
board = %{
|
||||
"0,0" => %{"type" => "queen", "colour" => "white"},
|
||||
"0,5" => %{"type" => "king", "colour" => "white"},
|
||||
"5,0" => %{"type" => "bishop", "colour" => "white"},
|
||||
"5,0" => %{"type" => "bishop", "colour" => "white"}
|
||||
}
|
||||
|
||||
moves = Moves.available(board, {0, 0})
|
||||
|
||||
expected_moves = Enum.sort([
|
||||
{0, 1}, {0, 2}, {0, 3}, {0, 4},
|
||||
{1, 0}, {2, 0}, {3, 0}, {4, 0},
|
||||
{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {6, 6}, {7, 7},
|
||||
])
|
||||
expected_moves =
|
||||
Enum.sort([
|
||||
{0, 1},
|
||||
{0, 2},
|
||||
{0, 3},
|
||||
{0, 4},
|
||||
{1, 0},
|
||||
{2, 0},
|
||||
{3, 0},
|
||||
{4, 0},
|
||||
{1, 1},
|
||||
{2, 2},
|
||||
{3, 3},
|
||||
{4, 4},
|
||||
{5, 5},
|
||||
{6, 6},
|
||||
{7, 7}
|
||||
])
|
||||
|
||||
assert Enum.sort(moves) == expected_moves
|
||||
end
|
||||
|
||||
@ -36,19 +74,36 @@ defmodule Chess.Moves.Pieces.QueenTest do
|
||||
board = %{
|
||||
"0,0" => %{"type" => "queen", "colour" => "white"},
|
||||
"0,5" => %{"type" => "knight", "colour" => "black"},
|
||||
"5,0" => %{"type" => "rook", "colour" => "black"},
|
||||
"5,0" => %{"type" => "rook", "colour" => "black"}
|
||||
}
|
||||
|
||||
moves = Moves.available(board, {0, 0})
|
||||
|
||||
expected_moves = Enum.sort([
|
||||
{0, 1}, {0, 2}, {0, 3}, {0, 4}, {0, 5},
|
||||
{1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0},
|
||||
{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {6, 6}, {7, 7},
|
||||
])
|
||||
expected_moves =
|
||||
Enum.sort([
|
||||
{0, 1},
|
||||
{0, 2},
|
||||
{0, 3},
|
||||
{0, 4},
|
||||
{0, 5},
|
||||
{1, 0},
|
||||
{2, 0},
|
||||
{3, 0},
|
||||
{4, 0},
|
||||
{5, 0},
|
||||
{1, 1},
|
||||
{2, 2},
|
||||
{3, 3},
|
||||
{4, 4},
|
||||
{5, 5},
|
||||
{6, 6},
|
||||
{7, 7}
|
||||
])
|
||||
|
||||
assert Enum.sort(moves) == expected_moves
|
||||
end
|
||||
|
||||
def board do
|
||||
Chess.Board.default
|
||||
Chess.Board.default()
|
||||
end
|
||||
end
|
||||
|
||||
@ -7,10 +7,24 @@ defmodule Chess.Moves.Pieces.RookTest do
|
||||
board = %{"4,5" => %{"type" => "rook", "colour" => "white"}}
|
||||
moves = Moves.available(board, {4, 5})
|
||||
|
||||
expected_moves = Enum.sort([
|
||||
{4, 0}, {4, 1}, {4, 2}, {4, 3}, {4, 4}, {4, 6}, {4, 7},
|
||||
{0, 5}, {1, 5}, {2, 5}, {3, 5}, {5, 5}, {6, 5}, {7, 5},
|
||||
])
|
||||
expected_moves =
|
||||
Enum.sort([
|
||||
{4, 0},
|
||||
{4, 1},
|
||||
{4, 2},
|
||||
{4, 3},
|
||||
{4, 4},
|
||||
{4, 6},
|
||||
{4, 7},
|
||||
{0, 5},
|
||||
{1, 5},
|
||||
{2, 5},
|
||||
{3, 5},
|
||||
{5, 5},
|
||||
{6, 5},
|
||||
{7, 5}
|
||||
])
|
||||
|
||||
assert Enum.sort(moves) == expected_moves
|
||||
end
|
||||
|
||||
@ -18,42 +32,81 @@ defmodule Chess.Moves.Pieces.RookTest do
|
||||
board = %{"0,0" => %{"type" => "rook", "colour" => "white"}}
|
||||
moves = Moves.available(board, {0, 0})
|
||||
|
||||
expected_moves = Enum.sort([
|
||||
{0, 1}, {0, 2}, {0, 3}, {0, 4}, {0, 5}, {0, 6}, {0, 7},
|
||||
{1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}, {7, 0},
|
||||
])
|
||||
expected_moves =
|
||||
Enum.sort([
|
||||
{0, 1},
|
||||
{0, 2},
|
||||
{0, 3},
|
||||
{0, 4},
|
||||
{0, 5},
|
||||
{0, 6},
|
||||
{0, 7},
|
||||
{1, 0},
|
||||
{2, 0},
|
||||
{3, 0},
|
||||
{4, 0},
|
||||
{5, 0},
|
||||
{6, 0},
|
||||
{7, 0}
|
||||
])
|
||||
|
||||
assert Enum.sort(moves) == expected_moves
|
||||
end
|
||||
|
||||
test "rooks are blocked by other pieces of the same colour" do
|
||||
board = %{
|
||||
"0,0" => %{"type" => "rook", "colour" => "white"},
|
||||
"0,5" => %{"type" => "king", "colour" => "white"},
|
||||
"0,5" => %{"type" => "king", "colour" => "white"}
|
||||
}
|
||||
|
||||
moves = Moves.available(board, {0, 0})
|
||||
|
||||
expected_moves = Enum.sort([
|
||||
{0, 1}, {0, 2}, {0, 3}, {0, 4},
|
||||
{1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}, {7, 0},
|
||||
])
|
||||
expected_moves =
|
||||
Enum.sort([
|
||||
{0, 1},
|
||||
{0, 2},
|
||||
{0, 3},
|
||||
{0, 4},
|
||||
{1, 0},
|
||||
{2, 0},
|
||||
{3, 0},
|
||||
{4, 0},
|
||||
{5, 0},
|
||||
{6, 0},
|
||||
{7, 0}
|
||||
])
|
||||
|
||||
assert Enum.sort(moves) == expected_moves
|
||||
end
|
||||
|
||||
test "rooks can take an opponents piece" do
|
||||
board = %{
|
||||
"0,0" => %{"type" => "rook", "colour" => "white"},
|
||||
"0,5" => %{"type" => "knight", "colour" => "black"},
|
||||
"0,5" => %{"type" => "knight", "colour" => "black"}
|
||||
}
|
||||
|
||||
moves = Moves.available(board, {0, 0})
|
||||
|
||||
expected_moves = Enum.sort([
|
||||
{0, 1}, {0, 2}, {0, 3}, {0, 4}, {0, 5},
|
||||
{1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}, {7, 0},
|
||||
])
|
||||
expected_moves =
|
||||
Enum.sort([
|
||||
{0, 1},
|
||||
{0, 2},
|
||||
{0, 3},
|
||||
{0, 4},
|
||||
{0, 5},
|
||||
{1, 0},
|
||||
{2, 0},
|
||||
{3, 0},
|
||||
{4, 0},
|
||||
{5, 0},
|
||||
{6, 0},
|
||||
{7, 0}
|
||||
])
|
||||
|
||||
assert Enum.sort(moves) == expected_moves
|
||||
end
|
||||
|
||||
def board do
|
||||
Chess.Board.default
|
||||
Chess.Board.default()
|
||||
end
|
||||
end
|
||||
|
||||
@ -7,51 +7,59 @@ defmodule Chess.Repo.QueriesTest do
|
||||
|
||||
describe "opponents" do
|
||||
test "it finds a user on a partial name match" do
|
||||
user = insert(:user, %{
|
||||
name: "Link",
|
||||
email: "link@hyrule.com",
|
||||
password: "ilovezelda"
|
||||
})
|
||||
opponent = insert(:user, %{
|
||||
name: "Princess Zelda",
|
||||
email: "zelda@hyrule.com",
|
||||
password: "ganonsucks"
|
||||
})
|
||||
user =
|
||||
insert(:user, %{
|
||||
name: "Link",
|
||||
email: "link@hyrule.com",
|
||||
password: "ilovezelda"
|
||||
})
|
||||
|
||||
opponent =
|
||||
insert(:user, %{
|
||||
name: "Princess Zelda",
|
||||
email: "zelda@hyrule.com",
|
||||
password: "ganonsucks"
|
||||
})
|
||||
|
||||
result =
|
||||
user
|
||||
|> Queries.opponents("zelda")
|
||||
|> Repo.one
|
||||
|> Repo.one()
|
||||
|
||||
assert result.id == opponent.id
|
||||
end
|
||||
|
||||
test "it finds a user on a complete email match" do
|
||||
user = insert(:user, %{
|
||||
name: "Link",
|
||||
email: "link@hyrule.com",
|
||||
password: "ilovezelda"
|
||||
})
|
||||
opponent = insert(:user, %{
|
||||
name: "Princess Zelda",
|
||||
email: "zelda@hyrule.com",
|
||||
password: "ganonsucks"
|
||||
})
|
||||
user =
|
||||
insert(:user, %{
|
||||
name: "Link",
|
||||
email: "link@hyrule.com",
|
||||
password: "ilovezelda"
|
||||
})
|
||||
|
||||
opponent =
|
||||
insert(:user, %{
|
||||
name: "Princess Zelda",
|
||||
email: "zelda@hyrule.com",
|
||||
password: "ganonsucks"
|
||||
})
|
||||
|
||||
result =
|
||||
user
|
||||
|> Queries.opponents("zelda@hyrule.com")
|
||||
|> Repo.one
|
||||
|> Repo.one()
|
||||
|
||||
assert result.id == opponent.id
|
||||
end
|
||||
|
||||
test "it does not find a user on a partial email" do
|
||||
user = insert(:user, %{
|
||||
name: "Link",
|
||||
email: "link@hyrule.com",
|
||||
password: "ilovezelda"
|
||||
})
|
||||
user =
|
||||
insert(:user, %{
|
||||
name: "Link",
|
||||
email: "link@hyrule.com",
|
||||
password: "ilovezelda"
|
||||
})
|
||||
|
||||
insert(:user, %{
|
||||
name: "Princess Zelda",
|
||||
email: "zelda@hyrule.com",
|
||||
@ -61,7 +69,7 @@ defmodule Chess.Repo.QueriesTest do
|
||||
result =
|
||||
user
|
||||
|> Queries.opponents("hyrule")
|
||||
|> Repo.one
|
||||
|> Repo.one()
|
||||
|
||||
assert result == nil
|
||||
end
|
||||
|
||||
@ -20,6 +20,7 @@ defmodule Chess.Store.GameTest do
|
||||
opponent_id: opponent.id,
|
||||
turn: "white"
|
||||
}
|
||||
|
||||
changeset = Game.changeset(%Game{}, attrs)
|
||||
|
||||
assert changeset.valid?
|
||||
@ -33,6 +34,7 @@ defmodule Chess.Store.GameTest do
|
||||
opponent_id: 2,
|
||||
turn: "white"
|
||||
}
|
||||
|
||||
changeset = Game.changeset(%Game{}, attrs)
|
||||
|
||||
assert changeset.valid?
|
||||
@ -81,18 +83,20 @@ defmodule Chess.Store.GameTest do
|
||||
user = insert(:user)
|
||||
opponent = insert(:opponent)
|
||||
|
||||
game = insert(:game, %{
|
||||
board: Board.default,
|
||||
user_id: user.id,
|
||||
opponent_id: opponent.id,
|
||||
})
|
||||
game =
|
||||
insert(:game, %{
|
||||
board: Board.default(),
|
||||
user_id: user.id,
|
||||
opponent_id: opponent.id
|
||||
})
|
||||
|
||||
move_params = %{"from" => [4, 1], "to" => [4, 3]}
|
||||
move_params = %{from: {4, 1}, to: {4, 3}}
|
||||
|
||||
changeset = Game.move_changeset(
|
||||
game,
|
||||
Board.move_piece(game.board, move_params)
|
||||
)
|
||||
changeset =
|
||||
Game.move_changeset(
|
||||
game,
|
||||
Board.move_piece(game.board, move_params)
|
||||
)
|
||||
|
||||
assert {:ok, new_game} = Repo.update(changeset)
|
||||
assert new_game.turn == "black"
|
||||
|
||||
@ -14,29 +14,32 @@ defmodule Chess.Store.MoveTest do
|
||||
user = insert(:user)
|
||||
opponent = insert(:opponent)
|
||||
|
||||
game = insert(:game, %{
|
||||
board: Board.default,
|
||||
user_id: user.id,
|
||||
opponent_id: opponent.id,
|
||||
})
|
||||
game =
|
||||
insert(:game, %{
|
||||
board: Board.default(),
|
||||
user_id: user.id,
|
||||
opponent_id: opponent.id
|
||||
})
|
||||
|
||||
changeset = Move.changeset(%Move{}, %{
|
||||
game_id: game.id,
|
||||
from: %{"file" => 4, "rank" => 1},
|
||||
to: %{"file" => 4, "rank" => 3},
|
||||
piece: %{"type" => "pawn", "colour" => "white"},
|
||||
})
|
||||
changeset =
|
||||
Move.changeset(%Move{}, %{
|
||||
game_id: game.id,
|
||||
from: %{"file" => 4, "rank" => 1},
|
||||
to: %{"file" => 4, "rank" => 3},
|
||||
piece: %{"type" => "pawn", "colour" => "white"}
|
||||
})
|
||||
|
||||
assert changeset.valid?
|
||||
assert {:ok, _move} = Repo.insert(changeset)
|
||||
end
|
||||
|
||||
test "move is invalid without a game" do
|
||||
changeset = Move.changeset(%Move{}, %{
|
||||
from: %{"file" => 4, "rank" => 1},
|
||||
to: %{"file" => 4, "rank" => 3},
|
||||
piece: %{"type" => "pawn", "colour" => "white"},
|
||||
})
|
||||
changeset =
|
||||
Move.changeset(%Move{}, %{
|
||||
from: %{"file" => 4, "rank" => 1},
|
||||
to: %{"file" => 4, "rank" => 3},
|
||||
piece: %{"type" => "pawn", "colour" => "white"}
|
||||
})
|
||||
|
||||
refute changeset.valid?
|
||||
end
|
||||
@ -45,16 +48,18 @@ defmodule Chess.Store.MoveTest do
|
||||
user = insert(:user)
|
||||
opponent = insert(:opponent)
|
||||
|
||||
game = insert(:game, %{
|
||||
board: Board.default,
|
||||
user_id: user.id,
|
||||
opponent_id: opponent.id,
|
||||
})
|
||||
game =
|
||||
insert(:game, %{
|
||||
board: Board.default(),
|
||||
user_id: user.id,
|
||||
opponent_id: opponent.id
|
||||
})
|
||||
|
||||
changeset = Move.changeset(%Move{}, %{
|
||||
game_id: game.id,
|
||||
piece: %{"type" => "pawn", "colour" => "white"},
|
||||
})
|
||||
changeset =
|
||||
Move.changeset(%Move{}, %{
|
||||
game_id: game.id,
|
||||
piece: %{"type" => "pawn", "colour" => "white"}
|
||||
})
|
||||
|
||||
refute changeset.valid?
|
||||
end
|
||||
@ -63,17 +68,19 @@ defmodule Chess.Store.MoveTest do
|
||||
user = insert(:user)
|
||||
opponent = insert(:opponent)
|
||||
|
||||
game = insert(:game, %{
|
||||
board: Board.default,
|
||||
user_id: user.id,
|
||||
opponent_id: opponent.id,
|
||||
})
|
||||
game =
|
||||
insert(:game, %{
|
||||
board: Board.default(),
|
||||
user_id: user.id,
|
||||
opponent_id: opponent.id
|
||||
})
|
||||
|
||||
changeset = Move.changeset(%Move{}, %{
|
||||
game_id: game.id,
|
||||
from: %{"file" => 4, "rank" => 1},
|
||||
to: %{"file" => 4, "rank" => 3},
|
||||
})
|
||||
changeset =
|
||||
Move.changeset(%Move{}, %{
|
||||
game_id: game.id,
|
||||
from: %{"file" => 4, "rank" => 1},
|
||||
to: %{"file" => 4, "rank" => 3}
|
||||
})
|
||||
|
||||
refute changeset.valid?
|
||||
end
|
||||
@ -83,16 +90,16 @@ defmodule Chess.Store.MoveTest do
|
||||
piece: %{"type" => "pawn", "colour" => "white"},
|
||||
piece_captured: %{"type" => "pawn", "colour" => "black"},
|
||||
from: %{"file" => 4, "rank" => 1},
|
||||
to: %{"file" => 4, "rank" => 3},
|
||||
to: %{"file" => 4, "rank" => 3}
|
||||
}
|
||||
|
||||
assert Move.transform(move) == %{
|
||||
id: nil,
|
||||
piece: %{"type" => "pawn", "colour" => "white"},
|
||||
piece_captured: %{"type" => "pawn", "colour" => "black"},
|
||||
from: "e2",
|
||||
to: "e4"
|
||||
}
|
||||
id: nil,
|
||||
piece: %{"type" => "pawn", "colour" => "white"},
|
||||
piece_captured: %{"type" => "pawn", "colour" => "black"},
|
||||
from: "e2",
|
||||
to: "e4"
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -8,10 +8,12 @@ defmodule ChessWeb.GameChannelTest do
|
||||
|
||||
test "assigns game_id to the socket after join" do
|
||||
user = insert(:user)
|
||||
game = insert(:game, %{
|
||||
user_id: user.id,
|
||||
opponent_id: insert(:opponent).id
|
||||
})
|
||||
|
||||
game =
|
||||
insert(:game, %{
|
||||
user_id: user.id,
|
||||
opponent_id: insert(:opponent).id
|
||||
})
|
||||
|
||||
token = Phoenix.Token.sign(@endpoint, "game socket", user.id)
|
||||
{:ok, socket} = connect(UserSocket, %{"token" => token})
|
||||
@ -24,20 +26,22 @@ defmodule ChessWeb.GameChannelTest do
|
||||
test "returns the game state after join" do
|
||||
user = insert(:user)
|
||||
opponent = insert(:opponent, %{name: "Daruk"})
|
||||
game = insert(:game, %{
|
||||
user_id: user.id,
|
||||
opponent_id: opponent.id
|
||||
})
|
||||
|
||||
game =
|
||||
insert(:game, %{
|
||||
user_id: user.id,
|
||||
opponent_id: opponent.id
|
||||
})
|
||||
|
||||
token = Phoenix.Token.sign(@endpoint, "game socket", user.id)
|
||||
{:ok, socket} = connect(UserSocket, %{"token" => token})
|
||||
|
||||
{:ok, _, _} = subscribe_and_join(socket, "game:#{game.id}", %{})
|
||||
|
||||
assert_push "game:update", %{
|
||||
assert_push("game:update", %{
|
||||
player: "white",
|
||||
opponent: "Daruk",
|
||||
turn: "white",
|
||||
}
|
||||
turn: "white"
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
@ -60,7 +60,7 @@ defmodule ChessWeb.GameControllerTest do
|
||||
|> login(user)
|
||||
|> get(game_path(conn, :show, game))
|
||||
|
||||
assert html_response(conn, 200) =~ "<div id=\"game\" data-game-id=\"#{game.id}\">"
|
||||
assert html_response(conn, 200) =~ "<div data-game-id=\"#{game.id}\">"
|
||||
end
|
||||
|
||||
test "does not show a game if the user is not a player", %{conn: conn} do
|
||||
@ -74,25 +74,25 @@ defmodule ChessWeb.GameControllerTest do
|
||||
conn
|
||||
|> login(other_user)
|
||||
|
||||
assert_error_sent 404, fn ->
|
||||
get conn, game_path(conn, :show, game.id)
|
||||
end
|
||||
assert_error_sent(404, fn ->
|
||||
get(conn, game_path(conn, :show, game.id))
|
||||
end)
|
||||
end
|
||||
|
||||
test "renders page not found when id is nonexistent", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
conn = login(conn, user)
|
||||
|
||||
assert_error_sent 404, fn ->
|
||||
get conn, game_path(conn, :show, -1)
|
||||
end
|
||||
assert_error_sent(404, fn ->
|
||||
get(conn, game_path(conn, :show, -1))
|
||||
end)
|
||||
end
|
||||
|
||||
test "deletes game", %{conn: conn} do
|
||||
game = Repo.insert! %Game{}
|
||||
game = Repo.insert!(%Game{})
|
||||
user = insert(:user)
|
||||
conn = login(conn, user)
|
||||
conn = delete conn, game_path(conn, :delete, game)
|
||||
conn = delete(conn, game_path(conn, :delete, game))
|
||||
assert redirected_to(conn) == game_path(conn, :index)
|
||||
refute Repo.get(Game, game.id)
|
||||
end
|
||||
|
||||
@ -2,7 +2,7 @@ defmodule ChessWeb.SessionControllerTest do
|
||||
use ChessWeb.ConnCase
|
||||
|
||||
test "shows log in form", %{conn: conn} do
|
||||
conn = get conn, session_path(conn, :new)
|
||||
conn = get(conn, session_path(conn, :new))
|
||||
assert html_response(conn, 200) =~ "Log in"
|
||||
end
|
||||
end
|
||||
|
||||
@ -40,22 +40,24 @@ defmodule Chess.Features.GamesTest do
|
||||
|> click(button("Create game"))
|
||||
|
||||
session
|
||||
|> assert_has(
|
||||
css(".help-block", text: "can't be blank")
|
||||
)
|
||||
|> assert_has(css(".help-block", text: "can't be blank"))
|
||||
end
|
||||
|
||||
test "can only see own games", %{session: session} do
|
||||
opponent = insert(:user, %{
|
||||
name: "Urbosa",
|
||||
email: "urbosa@gerudo.town",
|
||||
password: "gerudoqueen"
|
||||
})
|
||||
user = insert(:user, %{
|
||||
name: "Zelda",
|
||||
email: "zelda@hyrule.com",
|
||||
password: "ganonsucks"
|
||||
})
|
||||
opponent =
|
||||
insert(:user, %{
|
||||
name: "Urbosa",
|
||||
email: "urbosa@gerudo.town",
|
||||
password: "gerudoqueen"
|
||||
})
|
||||
|
||||
user =
|
||||
insert(:user, %{
|
||||
name: "Zelda",
|
||||
email: "zelda@hyrule.com",
|
||||
password: "ganonsucks"
|
||||
})
|
||||
|
||||
insert(:game, %{user_id: user.id, opponent_id: opponent.id})
|
||||
|
||||
session
|
||||
@ -72,16 +74,20 @@ defmodule Chess.Features.GamesTest do
|
||||
end
|
||||
|
||||
test "can see games as an opponent", %{session: session} do
|
||||
opponent = insert(:user, %{
|
||||
name: "Urbosa",
|
||||
email: "urbosa@gerudo.town",
|
||||
password: "gerudoqueen"
|
||||
})
|
||||
user = insert(:user, %{
|
||||
name: "Zelda",
|
||||
email: "zelda@hyrule.com",
|
||||
password: "ganonsucks"
|
||||
})
|
||||
opponent =
|
||||
insert(:user, %{
|
||||
name: "Urbosa",
|
||||
email: "urbosa@gerudo.town",
|
||||
password: "gerudoqueen"
|
||||
})
|
||||
|
||||
user =
|
||||
insert(:user, %{
|
||||
name: "Zelda",
|
||||
email: "zelda@hyrule.com",
|
||||
password: "ganonsucks"
|
||||
})
|
||||
|
||||
insert(:game, %{user_id: user.id, opponent_id: opponent.id})
|
||||
|
||||
session
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user