diff --git a/.env.example b/.env.example index cd3aa94..34f4d49 100644 --- a/.env.example +++ b/.env.example @@ -7,4 +7,6 @@ BASIC_USERNAME=root BASIC_PASSWORD=secret SENTRY_ENABLED=true SENTRY_PROJECT_ID=projectId -SENTRY_KEY=key \ No newline at end of file +SENTRY_KEY=key +FACEBOOK_APP_ID=appId +GOOGLE_CLIENT_ID=clientId \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ac2f6b8..3b8376b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,58 @@ # ๐Ÿ“ˆ LIST OF CHANGES FOR WEREWOLVES ASSISTANT API +## 0.11.0 (2021-04-30) + +### ๐Ÿš€ New features + +* [#137](https://github.com/antoinezanardi/werewolves-assistant-api/issues/137) - `position` for each player. +* [#138](https://github.com/antoinezanardi/werewolves-assistant-api/issues/138) - Facebook authentication. +* [#139](https://github.com/antoinezanardi/werewolves-assistant-api/issues/139) - Google authentication. +* [#141](https://github.com/antoinezanardi/werewolves-assistant-api/issues/141) - Fox role. +* [#142](https://github.com/antoinezanardi/werewolves-assistant-api/issues/142) - Bear Tamer role. +* [#143](https://github.com/antoinezanardi/werewolves-assistant-api/issues/143) - Hidden game repartition option. +* [#144](https://github.com/antoinezanardi/werewolves-assistant-api/issues/144) - Role revealed on death option. +* [#145](https://github.com/antoinezanardi/werewolves-assistant-api/issues/145) - Seer see exact role option. +* [#146](https://github.com/antoinezanardi/werewolves-assistant-api/issues/146) - Guard can protect twice option. +* [#147](https://github.com/antoinezanardi/werewolves-assistant-api/issues/147) - Fox not powerless if he misses a werewolf option. +* [#148](https://github.com/antoinezanardi/werewolves-assistant-api/issues/148) - Bear Tamer growls if he is infected option. +* [#149](https://github.com/antoinezanardi/werewolves-assistant-api/issues/149) - Number of vote requests for Stuttering Judge option. +* [#150](https://github.com/antoinezanardi/werewolves-assistant-api/issues/150) - Time for the Sheriff to be elected option. +* [#151](https://github.com/antoinezanardi/werewolves-assistant-api/issues/151) - Thief not obliged to choose between werewolves cards option. +* [#152](https://github.com/antoinezanardi/werewolves-assistant-api/issues/152) - Wild Child transformation is revealed option. +* [#153](https://github.com/antoinezanardi/werewolves-assistant-api/issues/153) - Dog Wolf chosen side is revealed option. +* [#154](https://github.com/antoinezanardi/werewolves-assistant-api/issues/154) - Big Bad Wolf not powerless if one werewolf dies option. +* [#155](https://github.com/antoinezanardi/werewolves-assistant-api/issues/155) - White Werewolf waking up interval option. +* [#156](https://github.com/antoinezanardi/werewolves-assistant-api/issues/156) - Number of charmed people per night by Pied Piper option. +* [#157](https://github.com/antoinezanardi/werewolves-assistant-api/issues/157) - Pied Piper not powerless if infected option. +* [#158](https://github.com/antoinezanardi/werewolves-assistant-api/issues/158) - Lives count for the Ancient option. +* [#159](https://github.com/antoinezanardi/werewolves-assistant-api/issues/159) - Ancient doesn't make all villagers powerless if murdered from them option. +* [#160](https://github.com/antoinezanardi/werewolves-assistant-api/issues/160) - Number of additional cards for Thief option. +* [#161](https://github.com/antoinezanardi/werewolves-assistant-api/issues/161) - Rusty Sword Knight role. + +### ๐ŸŒŸ Enhancements + +* [#162](https://github.com/antoinezanardi/werewolves-assistant-api/issues/162) - Role types. + +### โ™ป๏ธ Refactoring + +* [#140](https://github.com/antoinezanardi/werewolves-assistant-api/issues/140) - Extend token's lifetime to infinity. + +### ๐Ÿ“ฆ Packages + +* `axios` installed with version `0.21.1`. +* `@sentry/node` updated to version `6.3.5`. +* `apidoc` updated to version `0.26.0`. +* `chai` updated to version `4.3.4`. +* `eslint` updated to version `7.25.0`. +* `express-validator` updated to version `6.10.1`. +* `migrate-mongo` updated to version `8.2.2`. +* `mocha` updated to version `8.3.2`. +* `mongoose` updated to version `5.12.6`. +* `qs` updated to version `6.10.1`. +* `validator` updated to version `13.6.0`. + +--- + ## 0.10.0 (2021-03-03) ### ๐Ÿš€ New features diff --git a/README.md b/README.md index 197bc45..89d9c03 100644 --- a/README.md +++ b/README.md @@ -38,13 +38,13 @@ Two versions are available for testing this API: ๐Ÿ”ง **Sandbox API** _(Base URL: https://sandbox.werewolves-assistant-api.antoinezanardi.fr)_ -**Sandbox API** may contains some bugs and unexpected behaviors as its purpose is to test new features before deploying on **main API**. +**Sandbox API** may contain some bugs and unexpected behaviors as its purpose is to test new features before deploying on **main API**. Both APIs are running on a server with the following configuration: - **OS**: `Debian GNU/Linux 10 (buster)` -- **NodeJS**: `v14.15.4` -- **NPM**: `v7.5.2` -- **MongoDB shell version**: `v4.4.3` +- **NodeJS**: `v14.16.1` +- **NPM**: `v6.14.12` +- **MongoDB shell version**: `v4.4.5` The MongoDB database is protected under username and password authentication. @@ -52,7 +52,7 @@ The MongoDB database is protected under username and password authentication. ## ๐Ÿƒ Roles available -On this current version [![GitHub release](https://img.shields.io/github/release/antoinezanardi/werewolves-assistant-api.svg)](https://GitHub.com/antoinezanardi/werewolves-assistant-api/releases/), **24 different roles** are available to play: +On this current version [![GitHub release](https://img.shields.io/github/release/antoinezanardi/werewolves-assistant-api.svg)](https://GitHub.com/antoinezanardi/werewolves-assistant-api/releases/), **27 different roles** are available to play: - ** The Werewolf** - ** The Big Bad Wolf** @@ -71,7 +71,10 @@ On this current version [![GitHub release](https://img.shields.io/github/release - ** The Idiot** - ** The Two Sisters** - ** The Three Brothers** +- ** The Fox** +- ** The Bear Tamer** - ** The Stuttering Judge** +- ** The Rusty Sword Knight** - ** The Wild Child** - ** The Dog-Wolf** - ** The Thief** @@ -137,6 +140,10 @@ Complete list of all enabled rules is available in the **[.eslintrc.js file](htt - _**Not required**_ * **SENTRY_KEY**: Sentry secret key. - _**Not required**_ + * **FACEBOOK_APP_ID**: Facebook application ID if you want to enable Facebook authentication. + - _**Not Required**_ + * **GOOGLE_CLIENT_ID**: Google client ID if you want to enable Google authentication. + - _**Not Required**_ ## ๐Ÿ”Œ Let's go diff --git a/app.js b/app.js index abb9722..6932b16 100644 --- a/app.js +++ b/app.js @@ -8,6 +8,7 @@ const cors = require("cors"); const Config = require("./config"); const { sendError, generateError } = require("./src/helpers/functions/Error"); const { connect: connectDatabase } = require("./src/helpers/functions/Mongoose"); +const routes = require("./src/routes"); if (Config.sentry.enabled) { const Sentry = require("@sentry/node"); @@ -15,7 +16,9 @@ if (Config.sentry.enabled) { } console.log("Starting the application..."); connectDatabase().then(() => { - console.log("โœ… Connected to database."); + if (Config.app.nodeEnv !== "test") { + console.log("โœ… Connected to database."); + } app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); app.use(cors({ origin: "*" })); @@ -27,13 +30,13 @@ connectDatabase().then(() => { next(); } }); - const routes = require("./src/routes"); routes(app); app.listen(Config.app.port); - console.log(`${bold("๐Ÿบ Werewolves Assistant API")} server started on port ${bold.blue(Config.app.port)} and running on database ${bold.green(Config.db.name)}.`); - console.log(`${bold("๐Ÿ“š API Documentation:")} http://localhost:${Config.app.port}/apidoc`); + if (Config.app.nodeEnv !== "test") { + console.log(`${bold("๐Ÿบ Werewolves Assistant API")} server started on port ${bold.blue(Config.app.port)} and running on database ${bold.green(Config.db.name)}.`); + console.log(`${bold("๐Ÿ“š API Documentation:")} http://localhost:${Config.app.port}/apidoc`); + } app.emit("ready"); - app.prototype.isReady = true; }); module.exports = app; \ No newline at end of file diff --git a/config/apidoc/apidoc.json b/config/apidoc/apidoc.json index f1b480c..46b15a2 100755 --- a/config/apidoc/apidoc.json +++ b/config/apidoc/apidoc.json @@ -1,6 +1,6 @@ { "name": "\uD83D\uDC3A Werewolves Assistant API", - "version": "0.10.0", + "version": "0.11.0", "description": "Werewolves Assistant API provides over HTTP requests a way of manage The Werewolves of Millers Hollow games in order to help the game master in his task.", "header": { "title": "Classes", diff --git a/config/apidoc/footer.md b/config/apidoc/footer.md index 4d45c93..605d710 100644 --- a/config/apidoc/footer.md +++ b/config/apidoc/footer.md @@ -35,62 +35,66 @@ Among all players, groups are defined depending on players properties. Some grou Each player in a game has a role. It defines the original player's side and powers. -| Role       | Card | [Side](#player-sides) | Limits       | Description | -|:-----------------------------------------------:|:-------------------------------------------------------------------------------------------------------------:|:------------------------:|:------------------------------------------------------:|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| ๐Ÿบ
werewolf | | werewolves | **Max in game**: 39 | Each night, his group eats a villager chosen by the majority. | -| ๐Ÿบ
big-bad-wolf | | werewolves | **Max in game**: 1 | Each night until no player in the `werewolves` side has died, he eats another villager all by himself after the `werewolves` turn. | -| ๐Ÿบ
vile-father-of-wolves | | werewolves | **Max in game**: 1 | Once in the game, he infects the victim `eaten` by the werewolves and therefore, the infected villager immediately joins the `werewolves`. The victim keeps his original villager powers but must win with the other `werewolves`. | -| ๐Ÿบ
white-werewolf | | werewolves | **Max in game**: 1 | Every other night, starting the first night, he wakes up and can eat another player in the `werewolves` side if he wants to. He is the last werewolf, we doesn't wake up anymore. He is the last survivor, he is the only winner of the game. | -| ๐Ÿง‘โ€๐ŸŒพ
villager | | villagers | **Max in game**: 39 | Has no powers, can only count on his speech skills. | -| ๐Ÿง‘โ€๐ŸŒพ๐Ÿง‘โ€๐ŸŒพ
villager-villager | | villagers | **Max in game**: 1 | Like the normal villager, has no power but on the two faces of his role card, there is the illustration of a simple villager. So, everybody knows that it's a simple villager. | -| ๐Ÿ”ฎ
seer | | villagers | **Max in game**: 1 | Each night, she sees the role of the player she wants. | -| ๐Ÿน
cupid | | villagers | **Max in game**: 1 | The first night, he chooses two players that fall instantly in love (he can choose himself). Next, the lovers wake up to meet each other and must win together. | -| ๐Ÿช„
๏ธโ€witch | | villagers | **Max in game**: 1 | She has one life potion which prevents from being eaten by werewolves and a death potion which instantly kills. She can only use each one once in the game. | -| ๐Ÿ”ซ
hunter | | villagers | **Max in game**: 1 | If he dies, he shoots a victim to take his revenge. He can't kill himself. | -| ๐Ÿ‘ง
little-girl | | villagers | **Max in game**: 1 | She can slightly open her eyes during werewolves turn to spot some of them. Even if the guard protects her, she will die by the werewolves if she is chosen by them. | -| ๐Ÿ›ก๏ธ
guard | | villagers | **Max in game**: 1 | Each night, he protects the player he wants (including himself). He can't protect the same player twice in a row. | -| ๐Ÿ‘ด๐Ÿผ
ancient | | villagers | **Max in game**: 1 | If he dies from the `werewolves`, he has another life. But if he dies from the `death-potion`, the `vote` or the `hunter`, he dies and all who started the game in the `villagers` side will loose their powers. | -| ๐Ÿ
scapegoat | | villagers | **Max in game**: 1 | If there is a tie in votes, even if the `sheriff` is in the game or he is not in the tie, he will be the vote target. If so, he will choose who won't vote during the next day. | -| ๐Ÿคช
idiot | | villagers | **Max in game**: 1 | If he is nominated by the `vote`, he doesn't die but his role is revealed and he can't no more vote. If he dies and was the `sheriff`, he won't delegate the role and there won't be no more `sheriff` for the rest of the game. | -| ๐Ÿ‘ญ
two-sisters | | villagers | **Min in game if chosen**: 2
**Max in game**: 2 | The first night, they meet each other and, therefore, know that they can trust themselves. Depending on game options, they wake up every X night(s). (Default is `2`). | -| ๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ฆ
three-brothers | | villagers | **Min in game if chosen**: 3
**Max in game**: 3 | The first night, they meet each other and, therefore, know that they can trust themselves. Depending on game options, they wake up every X night(s). (Default is `2`). | -| โš–๏ธ
stuttering-judge | | villagers | **Max in game**: 1 | The first night, he chooses a sign with the game master. After a vote, once in the game, he can do the sign to the game master and this will cause another vote immediately. | -| ๐Ÿ’
wild-child | | villagers / werewolves | **Max in game**: 1 | The first night, he chooses a model among the other players. If this model dies during the game, the wild child changes his side to `werewolves` and must win with them. | -| ๐Ÿ•
dog-wolf | | villagers / werewolves | **Max in game**: 1 | The first night, he chooses a side between `villagers` and `werewolves`. Then, he must win with the chosen side. Other players don't know what he chose. | -| ๐Ÿฆน
thief | | villagers / werewolves | **Max in game**: 1 | If he's in the party, 2 additional role cards must be added. The first night, he can choose between one of the two cards or keep his original role. If the two cards are in the `werewolves` side, he must choose one of them. | -| ๐Ÿ‘ผ
angel | | villagers | **Max in game**: 1 | He must win alone. When he's in the party, the game starts with a `vote` (after `sheriff` election). If he dies from the first `votes` or by the `werewolves` the first night, he wins the game alone. | -| ๐Ÿ“ฃ
pied-piper | | villagers | **Max in game**: 1 | He must win alone. Each night, he charms two players, except himself. If all alive players are charmed, he wins the game. If he's infected by the `vile-father-of-wolves`, he looses his ability to charm. | -| ๐Ÿชถ
raven | | villagers | **Max in game**: 1 | Each night, he can mark someone (including himself) if he wants to. The next phase (during the `day`), the marked player will have two votes against himself for the next vote only. | +| Role       | Card | [Side](#player-sides) | Limits       | Description | +|:-----------------------------------------------:|:-------------------------------------------------------------------------------------------------------------:|:------------------------:|:------------------------------------------------------:|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| ๐Ÿบ
werewolf | | werewolves | **Max in game**: 39 | Each night, his group eats a villager chosen by the majority. | +| ๐Ÿบ
big-bad-wolf | | werewolves | **Max in game**: 1 | Each night until no player in the `werewolves` side has died, he eats another villager all by himself after the `werewolves` turn. | +| ๐Ÿบ
vile-father-of-wolves | | werewolves | **Max in game**: 1 | Once in the game, he infects the victim `eaten` by the werewolves and therefore, the infected villager immediately joins the `werewolves`. The victim keeps his original villager powers but must win with the other `werewolves`. | +| ๐Ÿบ
white-werewolf | | werewolves | **Max in game**: 1 | Every other night, starting the first night, he wakes up and can eat another player in the `werewolves` side if he wants to. He is the last werewolf, we doesn't wake up anymore. He is the last survivor, he is the only winner of the game. | +| ๐Ÿง‘โ€๐ŸŒพ
villager | | villagers | **Max in game**: 39 | Has no powers, can only count on his speech skills. | +| ๐Ÿง‘โ€๐ŸŒพ๐Ÿง‘โ€๐ŸŒพ
villager-villager | | villagers | **Max in game**: 1 | Like the normal villager, has no power but on the two faces of his role card, there is the illustration of a simple villager. So, everybody knows that it's a simple villager. | +| ๐Ÿ”ฎ
seer | | villagers | **Max in game**: 1 | Each night, she sees the role of the player she wants. | +| ๐Ÿน
cupid | | villagers | **Max in game**: 1 | The first night, he chooses two players that fall instantly in love (he can choose himself). Next, the lovers wake up to meet each other and must win together. | +| ๐Ÿช„
๏ธโ€witch | | villagers | **Max in game**: 1 | She has one life potion which prevents from being eaten by werewolves and a death potion which instantly kills. She can only use each one once in the game. | +| ๐Ÿ”ซ
hunter | | villagers | **Max in game**: 1 | If he dies, he shoots a victim to take his revenge. He can't kill himself. | +| ๐Ÿ‘ง
little-girl | | villagers | **Max in game**: 1 | She can slightly open her eyes during werewolves turn to spot some of them. Even if the guard protects her, she will die by the werewolves if she is chosen by them. | +| ๐Ÿ›ก๏ธ
guard | | villagers | **Max in game**: 1 | Each night, he protects the player he wants (including himself). He can't protect the same player twice in a row. | +| ๐Ÿ‘ด๐Ÿผ
ancient | | villagers | **Max in game**: 1 | If he dies from the `werewolves`, he has another life. But if he dies from the `death-potion`, the `vote` or the `hunter`, he dies and all who started the game in the `villagers` side will loose their powers. | +| ๐Ÿ
scapegoat | | villagers | **Max in game**: 1 | If there is a tie in votes, even if the `sheriff` is in the game or he is not in the tie, he will be the vote target. If so, he will choose who won't vote during the next day. | +| ๐Ÿคช
idiot | | villagers | **Max in game**: 1 | If he is nominated by the `vote`, he doesn't die but his role is revealed and he can't no more vote. If he dies and was the `sheriff`, he won't delegate the role and there won't be no more `sheriff` for the rest of the game. | +| ๐Ÿ‘ญ
two-sisters | | villagers | **Min in game if chosen**: 2
**Max in game**: 2 | The first night, they meet each other and, therefore, know that they can trust themselves. Depending on game options, they wake up every X night(s). (Default is `2`). | +| ๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ฆ
three-brothers | | villagers | **Min in game if chosen**: 3
**Max in game**: 3 | The first night, they meet each other and, therefore, know that they can trust themselves. Depending on game options, they wake up every X night(s). (Default is `2`). | +| ๐ŸฆŠ
fox | | villagers | **Max in game**: 1 | Every night, he chooses a player. If this player or one of his neighbors is from the `werewolves` side, the game master will make a positive sign to the `fox. If none is a `werewolf`, the `fox` looses his power for the rest of the game. He can skip his turn if he wants to. | +| ๐Ÿป
bear-tamer | | villagers | **Max in game**: 1 | Every time the day rises, if one of his alive neighbors is from the `werewolves` side, he growls (he has the `growls` attribute). If he is `infected`, he will growl everyday. | +| โš–๏ธ
stuttering-judge | | villagers | **Max in game**: 1 | The first night, he chooses a sign with the game master. After a vote, once in the game, he can do the sign to the game master and this will cause another vote immediately. | +| ๐Ÿ—ก
rusty-sword-knight | | villagers | **Max in game**: 1 | If he dies from the `eaten` attribute, the first alive player from the `werewolves` side on his left will have the `contaminated` attribute. This contaminated player will die at the end of the day. | +| ๐Ÿฆน
thief | | villagers / werewolves | **Max in game**: 1 | If he's in the party, 2 additional role cards must be added. The first night, he can choose between one of the two cards or keep his original role. If the two cards are in the `werewolves` side, he must choose one of them. | +| ๐Ÿ’
wild-child | | villagers / werewolves | **Max in game**: 1 | The first night, he chooses a model among the other players. If this model dies during the game, the wild child changes his side to `werewolves` and must win with them. | +| ๐Ÿ•
dog-wolf | | villagers / werewolves | **Max in game**: 1 | The first night, he chooses a side between `villagers` and `werewolves`. Then, he must win with the chosen side. Other players don't know what he chose. | +| ๐Ÿ‘ผ
angel | | villagers | **Max in game**: 1 | He must win alone. When he's in the party, the game starts with a `vote` (after `sheriff` election). If he dies from the first `votes` or by the `werewolves` the first night, he wins the game alone. | +| ๐Ÿ“ฃ
pied-piper | | villagers | **Max in game**: 1 | He must win alone. Each night, he charms two players, except himself. If all alive players are charmed, he wins the game. If he's infected by the `vile-father-of-wolves`, he looses his ability to charm. | +| ๐Ÿชถ
raven | | villagers | **Max in game**: 1 | Each night, he can mark someone (including himself) if he wants to. The next phase (during the `day`), the marked player will have two votes against himself for the next vote only. | ## ๐Ÿ”ช Player Actions Actions can be performed by a group, a role or a player which has a specific attribute. -| Action | [Role](#player-roles) | [Group](#player-groups) | [Attribute](#player-attributes) | When - Use and Limits | -|:-----------------:|:---------------------------------:|:-----------------------:|:--------------------------------:|------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| eat | ๐Ÿบ
werewolf | ๐Ÿบ
werewolves | - | Each night - Eat a villager chosen by majority. | -| eat | ๐Ÿบ
big-bad-wolf | ๐Ÿบ
werewolves | - | Each night until one player of the `werewolves` side has died - Kill a villager in addition to the `werewolves` target. | -| eat | ๐Ÿบ
white-werewolf | ๐Ÿบ
werewolves | - | Every other night starting the first night until there is no other werewolf other than him - Kill a werewolf. | -| look | ๐Ÿ”ฎ
seer | ๐Ÿง‘โ€๐ŸŒพ
villagers | - | Each night - Reveal a role. | -| charm | ๐Ÿน
cupid | ๐Ÿง‘โ€๐ŸŒพ
villagers | - | First night - Charm two people who have to win together. | -| use-potion | ๐Ÿช„
witch | ๐Ÿง‘โ€๐ŸŒพ
villagers | - | Each night - Protect or kill. One use available for each life and death potion. | -| shoot | ๐Ÿ”ซ
hunter | ๐Ÿง‘โ€๐ŸŒพ
villagers | - | When hunter dies - Kill someone chosen by hunter, can't be himself. | -| protect | ๐Ÿ›ก๏ธ
guard | ๐Ÿง‘โ€๐ŸŒพ
villagers | - | Each night - Prevents from death for the night. Can't protect the same player twice in a row. | -| mark | ๐Ÿชถ
raven | ๐Ÿง‘โ€๐ŸŒพ
villagers | - | Each night - Mark someone. The next day, the target will have 2 votes against himself. The mark goes away after the judgement. | -| meet-each-other | ๐Ÿ‘ญ
two-sisters | ๐Ÿง‘โ€๐ŸŒพ
villagers | - | First night and every X night depending on game options. Brief moment in which sisters meet each other and can speak (with gestures). | -| meet-each-other | ๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ฆ
three-brothers | ๐Ÿง‘โ€๐ŸŒพ
villagers | - | First night and every X night depending on game options. Brief moment in which brothers meet each other and can speak (with gestures). | -| choose-model | ๐Ÿ’
wild-child | ๐Ÿง‘โ€๐ŸŒพ
villagers | - | First night - The wild child chooses his model among the players. If the model dies, wild child changes side to `werewolves`. He can't choose himself. | -| choose-side | ๐Ÿ•
dog-wolf | ๐Ÿง‘โ€๐ŸŒพ
villagers | - | First night - The dog-wolf chooses his side between `villagers` and `werewolves` and must win with the chosen side. | -| ban-voting | ๐Ÿ
scapegoat | ๐Ÿง‘โ€๐ŸŒพ
villagers | - | When scapegoat dies from a tie in votes - He chooses who won't be able to vote during the next day. If no one can vote, there won't be votes during this day. | -| charm | ๐Ÿ“ฃ
pied-piper | ๐Ÿง‘โ€๐ŸŒพ
villagers | - | Each night - He charms two players which will have the `charmed` attribute for the rest of the game. Charmed players meet each other right after pied piper turn.| -| choose-sign | โš–๏ธ
stuttering-judge | ๐Ÿง‘โ€๐ŸŒพ
villagers | - | First night - He chooses a sign to do with the game master to cause a second vote. | -| choose-card | ๐Ÿฆน๏ธ
thief | ๐Ÿง‘โ€๐ŸŒพ
villagers | - | First night - He can choose a card from the additional cards. | -| elect-sheriff | - | ๐Ÿ‘ช
all | - | During the first phase (`night`) - Anyone can be elected as a sheriff. | -| vote | - | ๐Ÿ‘ช
all | - | Each day - All alive players vote for someone to kill. | -| delegate | - | - | ๐ŸŽ–
sheriff | When sheriff dies - The dying sheriff chooses the next one in among the living. | -| settle-votes | - | - | ๐ŸŽ–
sheriff | When there is a tie in the votes during the `day` - Choose which one will be executed. | -| meet-each-other | - | - | ๐Ÿ’•
in-love | Right after Cupid chose his targets, lovers wake up and meet each other. | -| meet-each-other | - | - | ๐Ÿ•บ๏ธ
charmed | Right after pied piper chose his targets, all charmed players (new and old) wake up and meet each other. | +| Action | [Role](#player-roles) | [Group](#player-groups) | [Attribute](#player-attributes) | When - Use and Limits | +|:-----------------:|:---------------------------------:|:-----------------------:|:--------------------------------:|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| eat | ๐Ÿบ
werewolf | ๐Ÿบ
werewolves | - | Each night - Eat a villager chosen by majority. | +| eat | ๐Ÿบ
big-bad-wolf | ๐Ÿบ
werewolves | - | Each night until one player of the `werewolves` side has died - Kill a villager in addition to the `werewolves` target. | +| eat | ๐Ÿบ
white-werewolf | ๐Ÿบ
werewolves | - | Every other night starting the first night until there is no other werewolf other than him - Kill a werewolf. | +| look | ๐Ÿ”ฎ
seer | ๐Ÿง‘โ€๐ŸŒพ
villagers | - | Each night - Reveal a role. | +| charm | ๐Ÿน
cupid | ๐Ÿง‘โ€๐ŸŒพ
villagers | - | First night - Charm two people who have to win together. | +| use-potion | ๐Ÿช„
witch | ๐Ÿง‘โ€๐ŸŒพ
villagers | - | Each night - Protect or kill. One use available for each life and death potion. | +| shoot | ๐Ÿ”ซ
hunter | ๐Ÿง‘โ€๐ŸŒพ
villagers | - | When hunter dies - Kill someone chosen by hunter, can't be himself. | +| protect | ๐Ÿ›ก๏ธ
guard | ๐Ÿง‘โ€๐ŸŒพ
villagers | - | Each night - Prevents from death for the night. Can't protect the same player twice in a row. | +| mark | ๐Ÿชถ
raven | ๐Ÿง‘โ€๐ŸŒพ
villagers | - | Each night - Mark someone. The next day, the target will have 2 votes against himself. The mark goes away after the judgement. | +| meet-each-other | ๐Ÿ‘ญ
two-sisters | ๐Ÿง‘โ€๐ŸŒพ
villagers | - | First night and every X night depending on game options. Brief moment in which sisters meet each other and can speak (with gestures). | +| meet-each-other | ๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ฆ
three-brothers | ๐Ÿง‘โ€๐ŸŒพ
villagers | - | First night and every X night depending on game options. Brief moment in which brothers meet each other and can speak (with gestures). | +| sniff | ๐ŸฆŠ
fox | ๐Ÿง‘โ€๐ŸŒพ
villagers | - | Each night - The fox chooses a group of 3 people by pointing the player on the center. Game master makes a positive sign if one of the group is from the `werewolves` side or looses his power otherwise. | +| choose-model | ๐Ÿ’
wild-child | ๐Ÿง‘โ€๐ŸŒพ
villagers | - | First night - The wild child chooses his model among the players. If the model dies, wild child changes side to `werewolves`. He can't choose himself. | +| choose-side | ๐Ÿ•
dog-wolf | ๐Ÿง‘โ€๐ŸŒพ
villagers | - | First night - The dog-wolf chooses his side between `villagers` and `werewolves` and must win with the chosen side. | +| ban-voting | ๐Ÿ
scapegoat | ๐Ÿง‘โ€๐ŸŒพ
villagers | - | When scapegoat dies from a tie in votes - He chooses who won't be able to vote during the next day. If no one can vote, there won't be votes during this day. | +| charm | ๐Ÿ“ฃ
pied-piper | ๐Ÿง‘โ€๐ŸŒพ
villagers | - | Each night - He charms two players which will have the `charmed` attribute for the rest of the game. Charmed players meet each other right after pied piper turn. | +| choose-sign | โš–๏ธ
stuttering-judge | ๐Ÿง‘โ€๐ŸŒพ
villagers | - | First night - He chooses a sign to do with the game master to cause a second vote. | +| choose-card | ๐Ÿฆน๏ธ
thief | ๐Ÿง‘โ€๐ŸŒพ
villagers | - | First night - He can choose a card from the additional cards. | +| elect-sheriff | - | ๐Ÿ‘ช
all | - | During the first phase (`night`) - Anyone can be elected as a sheriff. | +| vote | - | ๐Ÿ‘ช
all | - | Each day - All alive players vote for someone to kill. | +| delegate | - | - | ๐ŸŽ–
sheriff | When sheriff dies - The dying sheriff chooses the next one in among the living. | +| settle-votes | - | - | ๐ŸŽ–
sheriff | When there is a tie in the votes during the `day` - Choose which one will be executed. | +| meet-each-other | - | - | ๐Ÿ’•
in-love | Right after Cupid chose his targets, lovers wake up and meet each other. | +| meet-each-other | - | - | ๐Ÿ•บ๏ธ
charmed | Right after pied piper chose his targets, all charmed players (new and old) wake up and meet each other. | ## ๐ŸŽ–๏ธ Player Attributes @@ -107,25 +111,28 @@ Attributes are consequences of actions and hold by players. Each attribute has s | ๐Ÿชถ
raven-marked | The raven marked the player during the night. During the next phase (`day`), this player will have two votes against himself. | 2 phases
(_1 turn_) | | ๐Ÿ’•
in-love | Shot by the Cupid arrow, players with this attribute must win together the game. If one dies, the other one dies too. | Last forever
(`remainingPhases` _not set_) | | ๐Ÿ™‡
worshiped | The wild child chose the player during the first night. If the player dies, the wild child changes his side to `werewolves`. | Last forever
(`remainingPhases` _not set_) | -| ๐Ÿคทโ€
powerless | The ancient died from the `hunter`, the `witch` or from the `vote`, all who started the game in the `villagers` side will have this attribute and therefore, won't be able to use their powers anymore. | Last forever
(`remainingPhases` _not set_) | +| ๐Ÿคทโ€
powerless | This player can't use his power anymore. | Last forever
(`remainingPhases` _not set_) | | ๐Ÿšซ๐Ÿ—ณ๏ธโ€
cant-vote | The player is not able to vote while he has this attribute. If all alive players have this attribute, there is no vote. | From the `scapegoat`: 2 phases
(_1 turn_)
From the `idiot`: Last forever
(`remainingPhases` _not set_) | | ๐Ÿ•บ๏ธโ€
charmed | The pied piper charmed this player during the night. If all alive players have this attribute, the pied piper wis the game. | Last forever
(`remainingPhases` _not set_) | +| ๐Ÿปโ€
growls | The bear tamer has one of his alive neighbors from the `werewolves` side or is himself `infected`. | 1 phase | +| ๐Ÿฆ โ€
contaminated | The player will die at the end of the day because he's from the `werewolves` side and the nearest left neighbor of the `rusty-sword-knight`. | 1 phase | ## โ˜ ๏ธ Player Deaths During the game, players can die from multiple causes. -| Source | Cause | Possible target(s) | Description | -|:----------------------:|:------------:|:------------------------------------------:|--------------------------------------------------------------------------------------------------------------------------| -| ๐Ÿช„
๏ธโ€witch | use-potion | All alive players | This player died from drinking the death potion, there is no antidote. | -| ๐Ÿบ
werewolves | eat | All alive players in the `villagers` side | This player has been eaten by the `werewolves` and was not protected by the `guard` and didn't drink the life potion. | -| ๐Ÿบ
big-bad-wolf | eat | All alive players in the `villagers` side | This player has been eaten by the `big-bad-wolf` and was not protected by the `guard` and didn't drink the life potion. | -| ๐Ÿบ
white-werewolf | eat | All alive players in the `werewolves` side | This player has been eaten by the `white-werewolf` and there is no protection about it. | -| ๐Ÿ”ซ
hunter | shoot | All alive players | This player has been shoot by the hunter who just died. | -| ๐ŸŽ–๏ธ
sheriff | settle-votes | Players involved in the tie in votes | This player has been chosen by the sheriff to die after being in the tie in votes during the previous play. | -| ๐Ÿ‘ช
all | vote | All alive players | This player has been chosen by the village to be hanged after votes. | -| ๐Ÿน
cupid | charm | Players with `in-love` attribute | The other `in-love` player died, and then his beloved one died from love. | -| ๐Ÿ‘ช
all | reconsider | Player with `idiot` role | The player with the `idiot` role was already revealed and the `ancient` died. | +| Source | Cause | Possible target(s) | Description | +|:--------------------------:|:------------:|:------------------------------------------:|--------------------------------------------------------------------------------------------------------------------------| +| ๐Ÿช„
๏ธโ€witch | use-potion | All alive players | This player died from drinking the death potion, there is no antidote. | +| ๐Ÿบ
werewolves | eat | All alive players in the `villagers` side | This player has been eaten by the `werewolves` and was not protected by the `guard` and didn't drink the life potion. | +| ๐Ÿบ
big-bad-wolf | eat | All alive players in the `villagers` side | This player has been eaten by the `big-bad-wolf` and was not protected by the `guard` and didn't drink the life potion. | +| ๐Ÿบ
white-werewolf | eat | All alive players in the `werewolves` side | This player has been eaten by the `white-werewolf` and there is no protection about it. | +| ๐Ÿ”ซ
hunter | shoot | All alive players | This player has been shoot by the hunter who just died. | +| ๐ŸŽ–๏ธ
sheriff | settle-votes | Players involved in the tie in votes | This player has been chosen by the sheriff to die after being in the tie in votes during the previous play. | +| ๐Ÿ‘ช
all | vote | All alive players | This player has been chosen by the village to be hanged after votes. | +| ๐Ÿน
cupid | charm | Players with `in-love` attribute | The other `in-love` player died, and then his beloved one died from love. | +| ๐Ÿ‘ช
all | reconsider | Player with `idiot` role | The player with the `idiot` role was already revealed and the `ancient` died. | +| ๐Ÿ—ก
rusty-sword-knight | disease | All alive players in the `werewolves` side | This werewolf was the nearest left player of the `rusty-sword-knight` after eating him. | ## โš ๏ธ Errors @@ -188,7 +195,7 @@ Description for each case below : | 51 | CANT_EAT_HIMSELF | 400 | White werewolf can't eat himself. | | 52 | STUTTERING_JUDGE_ABSENT | 400 | Second vote can't be requested if stuttering judge is absent from the game. | | 53 | STUTTERING_JUDGE_POWERLESS | 400 | Stuttering judge is powerless and so can't request another vote. | -| 54 | ONLY_ONE_SECOND_VOTE_REQUEST | 400 | Second vote request has been already made. | +| 54 | VOTE_REQUESTS_EXCEEDED | 400 | Stuttering judge doesn't have any vote request left. | | 55 | BAD_PLAY_ACTION_FOR_SIDE_CHOICE | 400 | `side` can be set only if play's action is `choose-side`. | | 56 | BAD_PLAY_ACTION_FOR_JUDGE_REQUEST | 400 | `doesJudgeRequestAnotherVote` can be set only if play's action is `vote`. | | 57 | BAD_PLAY_ACTION_FOR_INFECTION | 400 | `isInfected` can be set on target only if play's action is `eat`. | @@ -196,7 +203,17 @@ Description for each case below : | 59 | STUTTERING_JUDGE_DIDNT_CHOOSE_SIGN_YET | 400 | Stuttering judge didn't choose his sign yet and so can't request another vote. | | 60 | ADDITIONAL_CARDS_NOT_ALLOWED | 400 | `additionalCards` is not allowed when there is no `thief` in game. | | 61 | FORBIDDEN_ADDITIONAL_CARD_ROLE_FOR_THIEF | 400 | Role is not allowed in additional cards for thief. | -| 62 | NEED_ADDITIONAL_CARDS_FOR_THIEF | 400 | When `thief` is in the game, 2 additional cards must be added for him. | +| 62 | NEED_ADDITIONAL_CARDS_FOR_THIEF | 400 | When `thief` is in the game, 2 additional cards must be added for him (depending on game's options). | | 63 | BAD_PLAY_ACTION_FOR_CHOSEN_CARD | 400 | `card` can be set only if action is `choose-card`. | | 64 | CHOSEN_CARD_NOT_FOUND | 404 | The chosen card is not found in additional cards. | -| 65 | THIEF_MUST_STEAL | 400 | As all additional cards for thief are on the werewolves side, he must choose one of them. | \ No newline at end of file +| 65 | THIEF_MUST_STEAL | 400 | As all additional cards for thief are on the werewolves side, he must choose one of them. | +| 66 | ALL_PLAYERS_POSITION_NOT_SET | 400 | Some players has a position and other not. You must define all position or none of them. | +| 67 | PLAYERS_POSITION_NOT_UNIQUE | 400 | Players don't all have unique position. | +| 68 | PLAYER_POSITION_TOO_HIGH | 400 | One player's position exceeds the maximum. | +| 69 | THIEF_ADDITIONAL_CARDS_COUNT_NOT_RESPECTED | 400 | Exactly 2 additional cards are needed for thief (depending on game's options). | +| 70 | BAD_FACEBOOK_ACCESS_TOKEN | 400 | Access token doesn't allow to get user info. | +| 71 | NEED_FACEBOOK_EMAIL_PERMISSION | 400 | You need to share your email to login with Facebook. | +| 72 | EMAIL_EXISTS_WITH_LOCAL_REGISTRATION | 400 | The email provided already exists with local registration. | +| 73 | EMAIL_EXISTS_WITH_FACEBOOK_REGISTRATION | 400 | The email provided already exists with facebook registration. | +| 74 | EMAIL_EXISTS_WITH_GOOGLE_REGISTRATION | 400 | The email provided already exists with google registration. | +| 75 | BAD_GOOGLE_ID_TOKEN | 400 | Id token doesn't allow to get user info. | \ No newline at end of file diff --git a/config/apidoc/header.md b/config/apidoc/header.md index f6e200e..21669ba 100644 --- a/config/apidoc/header.md +++ b/config/apidoc/header.md @@ -19,12 +19,14 @@ In order to log in and create games, a user must be created (aka the future game master). -| Field | Type | Description | -|----------------------|:--------:|---------------------------------------------------------------------| -| _id | ObjectId | User's ID. | -| email | String | User's email. | -| createdAt | Date | When the user created his account. | -| updatedAt | Date | When the user updated his account. | +| Field   | Type | Description | +|----------------------|:--------:|-----------------------------------------------------------------------------------------| +| _id | ObjectId | User's ID. | +| email | String | User's email. | +| registration | Object | User's registration data. | +|  โฎ‘ method | String | How the user registered himself. (_Possibilities: `local`, `facebook` or `google`_) | +| createdAt | Date | When the user created his account. | +| updatedAt | Date | When the user updated his account. | ## ๐ŸŽฒ Game @@ -50,47 +52,79 @@ At the end of the game, winner(s) are set in the `won` property. When game's status is `done` or `canceled`, it can be reviewed by the game master. -| Field             | Type | Description | -|-------------------------------------------------------------------------------|:-------------------------------------------------:|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| _id | ObjectId | Game's ID. | -| gameMaster | User | User who created the game and managing it. (_See: [Classes - User](#user-class)_) | -| players | [Player[]](#player-class) | Players of the game. | -| turn | Number | Starting at `1`, a turn starts with the first phase (the `night`) and ends with the second phase (the `day`). | -| phase | String | Each turn has two phases, `day` or `night`. Starting at `night`. | -| tick | Number | Starting at `1`, tick increments each time a play is made. | -| waiting | Object[] | Queue of upcoming actions and sources. | -|  โฎ‘ for | String | Can be either a group, a role or the sheriff. (_See: [Codes - Player Groups](#player-groups) or [Codes - Player Roles](#player-roles) or `sheriff`_) | -|  โฎ‘ to | String | What action needs to be performed by `waiting.for`. (_See: [Codes - Player Actions](#player-actions)_) | -| ** โฎ‘ cause*** | String | The cause of action. (_Possibilities: `stuttering-judge-request`_) | -| status | String | Game's current status. (_See: [Codes - Game Statuses](#game-statuses)_) | -| **additionalCards*** | [AdditionalCard[]](#game-additional-card-class) | Game's additional cards. Set if `thief` is in the game. | -| options | Object | Game's options. | -|  โฎ‘ roles | Object | Game roles options. | -|   โฎ‘ sheriff | Object | Game sheriff role's options. | -|    โฎ‘ isEnabled | Boolean | If set to `true`, `sheriff` will be elected the first tick and the responsibility will be delegated when he dies. Otherwise, there will be no sheriff in the game and tie in votes will result in another vote between the tied players. In case of another equality, there will be no vote. | -|    โฎ‘ hasDoubledVote | Boolean | If set to `true`, `sheriff` vote during the village's vote is doubled, otherwise, it's a regular vote. Default is `true`. | -|   โฎ‘ seer | Object | Game seer role's options. | -|    โฎ‘ isTalkative | Boolean | If set to `true`, the game master must say out loud what the seer saw during her night, otherwise, he must mime the seen role to the seer. Default is `true`. | -|   โฎ‘ littleGirl | Object | Game little girl role's options. | -|    โฎ‘ isProtectedByGuard | Boolean | If set to `false`, the little girl won't be protected by the guard from the werewolves attacks. Default is `false`. | -|   โฎ‘ idiot | Object | Game idiot role's options. | -|    โฎ‘ doesDieOnAncientDeath | Boolean | If set to `true`, the idiot will die if he is revealed and the ancient is dead. Default is `true`. | -|   โฎ‘ twoSisters | Object | Game two sisters role's options. | -|    โฎ‘ wakingUpInterval | Number | Since first `night`, interval of nights when the Two Sisters are waking up. Default is `2`, meaning they wake up every other night. If set to `0`, they are waking up the first night only. | -|   โฎ‘ threeBrothers | Object | Game three brothers role's options. | -|    โฎ‘ wakingUpInterval | Number | Since first `night`, interval of nights when the Three Brothers are waking up. Default is `2`, meaning they wake up every other night. If set to `0`, they are waking up the first night only. | -|   โฎ‘ raven | Object | Game raven's role options. | -|    โฎ‘ markPenalty | Number | Penalty of votes against the player targeted by the raven mark for the next village's vote. Default is `2`, meaning that the raven marked player will have two votes against himself. | -| history | [GameHistory[]](#game-history-class) | Game's history. Limited by default to `3` entries. (_See: [Classes - Game History Entry](#game-history-class)_) | -| **won*** | Object | Winner(s) of the game when status is `done`. | -|  โฎ‘ by | String | Can be either a group or a role. (_Possibilities: `werewolves`, `villagers`, `lovers`, `pied-piper`, `white-werewolf` or null if nobody won_) | -| ** โฎ‘ players*** | [Player[]](#player-class) | List of player(s) who won. (_See: [Classes - Player](#player-class)_) | -| **review*** | Object | Game master can attach a game review only if its status is set to `canceled` or `done`. | -|  โฎ‘ rating | Number | Review's rating, from 0 to 5. | -| ** โฎ‘ comment*** | String | Review's comment, from 1 to 500 characters long. | -| ** โฎ‘ dysfunctionFound*** | Boolean | If a bug or a dysfunction was found during the game. | -| createdAt | Date | When the user created his account. | -| updatedAt | Date | When the user updated his account. | +| Field             | Type | Description | +|-------------------------------------------------------------------------------|:-------------------------------------------------:|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| _id | ObjectId | Game's ID. | +| gameMaster | User | User who created the game and managing it. (_See: [Classes - User](#user-class)_) | +| players | [Player[]](#player-class) | Players of the game. | +| turn | Number | Starting at `1`, a turn starts with the first phase (the `night`) and ends with the second phase (the `day`). | +| phase | String | Each turn has two phases, `day` or `night`. Starting at `night`. | +| tick | Number | Starting at `1`, tick increments each time a play is made. | +| waiting | Object[] | Queue of upcoming actions and sources. | +|  โฎ‘ for | String | Can be either a group, a role or the sheriff. (_See: [Codes - Player Groups](#player-groups) or [Codes - Player Roles](#player-roles) or `sheriff`_) | +|  โฎ‘ to | String | What action needs to be performed by `waiting.for`. (_See: [Codes - Player Actions](#player-actions)_) | +| ** โฎ‘ cause*** | String | The cause of action. (_Possibilities: `stuttering-judge-request`_) | +| status | String | Game's current status. (_See: [Codes - Game Statuses](#game-statuses)_) | +| **additionalCards*** | [AdditionalCard[]](#game-additional-card-class) | Game's additional cards. Set if `thief` is in the game. | +| options | Object | Game's options. | +|  โฎ‘ repartition | Object | Game repartition's options. | +|   โฎ‘ isHidden | Boolean | If set to `true`, game's repartition will be hidden to all players. Default is `false`. | +|  โฎ‘ roles | Object | Game roles options. | +|   โฎ‘ areRevealedOnDeath | Boolean | If set to `true`, player's role is revealed when he's dead. Default is `true`. | +|   โฎ‘ sheriff | Object | Game sheriff role's options. | +|    โฎ‘ isEnabled | Boolean | If set to `true`, `sheriff` will be elected the first tick and the responsibility will be delegated when he dies. Otherwise, there will be no sheriff in the game and tie in votes will result in another vote between the tied players. In case of another equality, there will be no vote. Default is `true`. | +|    โฎ‘ electedAt | Object | When the sheriff is elected during the game. | +|     โฎ‘ turn | Number | Game's turn when the sheriff is elected. Default is `1`. | +|     โฎ‘ phase | String | Game's phase when the sheriff is elected. Default is `night`. | +|    โฎ‘ hasDoubledVote | Boolean | If set to `true`, `sheriff` vote during the village's vote is doubled, otherwise, it's a regular vote. Default is `true`. | +|   โฎ‘ bigBadWolf | Object | Game big bad wolf role's options. | +|    โฎ‘ isPowerlessIfWerewolfDies | Boolean | If set to `true`, `big-bad-wolf` won't wake up anymore during the night if at least one player from the `werewolves` side died. Default is `true`. | +|   โฎ‘ whiteWerewolf | Object | Game white werewolf role's options. | +|    โฎ‘ wakingUpInterval | Number | Since first `night`, interval of nights when the `white-werewolf` is waking up. Default is `2`, meaning he wakes up every other night. | +|   โฎ‘ seer | Object | Game seer role's options. | +|    โฎ‘ isTalkative | Boolean | If set to `true`, the game master must say out loud what the seer saw during her night, otherwise, he must mime the seen role to the seer. Default is `true`. | +|    โฎ‘ canSeeRoles | Boolean | If set to `true`, the seer can the exact `role` of the target, otherwise, she only sees the `side`. Default is `true`. | +|   โฎ‘ littleGirl | Object | Game little girl role's options. | +|    โฎ‘ isProtectedByGuard | Boolean | If set to `false`, the little girl won't be protected by the guard from the werewolves attacks. Default is `false`. | +|   โฎ‘ guard | Object | Game guard role's options. | +|    โฎ‘ canProtectTwice | Boolean | If set to `true`, the guard can protect twice in a row the same target. Default is `false`. | +|   โฎ‘ ancient | Object | Game ancient role's options. | +|    โฎ‘ livesCountAgainstWerewolves | Number | Number of lives ancient has against `werewolves`. Default is `2`. | +|    โฎ‘ doesTakeHisRevenge | Boolean | If set to `true`, the `ancient` will make all players from the `villagers` side `powerless` if he dies from them. Default is `true`. | +|   โฎ‘ idiot | Object | Game idiot role's options. | +|    โฎ‘ doesDieOnAncientDeath | Boolean | If set to `true`, the idiot will die if he is revealed and the ancient is dead. Default is `true`. | +|   โฎ‘ twoSisters | Object | Game two sisters role's options. | +|    โฎ‘ wakingUpInterval | Number | Since first `night`, interval of nights when the Two Sisters are waking up. Default is `2`, meaning they wake up every other night. If set to `0`, they are waking up the first night only. | +|   โฎ‘ threeBrothers | Object | Game three brothers role's options. | +|    โฎ‘ wakingUpInterval | Number | Since first `night`, interval of nights when the Three Brothers are waking up. Default is `2`, meaning they wake up every other night. If set to `0`, they are waking up the first night only. | +|   โฎ‘ fox | Object | Game fox's role options. | +|    โฎ‘ isPowerlessIfMissesWerewolf | Boolean | If set to `true`, the fox will loose his power if he doesn't find a player from the `werewolves` side during his turn if he doesn't skip. Default is `true`. | +|   โฎ‘ bearTamer | Object | Game bear tamer's role options. | +|    โฎ‘ doesGrowlIfInfected | Boolean | If set to `true`, the bear tamer will have the `growls` attribute until he dies if he is `infected`. Default is `true`. | +|   โฎ‘ stutteringJudge | Object | Game stuttering judge's role options. | +|    โฎ‘ voteRequestsCount | Number | Number of vote requests that the `stuttering-judge` can make during the game. Default is `1`. | +|   โฎ‘ wildChild | Object | Game wild child's role options. | +|    โฎ‘ isTransformationRevealed | Boolean | If set to `true`, when `wild-child` joins the `werewolves` side because his model died, the game master will announce his transformation to other players. Default is `false`. | +|   โฎ‘ dogWolf | Object | Game dog wolf's role options. | +|    โฎ‘ isChosenSideRevealed | Boolean | If set to `true`, when `dog-wolf` chooses his side at the beginning of the game, the game master will announce the chosen side to other players. Default is `false`. | +|   โฎ‘ thief | Object | Game thief's role options. | +|    โฎ‘ mustChooseBetweenWerewolves | Boolean | If set to `true`, if all `thief` additional cards are from the `werewolves` side, he can't skip and must choose one. Default is `true`. | +|    โฎ‘ additionalCardsCount | Number | Number of additional cards for the `thief` at the beginning of the game. Default is `2`. | +|   โฎ‘ piedPiper | Object | Game pied piper's role options. | +|    โฎ‘ charmedPeopleCountPerNight | Number | Number of charmed people by the `pied-piper` per night if there are enough targets (or number of not charmed players otherwise). Default is `2`. | +|    โฎ‘ isPowerlessIfInfected | Boolean | If set to `true`, `pied-piper` will be powerless if he is infected by the `vile-father-of-wolves`. Default is `true`. | +|   โฎ‘ raven | Object | Game raven's role options. | +|    โฎ‘ markPenalty | Number | Penalty of votes against the player targeted by the raven mark for the next village's vote. Default is `2`, meaning that the raven marked player will have two votes against himself. | +| history | [GameHistory[]](#game-history-class) | Game's history. Limited by default to `3` entries. (_See: [Classes - Game History Entry](#game-history-class)_) | +| **won*** | Object | Winner(s) of the game when status is `done`. | +|  โฎ‘ by | String | Can be either a group or a role. (_Possibilities: `werewolves`, `villagers`, `lovers`, `pied-piper`, `white-werewolf` or null if nobody won_) | +| ** โฎ‘ players*** | [Player[]](#player-class) | List of player(s) who won. (_See: [Classes - Player](#player-class)_) | +| **review*** | Object | Game master can attach a game review only if its status is set to `canceled` or `done`. | +|  โฎ‘ rating | Number | Review's rating, from 0 to 5. | +| ** โฎ‘ comment*** | String | Review's comment, from 1 to 500 characters long. | +| ** โฎ‘ dysfunctionFound*** | Boolean | If a bug or a dysfunction was found during the game. | +| createdAt | Date | When the user created his account. | +| updatedAt | Date | When the user updated his account. | ## ๐Ÿบโšก๐Ÿง™ Player @@ -122,6 +156,7 @@ When a player is killed, `isAlive` is set to `false` and `murdered` object is fi | ** โฎ‘ activeAt*** | Object | When the attribute will become active and will have consequences on players. Used for attributes with delay. If not set, the attribute is immediately active. | |   โฎ‘ turn | Number | From which game's turn the attribute will become active. | | **  โฎ‘ phase*** | String | From which game turn's phase (`day` or `night`) the attribute will become active. | +| position | Number | Unique player's position among all game's players. Increment from `0` to `players.length - 1`. | | isAlive | Boolean | If the player is currently alive or not. | | **murdered*** | Object | Set if `isAlive` is `false`. | |  โฎ‘ by | String | Which role or group killed the player. (_Possibilities: [Codes - Player Roles](#player-roles) or [Codes - Player Groups](#player-groups) or `sheriff`_) | @@ -138,6 +173,7 @@ All available roles of this version can be gathered on the [route GET /roles](#a | _id | ObjectId | Role's ID. | | name | String | Role's name. | | side | String | Role's original side. | +| type | String | Role's type. (_Possibilities: `villager`, `werewolf`, `ambiguous`, `lonely`_) | | **minInGame*** | Number | If the role is chosen by at least one player, then minimum X players must choose it to start the game. | | maxInGame | Number | Maximum possible of this role in a game. | | **recommendedMinPlayers*** | Number | It is recommended to have at least X players in game for choosing this role. | diff --git a/config/index.js b/config/index.js index 2e5c54d..6fbde93 100644 --- a/config/index.js +++ b/config/index.js @@ -22,6 +22,8 @@ const Config = { projectID: process.env.SENTRY_PROJECT_ID, key: process.env.SENTRY_KEY, }, + google: { client: { ID: process.env.GOOGLE_CLIENT_ID } }, + facebook: { app: { ID: process.env.FACEBOOK_APP_ID } }, }; if (Config.app.nodeEnv === "test") { diff --git a/package-lock.json b/package-lock.json index c2052db..01ac145 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,4896 +1,8 @@ { "name": "werewolwes-assistant-api", - "version": "0.10.0", - "lockfileVersion": 2, + "version": "0.11.0", + "lockfileVersion": 1, "requires": true, - "packages": { - "": { - "name": "werewolwes-assistant-api", - "version": "0.10.0", - "license": "ISC", - "dependencies": { - "@sentry/node": "^6.0.3", - "apidoc": "^0.26.0", - "bcrypt": "^5.0.0", - "body-parser": "^1.19.0", - "colors": "^1.4.0", - "cors": "^2.8.5", - "deepmerge": "^4.2.2", - "dotenv": "^8.2.0", - "express": "^4.17.1", - "express-rate-limit": "^5.2.3", - "express-validator": "^6.9.2", - "jsonwebtoken": "^8.5.1", - "migrate-mongo": "^8.1.4", - "mongo-dot-notation": "^2.0.0", - "mongoose": "^5.11.14", - "passport": "^0.4.1", - "passport-http": "^0.3.0", - "passport-jwt": "^4.0.0", - "passport-local": "^1.0.0", - "qs": "^6.9.6", - "requirejs": "^2.3.6", - "validator": "^13.5.2", - "xss": "^1.0.8" - }, - "devDependencies": { - "chai": "^4.2.0", - "chai-http": "^4.3.0", - "eslint": "^7.19.0", - "mocha": "^8.2.1", - "nodemon": "^2.0.7" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.10.4" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "dev": true - }, - "node_modules/@babel/highlight": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.8.tgz", - "integrity": "sha512-4vrIhfJyfNf+lCtXC2ck1rKSzDwciqF7IWFhXXrSOUC2O5DrVp+w4c6ed4AllTxhTkUP5x2tYj41VaxdVMMRDw==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.12.11", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@dabh/diagnostics": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", - "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", - "dependencies": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz", - "integrity": "sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/@eslint/eslintrc/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@eslint/eslintrc/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/@mapbox/node-pre-gyp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.0.tgz", - "integrity": "sha512-mEaiD1CURETR/dBIiJAwz0M0Q0mH3gCW4pPMaIlNt97mdzYUVeqGcTJSamgJpS6Tg4tBHDrOJpjdh5fJTLnyNQ==", - "dependencies": { - "detect-libc": "^1.0.3", - "http-proxy-agent": "^4.0.1", - "mkdirp": "^1.0.4", - "node-fetch": "^2.6.1", - "nopt": "^5.0.0", - "npmlog": "^4.1.2", - "rimraf": "^3.0.2", - "semver": "^7.3.4", - "tar": "^6.1.0" - }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" - } - }, - "node_modules/@sentry/core": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.2.1.tgz", - "integrity": "sha512-jPqQEtafxxDtLONhCbTHh/Uq8mZRhsfbwJTSVYfPVEe/ELfFZLQK7tP6rOh7zEWKbTkE0mE6XcaoH3ZRAhgrqg==", - "dependencies": { - "@sentry/hub": "6.2.1", - "@sentry/minimal": "6.2.1", - "@sentry/types": "6.2.1", - "@sentry/utils": "6.2.1", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/hub": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.2.1.tgz", - "integrity": "sha512-pG7wCQeRpzeP6t0bT4T0X029R19dbDS3/qswF8BL6bg0AI3afjfjBAZm/fqn1Uwe/uBoMHVVdbxgJDZeQ5d4rQ==", - "dependencies": { - "@sentry/types": "6.2.1", - "@sentry/utils": "6.2.1", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/minimal": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.2.1.tgz", - "integrity": "sha512-wuSXB4Ayxv9rBEQ4pm7fnG4UU2ZPtPnnChoEfd4/mw1UthXSvmPFEn6O4pdo2G8fTkl8eqm6wT/Q7uIXMEmw+A==", - "dependencies": { - "@sentry/hub": "6.2.1", - "@sentry/types": "6.2.1", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/node": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-6.2.1.tgz", - "integrity": "sha512-JlixtJHS6xMzh2G4Pz7oMM8Nd40mGUALQYtuGMwW2QE3IduOaaGsn1+eVpN6PwZetMnvRIn6VVFOc2UmFIzWpA==", - "dependencies": { - "@sentry/core": "6.2.1", - "@sentry/hub": "6.2.1", - "@sentry/tracing": "6.2.1", - "@sentry/types": "6.2.1", - "@sentry/utils": "6.2.1", - "cookie": "^0.4.1", - "https-proxy-agent": "^5.0.0", - "lru_map": "^0.3.3", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/tracing": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.2.1.tgz", - "integrity": "sha512-bvStY1SnL08wkSeVK3j9K5rivQQJdKFCPR2VYRFOCaUoleZ6ChPUnBvxQ/E2LXc0hk/y/wo1q4r5B0dfCCY+bQ==", - "dependencies": { - "@sentry/hub": "6.2.1", - "@sentry/minimal": "6.2.1", - "@sentry/types": "6.2.1", - "@sentry/utils": "6.2.1", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/types": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.2.1.tgz", - "integrity": "sha512-h0OV1QT+fv5ojfK5/+iEXClu33HirmvbjcQC2jf05IHj9yXIOWy6EB10S8nBjuLiiFqQiAQYj3FN9Ip4eN8NJA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/utils": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.2.1.tgz", - "integrity": "sha512-6kQgM/yBPdXu+3qbJnI6HBcWztN9QfiMkH++ZiKk4ERhg9d2LYWlze478uTU5Fyo/JQYcp+McpjtjpR9QIrr0g==", - "dependencies": { - "@sentry/types": "6.2.1", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "dependencies": { - "defer-to-connect": "^1.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/@types/bson": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.3.tgz", - "integrity": "sha512-mVRvYnTOZJz3ccpxhr3wgxVmSeiYinW+zlzQz3SXWaJmD1DuL05Jeq7nKw3SnbKmbleW5qrLG5vdyWe/A9sXhw==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/chai": { - "version": "4.2.15", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.15.tgz", - "integrity": "sha512-rYff6FI+ZTKAPkJUoyz7Udq3GaoDZnxYDEvdEdFZASiA7PoErltHezDishqQiSDWrGxvxmplH304jyzQmjp0AQ==", - "dev": true - }, - "node_modules/@types/cookiejar": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", - "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", - "dev": true - }, - "node_modules/@types/mongodb": { - "version": "3.6.8", - "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.8.tgz", - "integrity": "sha512-8qNbL5/GFrljXc/QijcuQcUMYZ1iWNcqnJ6tneROwbfU0LsAjQ9bmq3aHi5lWXM4cyBPd2F/n9INAk/pZZttHw==", - "dependencies": { - "@types/bson": "*", - "@types/node": "*" - } - }, - "node_modules/@types/node": { - "version": "14.14.31", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.31.tgz", - "integrity": "sha512-vFHy/ezP5qI0rFgJ7aQnjDXwAMrG0KqqIH7tQG5PPv3BWBayOPIQNBjVc/P6hhdZfMx51REc6tfDNXHUio893g==" - }, - "node_modules/@types/superagent": { - "version": "3.8.7", - "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-3.8.7.tgz", - "integrity": "sha512-9KhCkyXv268A2nZ1Wvu7rQWM+BmdYUVkycFeNnYrUL5Zwu7o8wPQ3wBfW59dDP+wuoxw0ww8YKgTNv8j/cgscA==", - "dev": true, - "dependencies": { - "@types/cookiejar": "*", - "@types/node": "*" - } - }, - "node_modules/@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "node_modules/accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "dependencies": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/agent-base/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/agent-base/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "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" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-align": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", - "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", - "dependencies": { - "string-width": "^3.0.0" - } - }, - "node_modules/ansi-align/node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-align/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "node_modules/ansi-align/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "engines": { - "node": ">=4" - } - }, - "node_modules/ansi-align/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-align/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/apidoc": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/apidoc/-/apidoc-0.26.0.tgz", - "integrity": "sha512-IEw/Z7HMMbjeVjK2sZvZSwAln8AqalLzf3qLDtkcedXVhdxGm6W7UgIW6fshegqNTMLzm8CFEMi4Lxbeu0xKTw==", - "dependencies": { - "apidoc-core": "^0.12.0", - "commander": "^2.20.0", - "fs-extra": "^9.0.1", - "handlebars": "^4.7.6", - "lodash": "^4.17.20", - "markdown-it": "^11.0.0", - "nodemon": "^2.0.4", - "winston": "^3.3.3" - }, - "bin": { - "apidoc": "bin/apidoc" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/apidoc-core": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/apidoc-core/-/apidoc-core-0.12.0.tgz", - "integrity": "sha512-VMhkJWz5IAyvWM0RnEbKNi1qe8se+id3/Ki3H/ePM8ih0KYTfaaSDxqo2w4uIVB1UVVKFvrTWyYUyQs7CEcoKQ==", - "dependencies": { - "fs-extra": "^9.0.1", - "glob": "^7.1.6", - "iconv-lite": "^0.6.2", - "klaw-sync": "^6.0.0", - "lodash": "^4.17.20", - "semver": "~7.3.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "node_modules/are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "node_modules/bcrypt": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.0.1.tgz", - "integrity": "sha512-9BTgmrhZM2t1bNuDtrtIMVSmmxZBrJ71n8Wg+YgdjHuIWYF7SjjmCPZFB+/5i/o/PIeRpwVJR3P+NrpIItUjqw==", - "hasInstallScript": true, - "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.0", - "node-addon-api": "^3.1.0" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/bl": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", - "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", - "dependencies": { - "readable-stream": "^2.3.5", - "safe-buffer": "^5.1.1" - } - }, - "node_modules/bluebird": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" - }, - "node_modules/body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "dependencies": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/body-parser/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/body-parser/node_modules/qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/boxen": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", - "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", - "dependencies": { - "ansi-align": "^3.0.0", - "camelcase": "^5.3.1", - "chalk": "^3.0.0", - "cli-boxes": "^2.2.0", - "string-width": "^4.1.0", - "term-size": "^2.1.0", - "type-fest": "^0.8.1", - "widest-line": "^3.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/boxen/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "node_modules/bson": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.5.tgz", - "integrity": "sha512-kDuEzldR21lHciPQAIulLs1LZlCXdLziXI6Mb/TDkwXhb//UORJNPXgcRs2CuO4H0DcMkpfT3/ySsP3unoZjBg==", - "engines": { - "node": ">=0.6.19" - } - }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" - }, - "node_modules/bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cacheable-request/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cacheable-request/node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/chai": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.0.tgz", - "integrity": "sha512-/BFd2J30EcOwmdOgXvVsmM48l0Br0nmZPlO0uOW4XKh6kpsUumRXBgPV+IlaqFaqr9cYbeoZAM1Npx0i4A+aiA==", - "dev": true, - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "pathval": "^1.1.0", - "type-detect": "^4.0.5" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/chai-http": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/chai-http/-/chai-http-4.3.0.tgz", - "integrity": "sha512-zFTxlN7HLMv+7+SPXZdkd5wUlK+KxH6Q7bIEMiEx0FK3zuuMqL7cwICAQ0V1+yYRozBburYuxN1qZstgHpFZQg==", - "dev": true, - "dependencies": { - "@types/chai": "4", - "@types/superagent": "^3.8.3", - "cookiejar": "^2.1.1", - "is-ip": "^2.0.0", - "methods": "^1.1.2", - "qs": "^6.5.1", - "superagent": "^3.7.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", - "dependencies": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.3.1", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.1" - } - }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "engines": { - "node": ">=10" - } - }, - "node_modules/ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" - }, - "node_modules/cli-boxes": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", - "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-table3": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.0.tgz", - "integrity": "sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ==", - "dependencies": { - "colors": "^1.1.2", - "object-assign": "^4.1.0", - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "colors": "^1.1.2" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "dependencies": { - "mimic-response": "^1.0.0" - } - }, - "node_modules/code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/color": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", - "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", - "dependencies": { - "color-convert": "^1.9.1", - "color-string": "^1.5.2" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/color-string": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.4.tgz", - "integrity": "sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/color/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/colorspace": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", - "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", - "dependencies": { - "color": "3.0.x", - "text-hex": "1.0.x" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, - "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "node_modules/configstore": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", - "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", - "dependencies": { - "dot-prop": "^5.2.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" - }, - "node_modules/content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "dependencies": { - "safe-buffer": "5.1.2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "node_modules/cookiejar": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", - "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", - "dev": true - }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/cssfilter": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz", - "integrity": "sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4=" - }, - "node_modules/date-fns": { - "version": "2.18.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.18.0.tgz", - "integrity": "sha512-NYyAg4wRmGVU4miKq5ivRACOODdZRY3q5WLmOJSq8djyzftYphU7dTHLcEtLqEvfqMKQ0jVv91P4BAwIjsXIcw==", - "engines": { - "node": ">=0.11" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/date-fns" - } - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dependencies": { - "mimic-response": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "node_modules/deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" - }, - "node_modules/denque": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz", - "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "bin": { - "detect-libc": "bin/detect-libc.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/dotenv": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", - "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" - }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "dependencies": { - "ansi-colors": "^4.1.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/entities": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", - "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==" - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-goat": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", - "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.21.0.tgz", - "integrity": "sha512-W2aJbXpMNofUp0ztQaF40fveSsJBjlSCSWpy//gzfTvwC+USs/nceBrKmlJOiM8r1bLwP2EuYkCqArn/6QTIgg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.0", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash": "^4.17.20", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.4", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^1.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/eslint/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", - "dev": true, - "dependencies": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "dependencies": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express-rate-limit": { - "version": "5.2.6", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-5.2.6.tgz", - "integrity": "sha512-nE96xaxGfxiS5jP3tD3kIW1Jg9yQgX0rXCs3rCkZtmbWHEGyotwaezkLj7bnB41Z0uaOLM8W4AX6qHao4IZ2YA==" - }, - "node_modules/express-validator": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.10.0.tgz", - "integrity": "sha512-gDtepU94EpUzgFvKO/8JzjZ4uqIF4xHekjYtcNgFDiBK6Hob3MQhPU8s/c3NaWd1xi5e5nA0oVmOJ0b0ZBO36Q==", - "dependencies": { - "lodash": "^4.17.20", - "validator": "^13.5.2" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/express/node_modules/cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "node_modules/fast-safe-stringify": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", - "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" - }, - "node_modules/fecha": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", - "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" - } - }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", - "dev": true - }, - "node_modules/fn-args": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/fn-args/-/fn-args-5.0.0.tgz", - "integrity": "sha512-CtbfI3oFFc3nbdIoHycrfbrxiGgxXBXXuyOl49h47JawM1mYrqpiRqnH5CB2mBatdXvHHOUO6a+RiAuuvKt0lw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, - "node_modules/form-data": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", - "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/formidable": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz", - "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==", - "dev": true, - "funding": { - "url": "https://ko-fi.com/tunnckoCore/commissions" - } - }, - "node_modules/forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "node_modules/gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dependencies": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "node_modules/gauge/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gauge/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dependencies": { - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gauge/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gauge/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/global-dirs": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.1.0.tgz", - "integrity": "sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ==", - "dependencies": { - "ini": "1.3.7" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "dev": true, - "dependencies": { - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "dependencies": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" - }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true, - "engines": { - "node": ">=4.x" - } - }, - "node_modules/handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" - }, - "node_modules/has-yarn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", - "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "bin": { - "he": "bin/he" - } - }, - "node_modules/http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" - }, - "node_modules/http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/http-errors/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/http-proxy-agent/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/http-proxy-agent/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/https-proxy-agent/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/iconv-lite": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", - "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=" - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", - "engines": { - "node": ">=4" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ini": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz", - "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==" - }, - "node_modules/ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dependencies": { - "ci-info": "^2.0.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-installed-globally": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", - "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", - "dependencies": { - "global-dirs": "^2.0.1", - "is-path-inside": "^3.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-2.0.0.tgz", - "integrity": "sha1-aO6gfooKCpTC0IDdZ0xzGrKkYas=", - "dev": true, - "dependencies": { - "ip-regex": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/is-npm": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", - "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", - "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "node_modules/is-yarn-global": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", - "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==" - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dependencies": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonwebtoken": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", - "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", - "dependencies": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=4", - "npm": ">=1.4.28" - } - }, - "node_modules/jsonwebtoken/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/jsonwebtoken/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "dependencies": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/kareem": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.2.tgz", - "integrity": "sha512-STHz9P7X2L4Kwn72fA4rGyqyXdmrMSdxqHx9IXon/FXluXieaFA6KJ2upcHAHxQPQ0LeM/OjLrhFxifHewOALQ==" - }, - "node_modules/keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "dependencies": { - "json-buffer": "3.0.0" - } - }, - "node_modules/klaw-sync": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", - "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", - "dependencies": { - "graceful-fs": "^4.1.11" - } - }, - "node_modules/kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "node_modules/latest-version": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", - "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", - "dependencies": { - "package-json": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/linkify-it": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.2.tgz", - "integrity": "sha512-gDBO4aHNZS6coiZCKVhSNh43F9ioIL4JwRjLZPkoLIY4yZFwg264Y5lu2x6rb1Js42Gh6Yqm2f6L2AJcnkzinQ==", - "dependencies": { - "uc.micro": "^1.0.1" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" - }, - "node_modules/lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" - }, - "node_modules/lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" - }, - "node_modules/lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" - }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" - }, - "node_modules/log-symbols": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", - "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/logform": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", - "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", - "dependencies": { - "colors": "^1.2.1", - "fast-safe-stringify": "^2.0.4", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "triple-beam": "^1.3.0" - } - }, - "node_modules/logform/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/lru_map": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", - "integrity": "sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0=" - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/markdown-it": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-11.0.1.tgz", - "integrity": "sha512-aU1TzmBKcWNNYvH9pjq6u92BML+Hz3h5S/QpfTFwiQF852pLT+9qHsrhM9JYipkOXZxGn+sGH8oyJE9FD9WezQ==", - "dependencies": { - "argparse": "^1.0.7", - "entities": "~2.0.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - }, - "bin": { - "markdown-it": "bin/markdown-it.js" - } - }, - "node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=" - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/memory-pager": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", - "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", - "optional": true - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/migrate-mongo": { - "version": "8.1.4", - "resolved": "https://registry.npmjs.org/migrate-mongo/-/migrate-mongo-8.1.4.tgz", - "integrity": "sha512-iuJiG86Qzr1w9B5j6jB6akZO0w040DTEWtKdclISaQg7KdovtXGYDmW8hehDsULY5oFGjNtJv/ABsOPlz0c5mw==", - "dependencies": { - "cli-table3": "^0.6.0", - "commander": "^6.1.0", - "date-fns": "^2.16.1", - "fn-args": "^5.0.0", - "fs-extra": "^9.0.1", - "lodash": "^4.17.20", - "mongodb": "^3.6.2", - "p-each-series": "^2.1.0" - }, - "bin": { - "migrate-mongo": "bin/migrate-mongo.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/migrate-mongo/node_modules/commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz", - "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.29", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz", - "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==", - "dependencies": { - "mime-db": "1.46.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "node_modules/minipass": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", - "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.3.0.tgz", - "integrity": "sha512-TQqyC89V1J/Vxx0DhJIXlq9gbbL9XFNdeLQ1+JsnZsVaSOV1z3tWfw0qZmQJGQRIfkvZcs7snQnZnOCKoldq1Q==", - "dev": true, - "dependencies": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.1", - "debug": "4.3.1", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.1.6", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.0.0", - "log-symbols": "4.0.0", - "minimatch": "3.0.4", - "ms": "2.1.3", - "nanoid": "3.1.20", - "serialize-javascript": "5.0.1", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "wide-align": "1.1.3", - "workerpool": "6.1.0", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha" - }, - "engines": { - "node": ">= 10.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" - } - }, - "node_modules/mocha/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/mocha/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/mocha/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/mocha/node_modules/js-yaml": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", - "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/mongo-dot-notation": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mongo-dot-notation/-/mongo-dot-notation-2.0.0.tgz", - "integrity": "sha512-ooU9nudAGxsMiTXYvfdL8DD9ouQoK2Na1lp4IT85J7uwJuS6770Y2plhkhrl/knMFjEQ0aJqXUMc3MAcnoCHKA==" - }, - "node_modules/mongodb": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.4.tgz", - "integrity": "sha512-Y+Ki9iXE9jI+n9bVtbTOOdK0B95d6wVGSucwtBkvQ+HIvVdTCfpVRp01FDC24uhC/Q2WXQ8Lpq3/zwtB5Op9Qw==", - "dependencies": { - "bl": "^2.2.1", - "bson": "^1.1.4", - "denque": "^1.4.1", - "require_optional": "^1.0.1", - "safe-buffer": "^5.1.2", - "saslprep": "^1.0.0" - }, - "engines": { - "node": ">=4" - }, - "optionalDependencies": { - "saslprep": "^1.0.0" - }, - "peerDependenciesMeta": { - "aws4": { - "optional": true - }, - "bson-ext": { - "optional": true - }, - "kerberos": { - "optional": true - }, - "mongodb-client-encryption": { - "optional": true - }, - "mongodb-extjson": { - "optional": true - }, - "snappy": { - "optional": true - } - } - }, - "node_modules/mongoose": { - "version": "5.11.18", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.11.18.tgz", - "integrity": "sha512-RsrPR9nhkXZbO3ml0DcmdbfeMvFNhgFrP81S6o1P+lFnDTNEKYnGNRCIL+ojD69wj7H5jJaAdZ0SJ5IlKxCHqw==", - "dependencies": { - "@types/mongodb": "^3.5.27", - "bson": "^1.1.4", - "kareem": "2.3.2", - "mongodb": "3.6.4", - "mongoose-legacy-pluralize": "1.0.2", - "mpath": "0.8.3", - "mquery": "3.2.4", - "ms": "2.1.2", - "regexp-clone": "1.0.0", - "safe-buffer": "5.2.1", - "sift": "7.0.1", - "sliced": "1.0.1" - }, - "engines": { - "node": ">=4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mongoose" - } - }, - "node_modules/mongoose-legacy-pluralize": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", - "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==", - "peerDependencies": { - "mongoose": "*" - } - }, - "node_modules/mongoose/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/mongoose/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/mpath": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.8.3.tgz", - "integrity": "sha512-eb9rRvhDltXVNL6Fxd2zM9D4vKBxjVVQNLNijlj7uoXUy19zNDsIif5zR+pWmPCWNKwAtqyo4JveQm4nfD5+eA==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/mquery": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.4.tgz", - "integrity": "sha512-uOLpp7iRX0BV1Uu6YpsqJ5b42LwYnmu0WeF/f8qgD/On3g0XDaQM6pfn0m6UxO6SM8DioZ9Bk6xxbWIGHm2zHg==", - "dependencies": { - "bluebird": "3.5.1", - "debug": "3.1.0", - "regexp-clone": "^1.0.0", - "safe-buffer": "5.1.2", - "sliced": "1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/mquery/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "node_modules/nanoid": { - "version": "3.1.20", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", - "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", - "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "node_modules/negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" - }, - "node_modules/node-addon-api": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.1.0.tgz", - "integrity": "sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw==" - }, - "node_modules/node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", - "engines": { - "node": "4.x || >=6.0.0" - } - }, - "node_modules/nodemon": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.7.tgz", - "integrity": "sha512-XHzK69Awgnec9UzHr1kc8EomQh4sjTQ8oRf8TsGrSmHDx9/UmiGG9E/mM3BuTfNeFwdNBvrqQq/RHL0xIeyFOA==", - "hasInstallScript": true, - "dependencies": { - "chokidar": "^3.2.2", - "debug": "^3.2.6", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.0.4", - "pstree.remy": "^1.1.7", - "semver": "^5.7.1", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.3", - "update-notifier": "^4.1.0" - }, - "bin": { - "nodemon": "bin/nodemon.js" - }, - "engines": { - "node": ">=8.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nodemon" - } - }, - "node_modules/nodemon/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/nodemon/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "engines": { - "node": ">=4" - } - }, - "node_modules/nodemon/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/nodemon/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/nodemon/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-url": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", - "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dependencies": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "node_modules/number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "dependencies": { - "fn.name": "1.x.x" - } - }, - "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/p-each-series": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", - "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/package-json": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", - "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", - "dependencies": { - "got": "^9.6.0", - "registry-auth-token": "^4.0.0", - "registry-url": "^5.0.0", - "semver": "^6.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/package-json/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/passport": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.1.tgz", - "integrity": "sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg==", - "dependencies": { - "passport-strategy": "1.x.x", - "pause": "0.0.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/passport-http": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/passport-http/-/passport-http-0.3.0.tgz", - "integrity": "sha1-juU9Q4C+nGDfIVGSUCmCb3cRVgM=", - "dependencies": { - "passport-strategy": "1.x.x" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/passport-jwt": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.0.tgz", - "integrity": "sha512-BwC0n2GP/1hMVjR4QpnvqA61TxenUMlmfNjYNgK0ZAs0HK4SOQkHcSv4L328blNTLtHq7DbmvyNJiH+bn6C5Mg==", - "dependencies": { - "jsonwebtoken": "^8.2.0", - "passport-strategy": "^1.0.0" - } - }, - "node_modules/passport-local": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", - "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=", - "dependencies": { - "passport-strategy": "1.x.x" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/passport-strategy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", - "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/pause": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", - "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" - }, - "node_modules/picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", - "engines": { - "node": ">=4" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", - "dependencies": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/pstree.remy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==" - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/pupa": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", - "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", - "dependencies": { - "escape-goat": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/qs": { - "version": "6.9.6", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", - "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "dependencies": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/regexp-clone": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-1.0.0.tgz", - "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==" - }, - "node_modules/regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/registry-auth-token": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", - "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", - "dependencies": { - "rc": "^1.2.8" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/registry-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", - "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", - "dependencies": { - "rc": "^1.2.8" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/require_optional": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", - "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", - "dependencies": { - "resolve-from": "^2.0.0", - "semver": "^5.1.0" - } - }, - "node_modules/require_optional/node_modules/resolve-from": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", - "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require_optional/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/requirejs": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz", - "integrity": "sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg==", - "bin": { - "r_js": "bin/r.js", - "r.js": "bin/r.js" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "dependencies": { - "lowercase-keys": "^1.0.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/saslprep": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", - "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", - "optional": true, - "dependencies": { - "sparse-bitfield": "^3.0.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver-diff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", - "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", - "dependencies": { - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/semver-diff/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "dependencies": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - }, - "node_modules/serialize-javascript": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", - "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "node_modules/setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/sift": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/sift/-/sift-7.0.1.tgz", - "integrity": "sha512-oqD7PMJ+uO6jV9EQCl0LrRw1OwsiPsiFQR5AR30heR+4Dl7jBBbDLnNvWiak20tzZlSE1H7RB30SX/1j/YYT7g==" - }, - "node_modules/signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" - }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/sliced": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", - "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sparse-bitfield": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", - "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", - "optional": true, - "dependencies": { - "memory-pager": "^1.0.2" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" - }, - "node_modules/stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", - "engines": { - "node": "*" - } - }, - "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/superagent": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", - "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", - "dev": true, - "dependencies": { - "component-emitter": "^1.2.0", - "cookiejar": "^2.1.0", - "debug": "^3.1.0", - "extend": "^3.0.0", - "form-data": "^2.3.1", - "formidable": "^1.2.0", - "methods": "^1.1.1", - "mime": "^1.4.1", - "qs": "^6.5.1", - "readable-stream": "^2.3.5" - }, - "engines": { - "node": ">= 4.0" - } - }, - "node_modules/superagent/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/superagent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/table": { - "version": "6.0.7", - "resolved": "https://registry.npmjs.org/table/-/table-6.0.7.tgz", - "integrity": "sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g==", - "dev": true, - "dependencies": { - "ajv": "^7.0.2", - "lodash": "^4.17.20", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/table/node_modules/ajv": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.1.1.tgz", - "integrity": "sha512-ga/aqDYnUy/o7vbsRTFhhTsNeXiYb5JWDIcRIeZfwRNCefwjNTVYCGdGSUrEmiu3yDK3vFvNbgJxvrQW4JXrYQ==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/table/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/tar": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz", - "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/term-size": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", - "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "node_modules/to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", - "engines": { - "node": ">=6" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/touch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", - "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", - "dependencies": { - "nopt": "~1.0.10" - }, - "bin": { - "nodetouch": "bin/nodetouch.js" - } - }, - "node_modules/touch/node_modules/nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - } - }, - "node_modules/triple-beam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", - "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" - }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "node_modules/uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" - }, - "node_modules/uglify-js": { - "version": "3.12.8", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.8.tgz", - "integrity": "sha512-fvBeuXOsvqjecUtF/l1dwsrrf5y2BCUk9AOJGzGcm6tE7vegku5u/YvqjyDaAGr422PLoLnrxg3EnRvTqsdC1w==", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/undefsafe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz", - "integrity": "sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A==", - "dependencies": { - "debug": "^2.2.0" - } - }, - "node_modules/unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "dependencies": { - "crypto-random-string": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/update-notifier": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.3.tgz", - "integrity": "sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A==", - "dependencies": { - "boxen": "^4.2.0", - "chalk": "^3.0.0", - "configstore": "^5.0.1", - "has-yarn": "^2.1.0", - "import-lazy": "^2.1.0", - "is-ci": "^2.0.0", - "is-installed-globally": "^0.3.1", - "is-npm": "^4.0.0", - "is-yarn-global": "^0.3.0", - "latest-version": "^5.0.0", - "pupa": "^2.0.1", - "semver-diff": "^3.1.1", - "xdg-basedir": "^4.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/yeoman/update-notifier?sponsor=1" - } - }, - "node_modules/update-notifier/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "dependencies": { - "prepend-http": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/v8-compile-cache": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", - "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", - "dev": true - }, - "node_modules/validator": { - "version": "13.5.2", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.5.2.tgz", - "integrity": "sha512-mD45p0rvHVBlY2Zuy3F3ESIe1h5X58GPfAtslBjY7EtTqGquZTj+VX/J4RnHWN8FKq0C9WRVt1oWAcytWRuYLQ==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dependencies": { - "string-width": "^1.0.2 || 2" - } - }, - "node_modules/wide-align/node_modules/ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "engines": { - "node": ">=4" - } - }, - "node_modules/wide-align/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "engines": { - "node": ">=4" - } - }, - "node_modules/wide-align/node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/wide-align/node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "dependencies": { - "string-width": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/winston": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", - "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", - "dependencies": { - "@dabh/diagnostics": "^2.0.2", - "async": "^3.1.0", - "is-stream": "^2.0.0", - "logform": "^2.2.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.4.0" - }, - "engines": { - "node": ">= 6.4.0" - } - }, - "node_modules/winston-transport": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", - "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", - "dependencies": { - "readable-stream": "^2.3.7", - "triple-beam": "^1.2.0" - }, - "engines": { - "node": ">= 6.4.0" - } - }, - "node_modules/winston/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" - }, - "node_modules/workerpool": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", - "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", - "dev": true - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "node_modules/xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/xss": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.8.tgz", - "integrity": "sha512-3MgPdaXV8rfQ/pNn16Eio6VXYPTkqwa0vc7GkiymmY/DqR1SE/7VPAAVZz1GJsJFrllMYO3RHfEaiUGjab6TNw==", - "dependencies": { - "commander": "^2.20.3", - "cssfilter": "0.0.10" - }, - "bin": { - "xss": "bin/xss" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/y18n": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz", - "integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser/node_modules/camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - }, "dependencies": { "@babel/code-frame": { "version": "7.12.11", @@ -4908,9 +20,9 @@ "dev": true }, "@babel/highlight": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.8.tgz", - "integrity": "sha512-4vrIhfJyfNf+lCtXC2ck1rKSzDwciqF7IWFhXXrSOUC2O5DrVp+w4c6ed4AllTxhTkUP5x2tYj41VaxdVMMRDw==", + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", + "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", @@ -5012,6 +124,15 @@ "ms": "2.1.2" } }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -5021,13 +142,13 @@ } }, "@mapbox/node-pre-gyp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.0.tgz", - "integrity": "sha512-mEaiD1CURETR/dBIiJAwz0M0Q0mH3gCW4pPMaIlNt97mdzYUVeqGcTJSamgJpS6Tg4tBHDrOJpjdh5fJTLnyNQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.1.tgz", + "integrity": "sha512-CUBdThIZMoLEQQxACwhLsPg/puxBca0abTH3ixuvBQkhjJ80Hdp99jmVjxFCOa52/tZqN9d70IbGUf+OuKDHGA==", "requires": { "detect-libc": "^1.0.3", "http-proxy-agent": "^4.0.1", - "mkdirp": "^1.0.4", + "make-dir": "^3.1.0", "node-fetch": "^2.6.1", "nopt": "^5.0.0", "npmlog": "^4.1.2", @@ -5037,47 +158,47 @@ } }, "@sentry/core": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.2.1.tgz", - "integrity": "sha512-jPqQEtafxxDtLONhCbTHh/Uq8mZRhsfbwJTSVYfPVEe/ELfFZLQK7tP6rOh7zEWKbTkE0mE6XcaoH3ZRAhgrqg==", - "requires": { - "@sentry/hub": "6.2.1", - "@sentry/minimal": "6.2.1", - "@sentry/types": "6.2.1", - "@sentry/utils": "6.2.1", + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.3.5.tgz", + "integrity": "sha512-VR2ibDy33mryD0mT6d9fGhKjdNzS2FSwwZPe9GvmNOjkyjly/oV91BKVoYJneCqOeq8fyj2lvkJGKuupdJNDqg==", + "requires": { + "@sentry/hub": "6.3.5", + "@sentry/minimal": "6.3.5", + "@sentry/types": "6.3.5", + "@sentry/utils": "6.3.5", "tslib": "^1.9.3" } }, "@sentry/hub": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.2.1.tgz", - "integrity": "sha512-pG7wCQeRpzeP6t0bT4T0X029R19dbDS3/qswF8BL6bg0AI3afjfjBAZm/fqn1Uwe/uBoMHVVdbxgJDZeQ5d4rQ==", + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.3.5.tgz", + "integrity": "sha512-ZYFo7VYKwdPVjuV9BDFiYn+MpANn6eZMz5QDBfZ2dugIvIVbuOyOOLx8PSa3ZXJoVTZZ7s2wD2fi/ZxKjNjZOQ==", "requires": { - "@sentry/types": "6.2.1", - "@sentry/utils": "6.2.1", + "@sentry/types": "6.3.5", + "@sentry/utils": "6.3.5", "tslib": "^1.9.3" } }, "@sentry/minimal": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.2.1.tgz", - "integrity": "sha512-wuSXB4Ayxv9rBEQ4pm7fnG4UU2ZPtPnnChoEfd4/mw1UthXSvmPFEn6O4pdo2G8fTkl8eqm6wT/Q7uIXMEmw+A==", + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.3.5.tgz", + "integrity": "sha512-4RqIGAU0+8iI/1sw0GYPTr4SUA88/i2+JPjFJ+qloh5ANVaNwhFPRChw+Ys9xpre8LV9JZrEsEf8AvQr4fkNbA==", "requires": { - "@sentry/hub": "6.2.1", - "@sentry/types": "6.2.1", + "@sentry/hub": "6.3.5", + "@sentry/types": "6.3.5", "tslib": "^1.9.3" } }, "@sentry/node": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-6.2.1.tgz", - "integrity": "sha512-JlixtJHS6xMzh2G4Pz7oMM8Nd40mGUALQYtuGMwW2QE3IduOaaGsn1+eVpN6PwZetMnvRIn6VVFOc2UmFIzWpA==", - "requires": { - "@sentry/core": "6.2.1", - "@sentry/hub": "6.2.1", - "@sentry/tracing": "6.2.1", - "@sentry/types": "6.2.1", - "@sentry/utils": "6.2.1", + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-6.3.5.tgz", + "integrity": "sha512-scPB+DoAEPaqkYuyb8d/gVWbFmX5PhaYSNHybeHncaP/P4itLdq/AoAWGNxl0Hj4EQokfT4OZWxaaJi7SCYnaw==", + "requires": { + "@sentry/core": "6.3.5", + "@sentry/hub": "6.3.5", + "@sentry/tracing": "6.3.5", + "@sentry/types": "6.3.5", + "@sentry/utils": "6.3.5", "cookie": "^0.4.1", "https-proxy-agent": "^5.0.0", "lru_map": "^0.3.3", @@ -5085,28 +206,28 @@ } }, "@sentry/tracing": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.2.1.tgz", - "integrity": "sha512-bvStY1SnL08wkSeVK3j9K5rivQQJdKFCPR2VYRFOCaUoleZ6ChPUnBvxQ/E2LXc0hk/y/wo1q4r5B0dfCCY+bQ==", - "requires": { - "@sentry/hub": "6.2.1", - "@sentry/minimal": "6.2.1", - "@sentry/types": "6.2.1", - "@sentry/utils": "6.2.1", + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.3.5.tgz", + "integrity": "sha512-TNKAST1ge2g24BlTfVxNp4gP5t3drbi0OVCh8h8ah+J7UjHSfdiqhd9W2h5qv1GO61gGlpWeN/TyioyQmOxu0Q==", + "requires": { + "@sentry/hub": "6.3.5", + "@sentry/minimal": "6.3.5", + "@sentry/types": "6.3.5", + "@sentry/utils": "6.3.5", "tslib": "^1.9.3" } }, "@sentry/types": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.2.1.tgz", - "integrity": "sha512-h0OV1QT+fv5ojfK5/+iEXClu33HirmvbjcQC2jf05IHj9yXIOWy6EB10S8nBjuLiiFqQiAQYj3FN9Ip4eN8NJA==" + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.3.5.tgz", + "integrity": "sha512-tY/3pkAmGYJ3F0BtwInsdt/uclNvF8aNG7XHsTPQNzk7BkNVWjCXx0sjxi6CILirl5nwNxYxVeTr2ZYAEZ/dSQ==" }, "@sentry/utils": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.2.1.tgz", - "integrity": "sha512-6kQgM/yBPdXu+3qbJnI6HBcWztN9QfiMkH++ZiKk4ERhg9d2LYWlze478uTU5Fyo/JQYcp+McpjtjpR9QIrr0g==", + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.3.5.tgz", + "integrity": "sha512-kHUcZ37QYlNzz7c9LVdApITXHaNmQK7+sw/If3M/qpff1fd5XoecA8laLfcYuz+Cw5mRhVmdhPcCRM3Xi1IGXg==", "requires": { - "@sentry/types": "6.2.1", + "@sentry/types": "6.3.5", "tslib": "^1.9.3" } }, @@ -5149,18 +270,18 @@ "dev": true }, "@types/mongodb": { - "version": "3.6.8", - "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.8.tgz", - "integrity": "sha512-8qNbL5/GFrljXc/QijcuQcUMYZ1iWNcqnJ6tneROwbfU0LsAjQ9bmq3aHi5lWXM4cyBPd2F/n9INAk/pZZttHw==", + "version": "3.6.12", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.12.tgz", + "integrity": "sha512-49aEzQD5VdHPxyd5dRyQdqEveAg9LanwrH8RQipnMuulwzKmODXIZRp0umtxi1eBUfEusRkoy8AVOMr+kVuFog==", "requires": { "@types/bson": "*", "@types/node": "*" } }, "@types/node": { - "version": "14.14.31", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.31.tgz", - "integrity": "sha512-vFHy/ezP5qI0rFgJ7aQnjDXwAMrG0KqqIH7tQG5PPv3BWBayOPIQNBjVc/P6hhdZfMx51REc6tfDNXHUio893g==" + "version": "14.14.36", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.36.tgz", + "integrity": "sha512-kjivUwDJfIjngzbhooRnOLhGYz6oRFi+L+EpMjxroDYXwDw9lHrJJ43E+dJ6KAd3V3WxWAJ/qZE9XKYHhjPOFQ==" }, "@types/superagent": { "version": "3.8.7", @@ -5202,8 +323,7 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", - "dev": true, - "requires": {} + "dev": true }, "agent-base": { "version": "6.0.2", @@ -5394,6 +514,14 @@ "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" }, + "axios": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", + "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "requires": { + "follow-redirects": "^1.10.0" + } + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -5482,6 +610,11 @@ "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" } } }, @@ -5509,9 +642,9 @@ "dev": true }, "bson": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.5.tgz", - "integrity": "sha512-kDuEzldR21lHciPQAIulLs1LZlCXdLziXI6Mb/TDkwXhb//UORJNPXgcRs2CuO4H0DcMkpfT3/ySsP3unoZjBg==" + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz", + "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==" }, "buffer-equal-constant-time": { "version": "1.0.1", @@ -5552,6 +685,15 @@ } } }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -5564,16 +706,16 @@ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" }, "chai": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.0.tgz", - "integrity": "sha512-/BFd2J30EcOwmdOgXvVsmM48l0Br0nmZPlO0uOW4XKh6kpsUumRXBgPV+IlaqFaqr9cYbeoZAM1Npx0i4A+aiA==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", + "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", "dev": true, "requires": { "assertion-error": "^1.1.0", "check-error": "^1.0.2", "deep-eql": "^3.0.1", "get-func-name": "^2.0.0", - "pathval": "^1.1.0", + "pathval": "^1.1.1", "type-detect": "^4.0.5" } }, @@ -5710,9 +852,9 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "color-string": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.4.tgz", - "integrity": "sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.5.tgz", + "integrity": "sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==", "requires": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" @@ -5840,9 +982,9 @@ "integrity": "sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4=" }, "date-fns": { - "version": "2.18.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.18.0.tgz", - "integrity": "sha512-NYyAg4wRmGVU4miKq5ivRACOODdZRY3q5WLmOJSq8djyzftYphU7dTHLcEtLqEvfqMKQ0jVv91P4BAwIjsXIcw==" + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.19.0.tgz", + "integrity": "sha512-X3bf2iTPgCAQp9wvjOQytnf5vO5rESYRXlPIVcgSbtT5OTScPcsf9eZU+B/YIkKAtYr5WeCii58BgATrNitlWg==" }, "debug": { "version": "2.6.9", @@ -6033,9 +1175,9 @@ "dev": true }, "eslint": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.21.0.tgz", - "integrity": "sha512-W2aJbXpMNofUp0ztQaF40fveSsJBjlSCSWpy//gzfTvwC+USs/nceBrKmlJOiM8r1bLwP2EuYkCqArn/6QTIgg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.25.0.tgz", + "integrity": "sha512-TVpSovpvCNpLURIScDRB6g5CYu/ZFq9GfX2hLNIV4dSBKxIWojeDODvYl3t0k0VtMxYeR8OXPCFE5+oHMlGfhw==", "dev": true, "requires": { "@babel/code-frame": "7.12.11", @@ -6055,7 +1197,7 @@ "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^5.0.0", - "globals": "^12.1.0", + "globals": "^13.6.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", @@ -6063,7 +1205,7 @@ "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.20", + "lodash": "^4.17.21", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", @@ -6258,11 +1400,11 @@ "integrity": "sha512-nE96xaxGfxiS5jP3tD3kIW1Jg9yQgX0rXCs3rCkZtmbWHEGyotwaezkLj7bnB41Z0uaOLM8W4AX6qHao4IZ2YA==" }, "express-validator": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.10.0.tgz", - "integrity": "sha512-gDtepU94EpUzgFvKO/8JzjZ4uqIF4xHekjYtcNgFDiBK6Hob3MQhPU8s/c3NaWd1xi5e5nA0oVmOJ0b0ZBO36Q==", + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.10.1.tgz", + "integrity": "sha512-joYSJdkUyKMZ2gAUvyQNmqJ7x1vhrC/IHCKWauhKfoXNF83j65KnlqEEXXynBnJRd0QrNZ/aXw9uIhS6ptG0Cg==", "requires": { - "lodash": "^4.17.20", + "lodash": "^4.17.21", "validator": "^13.5.2" } }, @@ -6296,9 +1438,9 @@ "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" }, "fecha": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", - "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz", + "integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q==" }, "file-entry-cache": { "version": "6.0.1", @@ -6373,6 +1515,11 @@ "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, + "follow-redirects": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.3.tgz", + "integrity": "sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA==" + }, "form-data": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", @@ -6430,6 +1577,11 @@ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "optional": true }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", @@ -6496,6 +1648,16 @@ "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", "dev": true }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, "get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", @@ -6518,9 +1680,9 @@ } }, "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "requires": { "is-glob": "^4.0.1" } @@ -6534,12 +1696,20 @@ } }, "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "version": "13.8.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.8.0.tgz", + "integrity": "sha512-rHtdA6+PDBIjeEvA91rpqzEvk/k3/i7EeNQiryiWuJH0Hw9cpyJMAt2jtbAwUaRdhD+573X4vWw6IcjKPasi9Q==", "dev": true, "requires": { - "type-fest": "^0.8.1" + "type-fest": "^0.20.2" + }, + "dependencies": { + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } } }, "got": { @@ -6583,11 +1753,24 @@ "wordwrap": "^1.0.0" } }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" + }, "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", @@ -6819,9 +2002,9 @@ "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" }, "is-path-inside": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", - "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==" + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==" }, "is-plain-obj": { "version": "2.1.0", @@ -7011,6 +2194,18 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", + "dev": true + }, "lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -7046,6 +2241,12 @@ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" }, + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "dev": true + }, "log-symbols": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", @@ -7079,11 +2280,6 @@ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" }, - "lru_map": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", - "integrity": "sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0=" - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -7092,6 +2288,11 @@ "yallist": "^4.0.0" } }, + "lru_map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", + "integrity": "sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0=" + }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -7146,24 +2347,24 @@ "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" }, "migrate-mongo": { - "version": "8.1.4", - "resolved": "https://registry.npmjs.org/migrate-mongo/-/migrate-mongo-8.1.4.tgz", - "integrity": "sha512-iuJiG86Qzr1w9B5j6jB6akZO0w040DTEWtKdclISaQg7KdovtXGYDmW8hehDsULY5oFGjNtJv/ABsOPlz0c5mw==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/migrate-mongo/-/migrate-mongo-8.2.2.tgz", + "integrity": "sha512-RK8zE9QGzaDZ8xN+Cyb/mUhSIA1pkj1Q/aNYeH4QB9U2UNfKej1lmxh20Ot1xFl1C62ro3hqiaZ9QErzCN3qPw==", "requires": { "cli-table3": "^0.6.0", - "commander": "^6.1.0", - "date-fns": "^2.16.1", + "commander": "^7.1.0", + "date-fns": "^2.19.0", "fn-args": "^5.0.0", - "fs-extra": "^9.0.1", - "lodash": "^4.17.20", - "mongodb": "^3.6.2", - "p-each-series": "^2.1.0" + "fs-extra": "^9.1.0", + "lodash": "^4.17.21", + "mongodb": "^3.6.4", + "p-each-series": "^2.2.0" }, "dependencies": { "commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==" + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" } } }, @@ -7226,9 +2427,9 @@ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" }, "mocha": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.3.0.tgz", - "integrity": "sha512-TQqyC89V1J/Vxx0DhJIXlq9gbbL9XFNdeLQ1+JsnZsVaSOV1z3tWfw0qZmQJGQRIfkvZcs7snQnZnOCKoldq1Q==", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.3.2.tgz", + "integrity": "sha512-UdmISwr/5w+uXLPKspgoV7/RXZwKRTiTjJ2/AC5ZiEztIoOYdfKb19+9jNmEInzx5pBsCyJQzarAxqIGBNYJhg==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", @@ -7313,9 +2514,9 @@ "integrity": "sha512-ooU9nudAGxsMiTXYvfdL8DD9ouQoK2Na1lp4IT85J7uwJuS6770Y2plhkhrl/knMFjEQ0aJqXUMc3MAcnoCHKA==" }, "mongodb": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.4.tgz", - "integrity": "sha512-Y+Ki9iXE9jI+n9bVtbTOOdK0B95d6wVGSucwtBkvQ+HIvVdTCfpVRp01FDC24uhC/Q2WXQ8Lpq3/zwtB5Op9Qw==", + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.5.tgz", + "integrity": "sha512-mQlYKw1iGbvJJejcPuyTaytq0xxlYbIoVDm2FODR+OHxyEiMR021vc32bTvamgBjCswsD54XIRwhg3yBaWqJjg==", "requires": { "bl": "^2.2.1", "bson": "^1.1.4", @@ -7326,17 +2527,17 @@ } }, "mongoose": { - "version": "5.11.18", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.11.18.tgz", - "integrity": "sha512-RsrPR9nhkXZbO3ml0DcmdbfeMvFNhgFrP81S6o1P+lFnDTNEKYnGNRCIL+ojD69wj7H5jJaAdZ0SJ5IlKxCHqw==", + "version": "5.12.5", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.12.5.tgz", + "integrity": "sha512-VVoqiELZcoI2HhHDuPpfN3qmExrtIeXSWNb1nihf4w1SJoWGXilU/g2cQgeeSMc2vAHSZd5Nv2sNPvbZHFw+pg==", "requires": { "@types/mongodb": "^3.5.27", "bson": "^1.1.4", "kareem": "2.3.2", - "mongodb": "3.6.4", + "mongodb": "3.6.6", "mongoose-legacy-pluralize": "1.0.2", "mpath": "0.8.3", - "mquery": "3.2.4", + "mquery": "3.2.5", "ms": "2.1.2", "regexp-clone": "1.0.0", "safe-buffer": "5.2.1", @@ -7344,6 +2545,19 @@ "sliced": "1.0.1" }, "dependencies": { + "mongodb": { + "version": "3.6.6", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.6.tgz", + "integrity": "sha512-WlirMiuV1UPbej5JeCMqE93JRfZ/ZzqE7nJTwP85XzjAF4rRSeq2bGCb1cjfoHLOF06+HxADaPGqT0g3SbVT1w==", + "requires": { + "bl": "^2.2.1", + "bson": "^1.1.4", + "denque": "^1.4.1", + "optional-require": "^1.0.2", + "safe-buffer": "^5.1.2", + "saslprep": "^1.0.0" + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -7359,8 +2573,7 @@ "mongoose-legacy-pluralize": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", - "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==", - "requires": {} + "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==" }, "mpath": { "version": "0.8.3", @@ -7368,9 +2581,9 @@ "integrity": "sha512-eb9rRvhDltXVNL6Fxd2zM9D4vKBxjVVQNLNijlj7uoXUy19zNDsIif5zR+pWmPCWNKwAtqyo4JveQm4nfD5+eA==" }, "mquery": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.4.tgz", - "integrity": "sha512-uOLpp7iRX0BV1Uu6YpsqJ5b42LwYnmu0WeF/f8qgD/On3g0XDaQM6pfn0m6UxO6SM8DioZ9Bk6xxbWIGHm2zHg==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.5.tgz", + "integrity": "sha512-VjOKHHgU84wij7IUoZzFRU07IAxd5kWJaDmyUzQlbjHjyoeK5TNeeo8ZsFDtTYnSgpW6n/nMNIHvE3u8Lbrf4A==", "requires": { "bluebird": "3.5.1", "debug": "3.1.0", @@ -7515,6 +2728,11 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, + "object-inspect": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", + "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==" + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -7539,6 +2757,11 @@ "fn.name": "1.x.x" } }, + "optional-require": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.0.3.tgz", + "integrity": "sha512-RV2Zp2MY2aeYK5G+B/Sps8lW5NHAzE5QClbFP15j+PWmP+T9PxlJXBOOLoSAdgwFvS4t0aMR4vpedMkbHfh0nA==" + }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -7750,9 +2973,12 @@ } }, "qs": { - "version": "6.9.6", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", - "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==" + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", + "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", + "requires": { + "side-channel": "^1.0.4" + } }, "randombytes": { "version": "2.1.0", @@ -7856,6 +3082,18 @@ "rc": "^1.2.8" } }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, "require_optional": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", @@ -7877,18 +3115,6 @@ } } }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, "requirejs": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz", @@ -7936,9 +3162,9 @@ } }, "semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "requires": { "lru-cache": "^6.0.0" } @@ -8030,6 +3256,16 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, "sift": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/sift/-/sift-7.0.1.tgz", @@ -8093,14 +3329,6 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - }, "string-width": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", @@ -8111,6 +3339,14 @@ "strip-ansi": "^6.0.0" } }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, "strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", @@ -8169,21 +3405,24 @@ } }, "table": { - "version": "6.0.7", - "resolved": "https://registry.npmjs.org/table/-/table-6.0.7.tgz", - "integrity": "sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.6.0.tgz", + "integrity": "sha512-iZMtp5tUvcnAdtHpZTWLPF0M7AgiQsURR2DwmxnJwSy8I3+cY+ozzVvYha3BOLG2TB+L0CqjIz+91htuj6yCXg==", "dev": true, "requires": { - "ajv": "^7.0.2", - "lodash": "^4.17.20", + "ajv": "^8.0.1", + "lodash.clonedeep": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.truncate": "^4.4.2", "slice-ansi": "^4.0.0", - "string-width": "^4.2.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0" }, "dependencies": { "ajv": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.1.1.tgz", - "integrity": "sha512-ga/aqDYnUy/o7vbsRTFhhTsNeXiYb5JWDIcRIeZfwRNCefwjNTVYCGdGSUrEmiu3yDK3vFvNbgJxvrQW4JXrYQ==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.1.0.tgz", + "integrity": "sha512-B/Sk2Ix7A36fs/ZkuGLIR86EdjbgR6fsAcbx9lOP/QBSXujDNbVmIS/U4Itz5k8fPFDeVZl/zQ/gJW4Jrq6XjQ==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -8293,7 +3532,8 @@ "type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true }, "type-is": { "version": "1.6.18", @@ -8318,9 +3558,9 @@ "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" }, "uglify-js": { - "version": "3.12.8", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.8.tgz", - "integrity": "sha512-fvBeuXOsvqjecUtF/l1dwsrrf5y2BCUk9AOJGzGcm6tE7vegku5u/YvqjyDaAGr422PLoLnrxg3EnRvTqsdC1w==", + "version": "3.13.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.4.tgz", + "integrity": "sha512-kv7fCkIXyQIilD5/yQy8O+uagsYIOt5cZvs890W40/e/rvjMSzJw81o9Bg0tkURxzZBROtDQhW2LFjOGoK3RZw==", "optional": true }, "undefsafe": { @@ -8408,15 +3648,15 @@ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, "v8-compile-cache": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", - "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, "validator": { - "version": "13.5.2", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.5.2.tgz", - "integrity": "sha512-mD45p0rvHVBlY2Zuy3F3ESIe1h5X58GPfAtslBjY7EtTqGquZTj+VX/J4RnHWN8FKq0C9WRVt1oWAcytWRuYLQ==" + "version": "13.6.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.6.0.tgz", + "integrity": "sha512-gVgKbdbHgtxpRyR8K0O6oFZPhhB5tT1jeEHZR0Znr9Svg03U0+r9DXWMrnRAB+HtCStDQKlaIZm42tVsVjqtjg==" }, "vary": { "version": "1.1.2", diff --git a/package.json b/package.json index 4248e72..c9fcabd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "werewolwes-assistant-api", - "version": "0.10.0", + "version": "0.11.0", "description": "Back end for the werewolves assistant", "main": "app.js", "scripts": { @@ -29,13 +29,14 @@ "devDependencies": { "chai": "^4.2.0", "chai-http": "^4.3.0", - "eslint": "^7.19.0", + "eslint": "^7.25.0", "mocha": "^8.2.1", "nodemon": "^2.0.7" }, "dependencies": { - "@sentry/node": "^6.0.3", + "@sentry/node": "^6.3.5", "apidoc": "^0.26.0", + "axios": "^0.21.1", "bcrypt": "^5.0.0", "body-parser": "^1.19.0", "colors": "^1.4.0", @@ -44,18 +45,18 @@ "dotenv": "^8.2.0", "express": "^4.17.1", "express-rate-limit": "^5.2.3", - "express-validator": "^6.9.2", + "express-validator": "^6.10.1", "jsonwebtoken": "^8.5.1", "migrate-mongo": "^8.1.4", "mongo-dot-notation": "^2.0.0", - "mongoose": "^5.11.14", + "mongoose": "5.12.5", "passport": "^0.4.1", "passport-http": "^0.3.0", "passport-jwt": "^4.0.0", "passport-local": "^1.0.0", "qs": "^6.9.6", "requirejs": "^2.3.6", - "validator": "^13.5.2", + "validator": "^13.6.0", "xss": "^1.0.8" } } diff --git a/src/controllers/Game.js b/src/controllers/Game.js index 6e0b7aa..0cbe7d0 100755 --- a/src/controllers/Game.js +++ b/src/controllers/Game.js @@ -64,15 +64,20 @@ exports.fillTickData = game => { game.turn = 1; }; -exports.checkAdditionalCardsData = ({ players, additionalCards }) => { +exports.checkAdditionalCardsData = ({ players, additionalCards, options }) => { + const { additionalCardsCount: thiefAdditionalCardsCount } = options.roles.thief; if (additionalCards && !getPlayerWithRole("thief", { players })) { throw generateError("ADDITIONAL_CARDS_NOT_ALLOWED", "`additionalCards` is not allowed when there is no `thief` in game."); } else if (!additionalCards && getPlayerWithRole("thief", { players })) { - throw generateError("NEED_ADDITIONAL_CARDS_FOR_THIEF", "2 additional cards are needed for thief."); + throw generateError("NEED_ADDITIONAL_CARDS_FOR_THIEF", `${thiefAdditionalCardsCount} additional cards are needed for thief.`); } if (additionalCards) { const additionalCardsThiefRoleNames = getAdditionalCardsThiefRoleNames(); const roles = getRoles(); + const thiefAdditionalCards = additionalCards.filter(({ for: recipient }) => recipient === "thief"); + if (thiefAdditionalCards.length !== thiefAdditionalCardsCount) { + throw generateError("THIEF_ADDITIONAL_CARDS_COUNT_NOT_RESPECTED", `Exactly ${thiefAdditionalCardsCount} additional cards are needed for thief.`); + } for (const { role: additionalRole, for: recipient } of additionalCards) { if (recipient === "thief" && !additionalCardsThiefRoleNames.includes(additionalRole)) { throw generateError("FORBIDDEN_ADDITIONAL_CARD_ROLE_FOR_THIEF", `"${additionalRole}" is not allowed in additional cards for thief.`); @@ -107,6 +112,7 @@ exports.checkRolesCompatibility = players => { }; exports.fillPlayersData = players => { + let position = 0; for (const player of players) { player.name = filterOutHTMLTags(player.name); const role = getRoles().find(playerRole => playerRole.name === player.role); @@ -115,13 +121,33 @@ exports.fillPlayersData = players => { if (role.name === "villager-villager") { player.role.isRevealed = true; } + if (player.position === undefined) { + player.position = position; + } player.isAlive = true; + position++; + } + players.sort((a, b) => a.position > b.position ? 1 : -1); +}; + +exports.checkPlayersPosition = players => { + const isOnePlayerPositionNotSet = !!players.find(({ position }) => position === undefined); + if (isOnePlayerPositionNotSet && !!players.find(({ position }) => position !== undefined)) { + throw generateError("ALL_PLAYERS_POSITION_NOT_SET", `Some players has a position and other not. You must define all position or none of them.`); + } else if (!isOnePlayerPositionNotSet) { + const playerPositionSet = [...new Set(players.map(({ position }) => position))]; + const playerMaxPosition = players.length - 1; + if (playerPositionSet.length !== players.length) { + throw generateError("PLAYERS_POSITION_NOT_UNIQUE", "Players don't all have unique position."); + } else if (players.some(({ position }) => position > playerMaxPosition)) { + throw generateError("PLAYER_POSITION_TOO_HIGH", `One player's position exceeds the maximum (${playerMaxPosition}).`); + } } }; exports.checkUniqueNameInPlayers = players => { - const playerSet = [...new Set(players.map(player => player.name))]; - if (playerSet.length !== players.length) { + const playerNameSet = [...new Set(players.map(({ name }) => name))]; + if (playerNameSet.length !== players.length) { throw generateError("PLAYERS_NAME_NOT_UNIQUE", "Players don't all have unique name."); } }; @@ -129,11 +155,12 @@ exports.checkUniqueNameInPlayers = players => { exports.checkAndFillDataBeforeCreate = async data => { await this.checkUserCurrentGames(data.gameMaster); this.checkUniqueNameInPlayers(data.players); + this.checkPlayersPosition(data.players); this.fillPlayersData(data.players); this.checkRolesCompatibility(data.players); + this.fillOptionsData(data); this.checkAdditionalCardsData(data); this.fillTickData(data); - this.fillOptionsData(data); data.waiting = await this.getWaitingQueueWithNightActions(data); }; @@ -387,8 +414,7 @@ exports.refreshNightWaitingQueue = async game => { "gameId": game._id, "turn": game.turn, "phase": "night", "play.source.name": waiting.for, "play.action": waiting.to, }; - if (currentPlay.for !== waiting.for && currentPlay.to !== waiting.to && - !await GameHistory.findOne(gameHistorySearch)) { + if (currentPlay.for !== waiting.for && currentPlay.to !== waiting.to && !await GameHistory.findOne(gameHistorySearch)) { newWaitingQueue.push(waiting); } } @@ -427,6 +453,9 @@ exports.fillWaitingQueueWithDayActions = async(game, gameHistoryEntry) => { } } } + if (await this.isTimeToElectSheriff(game)) { + game.waiting.push({ for: "all", to: "elect-sheriff" }); + } }; exports.isGroupCallableDuringTheNight = (game, group) => { @@ -435,16 +464,19 @@ exports.isGroupCallableDuringTheNight = (game, group) => { return !!cupidPlayer && !doesPlayerHaveAttribute(cupidPlayer, "powerless"); } else if (group === "charmed") { const piedPiperPlayer = getPlayerWithRole("pied-piper", game); - return piedPiperPlayer?.isAlive && piedPiperPlayer.side.current !== "werewolves" && !doesPlayerHaveAttribute(piedPiperPlayer, "powerless"); + return piedPiperPlayer?.isAlive && (piedPiperPlayer.side.current === "villagers" || !game.options.roles.piedPiper.isPowerlessIfInfected) && + !doesPlayerHaveAttribute(piedPiperPlayer, "powerless"); } const players = getPlayersWithSide(group, game); return game.tick === 1 ? !!players.length : !!players.length && players.some(({ isAlive }) => isAlive); }; exports.isWhiteWerewolfCallableDuringTheNight = async game => { + const { wakingUpInterval: whiteWerewolfWakingUpInterval } = game.options.roles.whiteWerewolf; const whiteWerewolfPlayer = getPlayerWithRole("white-werewolf", game); const lastWhiteWerewolfPlay = await GameHistory.getLastWhiteWerewolfPlay(game._id); - return whiteWerewolfPlayer?.isAlive && (!lastWhiteWerewolfPlay || game.turn - lastWhiteWerewolfPlay.turn > 1); + const turnsSinceLastWhiteWerewolfPlay = lastWhiteWerewolfPlay ? game.turn - lastWhiteWerewolfPlay.turn : undefined; + return whiteWerewolfPlayer?.isAlive && (!lastWhiteWerewolfPlay || turnsSinceLastWhiteWerewolfPlay >= whiteWerewolfWakingUpInterval); }; exports.areThreeBrothersCallableDuringTheNight = async game => { @@ -475,13 +507,13 @@ exports.isRoleCallableDuringTheNight = (game, role) => { } else if (role === "three-brothers") { return this.areThreeBrothersCallableDuringTheNight(game); } else if (role === "big-bad-wolf") { - return player.isAlive && areAllWerewolvesAlive(game); + return player.isAlive && (!game.options.roles.bigBadWolf.isPowerlessIfWerewolfDies || areAllWerewolvesAlive(game)); } else if (role === "pied-piper") { - return player.isAlive && player.side.current === "villagers"; + return player.isAlive && (player.side.current === "villagers" || !game.options.roles.piedPiper.isPowerlessIfInfected); } else if (role === "white-werewolf") { return this.isWhiteWerewolfCallableDuringTheNight(game); } - return game.tick === 1 ? !!player : !!player && player.isAlive; + return !!player && player.isAlive; }; exports.isSheriffCallableDuringTheNight = game => { @@ -492,9 +524,7 @@ exports.isSheriffCallableDuringTheNight = game => { exports.isSourceCallableDuringTheNight = (game, source, action) => { if (source === "all") { - if (action === "elect-sheriff") { - return getProp(game, "options.roles.sheriff.isEnabled", true); - } else if (action === "vote") { + if (action === "vote") { return !!getPlayerWithRole("angel", game); } } else if (source === "sheriff") { @@ -504,6 +534,15 @@ exports.isSourceCallableDuringTheNight = (game, source, action) => { return sourceType === "group" ? this.isGroupCallableDuringTheNight(game, source) : this.isRoleCallableDuringTheNight(game, source); }; +exports.isTimeToElectSheriff = async game => { + const sheriffOptions = game.options.roles.sheriff; + if (sheriffOptions.isEnabled && game.turn === sheriffOptions.electedAt.turn && game.phase === sheriffOptions.electedAt.phase) { + const allElectSheriffPlays = await GameHistory.find({ "gameId": game._id, "play.source.name": "all", "play.action": "elect-sheriff" }); + return !allElectSheriffPlays.length; + } + return false; +}; + exports.getWaitingQueueWithNightActions = async game => { let actionsOrder; if (game.turn === 1 && game.phase === "night") { @@ -512,6 +551,9 @@ exports.getWaitingQueueWithNightActions = async game => { actionsOrder = getGameTurnNightActionsOrder().filter(action => !action.isFirstNightOnly); } const waitingQueue = []; + if (await this.isTimeToElectSheriff(game)) { + waitingQueue.push({ for: "all", to: "elect-sheriff" }); + } for (const { source, action } of actionsOrder) { if (await this.isSourceCallableDuringTheNight(game, source, action)) { waitingQueue.push({ for: source, to: action }); @@ -528,12 +570,14 @@ exports.fillWaitingQueue = async(game, gameHistoryEntry) => { game.waiting.push({ for: "all", to: "vote" }); } this.decreasePlayersAttributesRemainingPhases(game); + Player.makeBearTamerGrowls(game); if (!game.waiting.length) { await this.fillWaitingQueue(game, gameHistoryEntry); } } else if (game.phase === "day") { await this.fillWaitingQueueWithDayActions(game, gameHistoryEntry); if (!game.waiting || !game.waiting.length) { + await Player.makeWerewolfDiesFromDisease(game, gameHistoryEntry); this.decreasePlayersAttributesRemainingPhases(game); game.phase = "night"; game.turn++; @@ -564,6 +608,7 @@ exports.generatePlayMethods = () => ({ "white-werewolf": Player.whiteWerewolfPlays, "stuttering-judge": () => undefined, "thief": Player.thiefPlays, + "fox": Player.foxPlays, }); exports.generateGameHistoryEntry = (game, { source, ...rest }) => ({ diff --git a/src/controllers/GameHistory.js b/src/controllers/GameHistory.js index f38ec6c..8685216 100755 --- a/src/controllers/GameHistory.js +++ b/src/controllers/GameHistory.js @@ -75,7 +75,10 @@ exports.isInfectionUsed = async gameId => !!await this.findOne({ gameId, "play.t exports.didJudgeChooseSign = async gameId => !!await this.findOne({ gameId, "play.action": "choose-sign", "play.source.name": "stuttering-judge" }); -exports.isSecondVoteRequestUsed = async gameId => !!await this.findOne({ gameId, "play.doesJudgeRequestAnotherVote": true }); +exports.doesStutteringJudgeHaveVoteRequestsLeft = async game => { + const stutteringJudgeVoteRequests = await this.find({ "gameId": game._id, "play.doesJudgeRequestAnotherVote": true }); + return stutteringJudgeVoteRequests.length < game.options.roles.stutteringJudge.voteRequestsCount; +}; exports.getLastNightPlay = gameId => { const nightPlayActions = [...turnPreNightActionsOrder, ...turnNightActionsOrder].map(({ action }) => action); diff --git a/src/controllers/Player.js b/src/controllers/Player.js index bc2abc2..84b8d3e 100644 --- a/src/controllers/Player.js +++ b/src/controllers/Player.js @@ -7,7 +7,7 @@ const { } = require("../helpers/functions/Player"); const { getPlayerWithAttribute, getPlayerWithRole, getPlayerWithId, filterOutSourcesFromWaitingQueue, - getRemainingPlayersToCharm, getRemainingVillagersToEat, getRemainingWerewolvesToEat, + getRemainingPlayersToCharm, getRemainingVillagersToEat, getRemainingWerewolvesToEat, getNearestNeighbor, } = require("../helpers/functions/Game"); const { getVillagerRoles, getWerewolfRoles, getRoles } = require("../helpers/functions/Role"); const { generateError } = require("../helpers/functions/Error"); @@ -39,6 +39,13 @@ exports.checkPiedPiperTargets = target => { } }; +exports.checkProtectTarget = async(target, game) => { + const lastProtectedTarget = await GameHistory.getLastProtectedPlayer(game._id); + if (lastProtectedTarget && lastProtectedTarget._id.toString() === target.player._id.toString() && !game.options.roles.guard.canProtectTwice) { + throw generateError("CANT_PROTECT_TWICE", `Guard can't protect the same player twice in a row.`); + } +}; + exports.checkEatTarget = async(target, game, source) => { if (source !== "white-werewolf" && target.player.side.current === "werewolves") { throw generateError("CANT_EAT_EACH_OTHER", `Werewolves target can't be a player with current side "werewolves".`); @@ -71,10 +78,7 @@ exports.checkTargetDependingOnPlay = async(target, game, { source, action }) => throw generateError("BAD_LIFE_POTION_USE", `Witch can only use life potion on a target eaten by "werewolves" of "big-bad-wolf".`); } } else if (action === "protect") { - const lastProtectedTarget = await GameHistory.getLastProtectedPlayer(game._id); - if (lastProtectedTarget && lastProtectedTarget._id.toString() === target.player._id.toString()) { - throw generateError("CANT_PROTECT_TWICE", `Guard can't protect the same player twice in a row.`); - } + await this.checkProtectTarget(target, game); } else if (action === "settle-votes") { const lastVotePlay = await GameHistory.getLastVotePlay(game._id); if (lastVotePlay && !lastVotePlay.play.targets.find(({ player }) => player._id.toString() === target.player._id.toString())) { @@ -99,6 +103,17 @@ exports.checkAndFillPlayerTarget = (target, game) => { target.player = player; }; +exports.addFoxTargets = (targets, game) => { + const leftAliveNeighbor = getNearestNeighbor(targets[0].player, game.players, "left", { isAlive: true }); + const rightAliveNeighbor = getNearestNeighbor(targets[0].player, game.players, "right", { isAlive: true }); + if (leftAliveNeighbor) { + targets.unshift({ player: leftAliveNeighbor._id }); + } + if (rightAliveNeighbor && leftAliveNeighbor !== rightAliveNeighbor) { + targets.push({ player: rightAliveNeighbor._id }); + } +}; + exports.checkTargetStructure = (target, action) => { if (target.player === undefined) { throw generateError("BAD_TARGET_STRUCTURE", `Bad target structure. Field "player" is missing.`); @@ -134,10 +149,15 @@ exports.checkAndFillTargets = async(targets, game, options) => { if (!targets || !targets.length) { return; } - for (let i = 0; i < targets.length; i++) { - this.checkTargetStructure(targets[i], options.play.action); - this.checkAndFillPlayerTarget(targets[i], game); - await this.checkTargetDependingOnPlay(targets[i], game, options.play); + for (const target of targets) { + this.checkTargetStructure(target, options.play.action); + } + if (options.play.action === "sniff") { + this.addFoxTargets(targets, game); + } + for (const target of targets) { + this.checkAndFillPlayerTarget(target, game); + await this.checkTargetDependingOnPlay(target, game, options.play); } this.checkUniqueTargets(targets); await this.checkAllTargetsDependingOnAction(targets, game, options.play.action); @@ -205,7 +225,7 @@ exports.applyConsequencesDependingOnKilledPlayerRole = async(player, action, gam if (player.role.current === "hunter" && !doesPlayerHaveAttribute(player, "powerless")) { this.insertActionImmediately(game, { for: "hunter", to: "shoot" }); } else if (player.role.current === "ancient") { - if (ancientRevengeActions.includes(action)) { + if (ancientRevengeActions.includes(action) && game.options.roles.ancient.doesTakeHisRevenge) { for (const { _id, isAlive, side } of game.players) { if (isAlive && side.original === "villagers") { this.addPlayerAttribute(_id, "powerless", game); @@ -218,8 +238,14 @@ exports.applyConsequencesDependingOnKilledPlayerRole = async(player, action, gam if (idiotPlayer?.isAlive && idiotPlayer.role.isRevealed && game.options.roles.idiot.doesDieOnAncientDeath) { await this.killPlayer(idiotPlayer._id, "reconsider", game, gameHistoryEntry); } - } else if (player.role.current === "scapegoat" && action === "vote" && options?.nominatedPlayers?.length > 1) { + } else if (player.role.current === "scapegoat" && action === "vote" && options?.nominatedPlayers?.length > 1 && + !doesPlayerHaveAttribute(player, "powerless")) { this.insertActionImmediately(game, { for: "scapegoat", to: "ban-voting" }); + } else if (player.role.current === "rusty-sword-knight" && action === "eat" && !doesPlayerHaveAttribute(player, "powerless")) { + const leftAliveWerewolfNeighbor = getNearestNeighbor(player._id, game.players, "left", { isAlive: true, side: "werewolves" }); + if (leftAliveWerewolfNeighbor) { + this.addPlayerAttribute(leftAliveWerewolfNeighbor._id, "contaminated", game); + } } }; @@ -234,7 +260,7 @@ exports.fillMurderedData = (player, action, gameHistoryEntry, options) => { } }; -exports.isAncientKillable = async(action, gameHistoryEntry) => { +exports.isAncientKillable = async(action, game, gameHistoryEntry) => { if (action !== "eat") { return true; } @@ -254,7 +280,7 @@ exports.isAncientKillable = async(action, gameHistoryEntry) => { "play.source.name": "guard", "play.targets": { $elemMatch: { "player.role.current": "ancient" } }, }; - let livesCount = 2; + let livesCount = game.options.roles.ancient.livesCountAgainstWerewolves; for (const werewolvesPlay of werewolvesPlaysOnAncient) { if (werewolvesPlay.play.action === "eat" && werewolvesPlay.play.targets.find(({ player }) => player.role.current === "ancient") && !await GameHistory.findOne({ ...ancientSavedByWitchPlaySearch, turn: werewolvesPlay.turn }) && @@ -265,15 +291,15 @@ exports.isAncientKillable = async(action, gameHistoryEntry) => { return livesCount <= 0; }; -exports.isPlayerKillable = async({ role, attributes }, action, alreadyRevealed, gameHistoryEntry) => role.current !== "ancient" && - role.current !== "idiot" || role.current === "ancient" && await this.isAncientKillable(action, gameHistoryEntry) || +exports.isPlayerKillable = async({ role, attributes }, action, alreadyRevealed, game, gameHistoryEntry) => role.current !== "ancient" && + role.current !== "idiot" || role.current === "ancient" && await this.isAncientKillable(action, game, gameHistoryEntry) || role.current === "idiot" && isIdiotKillable(action, { attributes }, alreadyRevealed); exports.killPlayer = async(playerId, action, game, gameHistoryEntry, options = {}) => { const player = getPlayerWithId(playerId, game); if (player?.isAlive && (action !== "eat" || canBeEaten(player, game))) { const alreadyRevealed = player.role.isRevealed; - if (!alreadyRevealed && (player.role.current !== "ancient" || await this.isAncientKillable(action, gameHistoryEntry))) { + if (!alreadyRevealed && (player.role.current !== "ancient" || await this.isAncientKillable(action, game, gameHistoryEntry))) { player.role.isRevealed = true; this.insertRevealedPlayerIntoGameHistoryEntry(player, gameHistoryEntry); if (player.role.current === "idiot" && !doesPlayerHaveAttribute(player, "powerless") && @@ -281,7 +307,7 @@ exports.killPlayer = async(playerId, action, game, gameHistoryEntry, options = { this.addPlayerAttribute(player._id, "cant-vote", game, { source: "all" }); } } - if (await this.isPlayerKillable(player, action, alreadyRevealed, gameHistoryEntry)) { + if (await this.isPlayerKillable(player, action, alreadyRevealed, game, gameHistoryEntry)) { player.isAlive = false; this.fillMurderedData(player, action, gameHistoryEntry, options); if (action === "vote") { @@ -423,8 +449,8 @@ exports.checkJudgeSecondVoteRequest = async game => { throw generateError("STUTTERING_JUDGE_POWERLESS", "Stuttering judge is powerless and so can't request another vote."); } else if (!await GameHistory.didJudgeChooseSign(game._id)) { throw generateError("STUTTERING_JUDGE_DIDNT_CHOOSE_SIGN_YET", `Stuttering judge didn't choose his sign yet and so can't request another vote.`); - } else if (await GameHistory.isSecondVoteRequestUsed(game._id)) { - throw generateError("ONLY_ONE_SECOND_VOTE_REQUEST", "Second vote request has been already made."); + } else if (!await GameHistory.doesStutteringJudgeHaveVoteRequestsLeft(game)) { + throw generateError("VOTE_REQUESTS_EXCEEDED", "Stuttering judge doesn't have any vote request left."); } }; @@ -444,13 +470,24 @@ exports.checkAndFillVotes = async(votes, game, options) => { } }; +exports.foxPlays = async(play, game) => { + const { targets } = play; + await this.checkAndFillTargets(targets, game, { canBeUnset: true, canBeEmpty: true, expectedLength: 1, play }); + if (targets?.length && !targets.find(({ player }) => player.side.current === "werewolves") && + game.options.roles.fox.isPowerlessIfMissesWerewolf) { + const foxPlayer = getPlayerWithRole("fox", game); + this.addPlayerAttribute(foxPlayer._id, "powerless", game, { source: "fox" }); + } +}; + exports.checkAndGetChosenCard = (chosenCardId, game) => { const chosenCard = chosenCardId ? game.additionalCards.find(({ _id }) => _id.toString() === chosenCardId.toString()) : null; const werewolvesRoles = getWerewolfRoles(); const thiefAdditionalCards = game.additionalCards.filter(({ for: recipient }) => recipient === "thief"); if (chosenCardId && !chosenCard) { throw generateError("CHOSEN_CARD_NOT_FOUND", `The chosen card with id "${chosenCardId}" is not found in additional cards.`); - } else if (!chosenCard && thiefAdditionalCards.every(({ role: roleName }) => werewolvesRoles.find(({ name }) => name === roleName))) { + } else if (!chosenCard && thiefAdditionalCards.every(({ role: roleName }) => werewolvesRoles.find(({ name }) => name === roleName)) && + game.options.roles.thief.mustChooseBetweenWerewolves) { throw generateError("THIEF_MUST_STEAL", `As all additional cards for thief are on the werewolves side, he must choose one of them.`); } if (chosenCard) { @@ -482,7 +519,9 @@ exports.whiteWerewolfPlays = async(play, game) => { exports.piedPiperPlays = async(play, game) => { const { targets } = play; - const targetsExpectedLength = getRemainingPlayersToCharm(game).length === 1 ? 1 : 2; + const { charmedPeopleCountPerNight: maxCharmedPeople } = game.options.roles.piedPiper; + const remainingPlayersToCharmCount = getRemainingPlayersToCharm(game).length; + const targetsExpectedLength = remainingPlayersToCharmCount >= maxCharmedPeople ? maxCharmedPeople : remainingPlayersToCharmCount; await this.checkAndFillTargets(targets, game, { expectedLength: targetsExpectedLength, play }); for (const { player } of targets) { this.addPlayerAttribute(player._id, "charmed", game); @@ -558,11 +597,11 @@ exports.werewolvesPlay = async(play, game, gameHistoryEntry) => { if (targets[0].isInfected) { const infectedPlayer = getPlayerWithId(targets[0].player._id, game); if (infectedPlayer) { - if (infectedPlayer.role.current === "ancient" && !await this.isAncientKillable(play.action, gameHistoryEntry)) { + if (infectedPlayer.role.current === "ancient" && !await this.isAncientKillable(play.action, game, gameHistoryEntry)) { this.addPlayerAttribute(targets[0].player._id, "eaten", game); } else { infectedPlayer.side.current = "werewolves"; - if (infectedPlayer.role.current === "pied-piper") { + if (infectedPlayer.role.current === "pied-piper" && game.options.roles.piedPiper.isPowerlessIfInfected) { filterOutSourcesFromWaitingQueue(game, ["pied-piper", "charmed"]); } } @@ -672,4 +711,23 @@ exports.drankDeathPotion = async(game, play, gameHistoryEntry) => { const poisonedPlayer = getPlayerWithAttribute("drank-death-potion", game); await this.killPlayer(poisonedPlayer._id, "use-potion", game, gameHistoryEntry); this.removePlayerAttribute(poisonedPlayer._id, "drank-death-potion", game); +}; + +exports.makeBearTamerGrowls = game => { + const bearTamerPlayer = getPlayerWithRole("bear-tamer", game); + if (bearTamerPlayer && !doesPlayerHaveAttribute(bearTamerPlayer, "powerless")) { + const leftAliveNeighbor = getNearestNeighbor(bearTamerPlayer._id, game.players, "left", { isAlive: true }); + const rightAliveNeighbor = getNearestNeighbor(bearTamerPlayer._id, game.players, "right", { isAlive: true }); + if (bearTamerPlayer.side.current === "werewolves" && game.options.roles.bearTamer.doesGrowlIfInfected || + leftAliveNeighbor?.side.current === "werewolves" || rightAliveNeighbor?.side.current === "werewolves") { + this.addPlayerAttribute(bearTamerPlayer._id, "growls", game); + } + } +}; + +exports.makeWerewolfDiesFromDisease = async(game, gameHistoryEntry) => { + const contaminatedWerewolf = getPlayerWithAttribute("contaminated", game); + if (contaminatedWerewolf) { + await this.killPlayer(contaminatedWerewolf._id, "disease", game, gameHistoryEntry); + } }; \ No newline at end of file diff --git a/src/controllers/User.js b/src/controllers/User.js index ac013fc..87bf304 100755 --- a/src/controllers/User.js +++ b/src/controllers/User.js @@ -1,3 +1,4 @@ +const axios = require("axios"); const { flatten } = require("mongo-dot-notation"); const { sign } = require("jsonwebtoken"); const passport = require("passport"); @@ -8,7 +9,24 @@ const { checkJWTUserRights } = require("../helpers/functions/User"); const { checkRequestData } = require("../helpers/functions/Express"); const Config = require("../../config"); +exports.checkDataBeforeCreate = async data => { + const existingUser = await this.findOne({ email: data.email }); + if (existingUser) { + if (existingUser.registration.method === data.registration.method) { + throw generateError("EMAIL_EXISTS", "The email provided already exists."); + } + if (existingUser.registration.method === "local") { + throw generateError("EMAIL_EXISTS_WITH_LOCAL_REGISTRATION", "The email provided already exists with local registration."); + } else if (existingUser.registration.method === "facebook") { + throw generateError("EMAIL_EXISTS_WITH_FACEBOOK_REGISTRATION", "The email provided already exists with facebook registration."); + } else if (existingUser.registration.method === "google") { + throw generateError("EMAIL_EXISTS_WITH_GOOGLE_REGISTRATION", "The email provided already exists with google registration."); + } + } +}; + exports.create = async(data, options = {}) => { + await this.checkDataBeforeCreate(data); const { toJSON } = options; delete options.toJSON; if (!Array.isArray(data)) { @@ -53,6 +71,7 @@ exports.postUser = async(req, res) => { try { const { body } = checkRequestData(req); await this.generateSaltAndHash(body); + body.registration = { method: "local" }; const newUser = await this.create(body, { toJSON: true }); delete newUser.password; res.status(200).json(newUser); @@ -90,6 +109,8 @@ exports.getUser = async(req, res) => { } }; +exports.getJWT = user => sign({ userId: user._id }, Config.app.JWTSecret); + exports.login = (req, res) => { try { checkRequestData(req); @@ -101,11 +122,71 @@ exports.login = (req, res) => { if (loginErr) { res.status(500).send(loginErr); } - const token = sign({ userId: user._id, exp: Math.floor(Date.now() / 1000) + 3600 * 24 }, Config.app.JWTSecret); + const token = this.getJWT(user); return res.status(200).json({ token }); }); })(req, res); } catch (e) { sendError(res, e); } +}; + +exports.getFacebookUser = async accessToken => { + try { + const { data: facebookApp } = await axios.get(`https://graph.facebook.com/app?access_token=${accessToken}`); + if (facebookApp.id !== Config.facebook.app.ID) { + throw generateError("BAD_FACEBOOK_ACCESS_TOKEN", `Access token "${accessToken}" doesn't belong to the Werewolves Assistant Facebook app.`); + } + const { data: facebookUser } = await axios.get(`https://graph.facebook.com/me?fields=email&access_token=${accessToken}`); + return facebookUser; + } catch (e) { + throw generateError("BAD_FACEBOOK_ACCESS_TOKEN", `Access token "${accessToken}" doesn't allow to get user info.`); + } +}; + +exports.loginWithFacebook = async(req, res) => { + try { + const { body } = checkRequestData(req); + const facebookUser = await this.getFacebookUser(body.accessToken); + if (!facebookUser.email) { + throw generateError("NEED_FACEBOOK_EMAIL_PERMISSION", `You need to share your email to login with Facebook.`); + } + const facebookUserData = { email: facebookUser.email, registration: { method: "facebook" } }; + let user = await this.findOne(facebookUserData); + if (!user) { + user = await this.create(facebookUserData); + } + const token = this.getJWT(user); + res.status(200).json({ token }); + } catch (e) { + sendError(res, e); + } +}; + +exports.getGoogleUser = async idToken => { + try { + const { data } = await axios.get(`https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=${idToken}`); + if (data.azp !== Config.google.client.ID) { + throw generateError("BAD_GOOGLE_ID_TOKEN", `Id token "${idToken}" doesn't belong to the Werewolves Assistant Google app.`); + } + return data; + } catch (e) { + throw generateError("BAD_GOOGLE_ID_TOKEN", `Id token "${idToken}" doesn't allow to get user info.`); + } +}; + +exports.loginWithGoogle = async(req, res) => { + try { + const { body } = checkRequestData(req); + const googleUser = await this.getGoogleUser(body.idToken); + const googleUserData = { email: googleUser.email, registration: { method: "google" } }; + let user = await this.findOne(googleUserData); + if (!user) { + user = await this.create(googleUserData); + } + const token = this.getJWT(user); + res.status(200).json({ token }); + } catch (e) { + sendError(res, e); + } }; \ No newline at end of file diff --git a/src/db/schemas/Game.js b/src/db/schemas/Game.js index 21add4a..8b83497 100755 --- a/src/db/schemas/Game.js +++ b/src/db/schemas/Game.js @@ -8,22 +8,61 @@ const { const { getPlayerActions } = require("../../helpers/functions/Player"); const gameOptions = { + repartition: { + isHidden: { + type: Boolean, + default: false, + }, + }, roles: { + areRevealedOnDeath: { + type: Boolean, + default: true, + }, sheriff: { isEnabled: { type: Boolean, default: true, }, + electedAt: { + turn: { + type: Number, + default: 1, + }, + phase: { + type: String, + enum: getGamePhases(), + default: "night", + }, + }, hasDoubledVote: { type: Boolean, default: true, }, }, + bigBadWolf: { + isPowerlessIfWerewolfDies: { + type: Boolean, + default: true, + }, + }, + whiteWerewolf: { + wakingUpInterval: { + type: Number, + default: 2, + min: 1, + max: 5, + }, + }, seer: { isTalkative: { type: Boolean, default: true, }, + canSeeRoles: { + type: Boolean, + default: true, + }, }, littleGirl: { isProtectedByGuard: { @@ -31,6 +70,24 @@ const gameOptions = { default: false, }, }, + guard: { + canProtectTwice: { + type: Boolean, + default: false, + }, + }, + ancient: { + livesCountAgainstWerewolves: { + type: Number, + default: 2, + min: 1, + max: 5, + }, + doesTakeHisRevenge: { + type: Boolean, + default: true, + }, + }, idiot: { doesDieOnAncientDeath: { type: Boolean, @@ -53,6 +110,62 @@ const gameOptions = { max: 5, }, }, + fox: { + isPowerlessIfMissesWerewolf: { + type: Boolean, + default: true, + }, + }, + bearTamer: { + doesGrowlIfInfected: { + type: Boolean, + default: true, + }, + }, + stutteringJudge: { + voteRequestsCount: { + type: Number, + default: 1, + min: 1, + max: 5, + }, + }, + wildChild: { + isTransformationRevealed: { + type: Boolean, + default: false, + }, + }, + dogWolf: { + isChosenSideRevealed: { + type: Boolean, + default: false, + }, + }, + thief: { + mustChooseBetweenWerewolves: { + type: Boolean, + default: true, + }, + additionalCardsCount: { + type: Number, + default: 2, + min: 1, + max: 5, + }, + }, + piedPiper: { + charmedPeopleCountPerNight: { + type: Number, + default: 2, + min: 1, + max: 5, + }, + isPowerlessIfInfected: { + type: Boolean, + default: true, + }, + }, raven: { markPenalty: { type: Number, diff --git a/src/db/schemas/Player.js b/src/db/schemas/Player.js index 1677e88..2178f60 100755 --- a/src/db/schemas/Player.js +++ b/src/db/schemas/Player.js @@ -31,21 +31,6 @@ const PlayerAttributeSchema = new Schema({ versionKey: false, }); -const PlayerPowerSchema = new Schema({ - action: { - type: String, - required: true, - }, - used: { - type: Boolean, - required: true, - }, -}, { - _id: false, - timestamps: false, - versionKey: false, -}); - const MurderedSchema = new Schema({ by: { type: String, @@ -100,9 +85,9 @@ const PlayerSchema = new Schema({ type: [PlayerAttributeSchema], default: undefined, }, - powers: { - type: [PlayerPowerSchema], - default: undefined, + position: { + type: Number, + min: 0, }, isAlive: { type: Boolean, diff --git a/src/db/schemas/User.js b/src/db/schemas/User.js index e1d7d56..6ae65e1 100755 --- a/src/db/schemas/User.js +++ b/src/db/schemas/User.js @@ -1,4 +1,5 @@ const { Schema } = require("mongoose"); +const { getRegistrationMethods } = require("../../helpers/functions/User"); const userSchema = new Schema({ email: { @@ -6,9 +7,13 @@ const userSchema = new Schema({ required: true, unique: true, }, - password: { - type: String, - required: true, + password: { type: String }, + registration: { + method: { + type: String, + enum: getRegistrationMethods(), + default: "local", + }, }, }, { timestamps: true, diff --git a/src/helpers/constants/Error.js b/src/helpers/constants/Error.js index 0e4f72c..db2261c 100644 --- a/src/helpers/constants/Error.js +++ b/src/helpers/constants/Error.js @@ -211,7 +211,7 @@ exports.errorMetadata = { code: 53, HTTPCode: 400, }, - ONLY_ONE_SECOND_VOTE_REQUEST: { + VOTE_REQUESTS_EXCEEDED: { code: 54, HTTPCode: 400, }, @@ -259,4 +259,44 @@ exports.errorMetadata = { code: 65, HTTPCode: 400, }, + ALL_PLAYERS_POSITION_NOT_SET: { + code: 66, + HTTPCode: 400, + }, + PLAYERS_POSITION_NOT_UNIQUE: { + code: 67, + HTTPCode: 400, + }, + PLAYER_POSITION_TOO_HIGH: { + code: 68, + HTTPCode: 400, + }, + THIEF_ADDITIONAL_CARDS_COUNT_NOT_RESPECTED: { + code: 69, + HTTPCode: 400, + }, + BAD_FACEBOOK_ACCESS_TOKEN: { + code: 70, + HTTPCode: 400, + }, + NEED_FACEBOOK_EMAIL_PERMISSION: { + code: 71, + HTTPCode: 400, + }, + EMAIL_EXISTS_WITH_LOCAL_REGISTRATION: { + code: 72, + HTTPCode: 400, + }, + EMAIL_EXISTS_WITH_FACEBOOK_REGISTRATION: { + code: 73, + HTTPCode: 400, + }, + EMAIL_EXISTS_WITH_GOOGLE_REGISTRATION: { + code: 74, + HTTPCode: 400, + }, + BAD_GOOGLE_ID_TOKEN: { + code: 75, + HTTPCode: 400, + }, }; \ No newline at end of file diff --git a/src/helpers/constants/Game.js b/src/helpers/constants/Game.js index c4658b4..7b93286 100644 --- a/src/helpers/constants/Game.js +++ b/src/helpers/constants/Game.js @@ -20,14 +20,14 @@ exports.populate = [ ]; exports.turnNightActionsOrder = [ - { source: "all", action: "elect-sheriff", isFirstNightOnly: true }, { source: "all", action: "vote", isFirstNightOnly: true }, { source: "thief", action: "choose-card", isFirstNightOnly: true }, { source: "dog-wolf", action: "choose-side", isFirstNightOnly: true }, { source: "cupid", action: "charm", isFirstNightOnly: true }, + { source: "seer", action: "look" }, + { source: "fox", action: "sniff" }, { source: "lovers", action: "meet-each-other", isFirstNightOnly: true }, { source: "stuttering-judge", action: "choose-sign", isFirstNightOnly: true }, - { source: "seer", action: "look" }, { source: "two-sisters", action: "meet-each-other" }, { source: "three-brothers", action: "meet-each-other" }, { source: "wild-child", action: "choose-model", isFirstNightOnly: true }, @@ -44,13 +44,30 @@ exports.turnNightActionsOrder = [ exports.findFields = ["status"]; exports.defaultGameOptions = { + repartition: { isHidden: false }, roles: { - sheriff: { isEnabled: true, hasDoubledVote: true }, - seer: { isTalkative: true }, + areRevealedOnDeath: true, + sheriff: { + isEnabled: true, + electedAt: { turn: 1, phase: "night" }, + hasDoubledVote: true, + }, + bigBadWolf: { isPowerlessIfWerewolfDies: true }, + whiteWerewolf: { wakingUpInterval: 2 }, + seer: { isTalkative: true, canSeeRoles: true }, littleGirl: { isProtectedByGuard: false }, + guard: { canProtectTwice: false }, + ancient: { livesCountAgainstWerewolves: 2, doesTakeHisRevenge: true }, idiot: { doesDieOnAncientDeath: true }, twoSisters: { wakingUpInterval: 2 }, threeBrothers: { wakingUpInterval: 2 }, + fox: { isPowerlessIfMissesWerewolf: true }, + bearTamer: { doesGrowlIfInfected: true }, + stutteringJudge: { voteRequestsCount: 1 }, + wildChild: { isTransformationRevealed: false }, + dogWolf: { isChosenSideRevealed: false }, + thief: { mustChooseBetweenWerewolves: true, additionalCardsCount: 2 }, + piedPiper: { charmedPeopleCountPerNight: 2, isPowerlessIfInfected: true }, raven: { markPenalty: 2 }, }, }; diff --git a/src/helpers/constants/Player.js b/src/helpers/constants/Player.js index 7bb9713..3801d3a 100644 --- a/src/helpers/constants/Player.js +++ b/src/helpers/constants/Player.js @@ -12,9 +12,12 @@ exports.playerAttributes = [ { name: "in-love", source: "cupid" }, { name: "worshiped", source: "wild-child" }, { name: "powerless", source: "ancient" }, + { name: "powerless", source: "fox" }, { name: "cant-vote", source: "scapegoat", remainingPhases: 2 }, { name: "cant-vote", source: "all" }, { name: "charmed", source: "pied-piper" }, + { name: "growls", source: "bear-tamer", remainingPhases: 1 }, + { name: "contaminated", source: "rusty-sword-knight", remainingPhases: 2 }, ]; exports.playerActions = [ @@ -35,6 +38,7 @@ exports.playerActions = [ "ban-voting", "choose-sign", "choose-card", + "sniff", ]; exports.murderedPossibilities = [ @@ -47,4 +51,5 @@ exports.murderedPossibilities = [ { by: "all", of: "vote" }, { by: "cupid", of: "charm" }, { by: "all", of: "reconsider" }, + { by: "rusty-sword-knight", of: "disease" }, ]; \ No newline at end of file diff --git a/src/helpers/constants/Role.js b/src/helpers/constants/Role.js index 70a6f13..e6b586e 100644 --- a/src/helpers/constants/Role.js +++ b/src/helpers/constants/Role.js @@ -3,30 +3,33 @@ exports.sideNames = ["villagers", "werewolves"]; exports.groupNames = [...this.sideNames, "lovers", "charmed"]; exports.roles = [ - { name: "werewolf", side: "werewolves", maxInGame: 39 }, - { name: "big-bad-wolf", side: "werewolves", maxInGame: 1, recommendedMinPlayers: 15 }, - { name: "vile-father-of-wolves", side: "werewolves", maxInGame: 1, recommendedMinPlayers: 12 }, - { name: "white-werewolf", side: "werewolves", maxInGame: 1, recommendedMinPlayers: 12 }, - { name: "villager", side: "villagers", maxInGame: 39 }, - { name: "villager-villager", side: "villagers", maxInGame: 1 }, - { name: "seer", side: "villagers", maxInGame: 1 }, - { name: "cupid", side: "villagers", maxInGame: 1 }, - { name: "witch", side: "villagers", maxInGame: 1 }, - { name: "hunter", side: "villagers", maxInGame: 1 }, - { name: "little-girl", side: "villagers", maxInGame: 1 }, - { name: "guard", side: "villagers", maxInGame: 1 }, - { name: "ancient", side: "villagers", maxInGame: 1 }, - { name: "scapegoat", side: "villagers", maxInGame: 1 }, - { name: "idiot", side: "villagers", maxInGame: 1 }, - { name: "stuttering-judge", side: "villagers", maxInGame: 1 }, - { name: "two-sisters", side: "villagers", minInGame: 2, maxInGame: 2, recommendedMinPlayers: 12 }, - { name: "three-brothers", side: "villagers", minInGame: 3, maxInGame: 3, recommendedMinPlayers: 15 }, - { name: "wild-child", side: "villagers", maxInGame: 1 }, - { name: "dog-wolf", side: "villagers", maxInGame: 1 }, - { name: "thief", side: "villagers", maxInGame: 1 }, - { name: "angel", side: "villagers", maxInGame: 1 }, - { name: "pied-piper", side: "villagers", maxInGame: 1, recommendedMinPlayers: 12 }, - { name: "raven", side: "villagers", maxInGame: 1 }, + { name: "werewolf", side: "werewolves", maxInGame: 99, type: "werewolf" }, + { name: "big-bad-wolf", side: "werewolves", maxInGame: 1, recommendedMinPlayers: 15, type: "werewolf" }, + { name: "vile-father-of-wolves", side: "werewolves", maxInGame: 1, recommendedMinPlayers: 12, type: "werewolf" }, + { name: "white-werewolf", side: "werewolves", maxInGame: 1, recommendedMinPlayers: 12, type: "lonely" }, + { name: "villager", side: "villagers", maxInGame: 99, type: "villager" }, + { name: "villager-villager", side: "villagers", maxInGame: 1, type: "villager" }, + { name: "seer", side: "villagers", maxInGame: 1, type: "villager" }, + { name: "cupid", side: "villagers", maxInGame: 1, type: "villager" }, + { name: "witch", side: "villagers", maxInGame: 1, type: "villager" }, + { name: "hunter", side: "villagers", maxInGame: 1, type: "villager" }, + { name: "little-girl", side: "villagers", maxInGame: 1, type: "villager" }, + { name: "guard", side: "villagers", maxInGame: 1, type: "villager" }, + { name: "ancient", side: "villagers", maxInGame: 1, type: "villager" }, + { name: "scapegoat", side: "villagers", maxInGame: 1, type: "villager" }, + { name: "idiot", side: "villagers", maxInGame: 1, type: "villager" }, + { name: "two-sisters", side: "villagers", minInGame: 2, maxInGame: 2, recommendedMinPlayers: 12, type: "villager" }, + { name: "three-brothers", side: "villagers", minInGame: 3, maxInGame: 3, recommendedMinPlayers: 15, type: "villager" }, + { name: "fox", side: "villagers", maxInGame: 1, recommendedMinPlayers: 12, type: "villager" }, + { name: "bear-tamer", side: "villagers", maxInGame: 1, type: "villager" }, + { name: "stuttering-judge", side: "villagers", maxInGame: 1, type: "villager" }, + { name: "rusty-sword-knight", side: "villagers", maxInGame: 1, type: "villager" }, + { name: "thief", side: "villagers", maxInGame: 1, type: "ambiguous" }, + { name: "wild-child", side: "villagers", maxInGame: 1, type: "ambiguous" }, + { name: "dog-wolf", side: "villagers", maxInGame: 1, type: "ambiguous" }, + { name: "angel", side: "villagers", maxInGame: 1, type: "lonely" }, + { name: "pied-piper", side: "villagers", maxInGame: 1, recommendedMinPlayers: 12, type: "lonely" }, + { name: "raven", side: "villagers", maxInGame: 1, type: "villager" }, ]; exports.roleNames = this.roles.map(({ name }) => name); \ No newline at end of file diff --git a/src/helpers/constants/User.js b/src/helpers/constants/User.js new file mode 100644 index 0000000..ce47994 --- /dev/null +++ b/src/helpers/constants/User.js @@ -0,0 +1 @@ +exports.registrationMethods = ["local", "facebook", "google"]; \ No newline at end of file diff --git a/src/helpers/functions/Game.js b/src/helpers/functions/Game.js index 2f3bc48..e0edfd7 100644 --- a/src/helpers/functions/Game.js +++ b/src/helpers/functions/Game.js @@ -28,10 +28,11 @@ exports.getRemainingWerewolvesToEat = game => game.players.filter(({ side, role, role.current !== "white-werewolf"); exports.hasPiedPiperWon = game => { + const { isPowerlessIfInfected } = game.options.roles.piedPiper; const piedPiperPlayer = this.getPlayerWithRole("pied-piper", game); const remainingPlayersToCharm = this.getRemainingPlayersToCharm(game); - return piedPiperPlayer?.isAlive && !doesPlayerHaveAttribute(piedPiperPlayer, "powerless") && piedPiperPlayer.side.current === "villagers" && - !remainingPlayersToCharm.length; + return piedPiperPlayer?.isAlive && !doesPlayerHaveAttribute(piedPiperPlayer, "powerless") && + (!isPowerlessIfInfected || piedPiperPlayer.side.current === "villagers") && !remainingPlayersToCharm.length; }; exports.hasAngelWon = game => { @@ -108,4 +109,27 @@ exports.getVotesResults = () => JSON.parse(JSON.stringify(votesResults)); exports.getAdditionalCardsForRoleNames = () => JSON.parse(JSON.stringify(additionalCardsForRoleNames)); -exports.getAdditionalCardsThiefRoleNames = () => JSON.parse(JSON.stringify(additionalCardsThiefRoleNames)); \ No newline at end of file +exports.getAdditionalCardsThiefRoleNames = () => JSON.parse(JSON.stringify(additionalCardsThiefRoleNames)); + +exports.getNearestNeighbor = (playerId, players, direction, options = {}) => { + let checkedNeighborsCount = 0; + const player = players.find(({ _id }) => _id.toString() === playerId.toString()); + if (!player) { + return null; + } + let checkingNeighborPosition = player.position; + while (checkedNeighborsCount < players.length) { + const checkingNeighbor = players[checkingNeighborPosition]; + if (checkingNeighbor.position !== player.position && (!options.isAlive || checkingNeighbor.isAlive) && + (!options.side || checkingNeighbor.side.current === options.side)) { + return checkingNeighbor; + } + if (direction === "left") { + checkingNeighborPosition = checkingNeighborPosition + 1 === players.length ? 0 : checkingNeighborPosition + 1; + } else if (direction === "right") { + checkingNeighborPosition = checkingNeighborPosition - 1 === -1 ? players.length - 1 : checkingNeighborPosition - 1; + } + checkedNeighborsCount++; + } + return null; +}; \ No newline at end of file diff --git a/src/helpers/functions/User.js b/src/helpers/functions/User.js index 4df0476..e903b29 100644 --- a/src/helpers/functions/User.js +++ b/src/helpers/functions/User.js @@ -1,7 +1,10 @@ +const { registrationMethods } = require("../constants/User"); const { generateError } = require("./Error"); exports.checkJWTUserRights = (req, userId) => { if (req.user.strategy === "JWT" && userId.toString() !== req.user._id.toString()) { throw generateError("UNAUTHORIZED", "You can't access other's data."); } -}; \ No newline at end of file +}; + +exports.getRegistrationMethods = () => JSON.parse(JSON.stringify(registrationMethods)); \ No newline at end of file diff --git a/src/routes/Game.js b/src/routes/Game.js index cdbf237..b591457 100755 --- a/src/routes/Game.js +++ b/src/routes/Game.js @@ -4,7 +4,7 @@ const Game = require("../controllers/Game"); const GameHistory = require("../controllers/GameHistory"); const { getRoleNames, getSideNames } = require("../helpers/functions/Role"); const { - getPatchableGameStatuses, getWaitingForPossibilities, getGameStatuses, + getPatchableGameStatuses, getWaitingForPossibilities, getGameStatuses, getGamePhases, getGameRepartitionForbiddenRoleNames, getAdditionalCardsForRoleNames, } = require("../helpers/functions/Game"); const { getPlayerActions } = require("../helpers/functions/Player"); @@ -26,16 +26,52 @@ module.exports = app => { * @apiSuccess {String} status Game's current status. (_Possibilities: [Codes - Game Statuses](#game-statuses)_) * @apiSuccess {AdditionalCard[]} [additionalCards] Game's additional cards. Set if `thief` is in the game. (_See: [Classes - Additional Card](#game-additional-card-class)_) * @apiSuccess {Object} options Game's options. + * @apiSuccess {Object} options.repartition Game role's repartition. + * @apiSuccess {Boolean} options.repartition.isHidden=false If set to `true`, game's repartition will be hidden to all players. Default is false. * @apiSuccess {Object} options.roles Game roles options. + * @apiSuccess {Boolean} options.roles.areRevealedOnDeath=true If set to `true`, player's role is revealed when he's dead. Default is `true`. * @apiSuccess {Object} options.roles.sheriff Game sheriff role's options. * @apiSuccess {Boolean} options.roles.sheriff.isEnabled=true If set to `true`, `sheriff` will be elected the first tick and the responsibility will be delegated when he dies. Otherwise, there will be no sheriff in the game and tie in votes will result in another vote between the tied players. In case of another equality, there will be no vote. + * @apiSuccess {Object} options.roles.sheriff.electedAt When the sheriff is elected during the game. + * @apiSuccess {Number} options.roles.sheriff.electedAt.turn=1 Game's turn when the sheriff is elected. Default is `1`. + * @apiSuccess {String} options.roles.sheriff.electedAt.phase="night" Game's phase when the sheriff is elected. Default is `night`. * @apiSuccess {Boolean} options.roles.sheriff.hasDoubledVote=true If set to `true`, `sheriff` vote during the village's vote is doubled, otherwise, it's a regular vote. + * @apiSuccess {Object} options.roles.bigBadWolf Game big bad wolf role's options. + * @apiSuccess {Boolean} options.roles.bigBadWolf.isPowerlessIfWerewolfDies=true If set to `true`, `big-bad-wolf` won't wake up anymore during the night if at least one player from the `werewolves` side died. Default is `true`. + * @apiSuccess {Object} options.roles.whiteWerewolf Game white werewolf role's options. + * @apiSuccess {Number{>= 1 && <= 5}} options.roles.whiteWerewolf.wakingUpInterval=2 Since first `night`, interval of nights when the `white-werewolf` is waking up. Default is `2`, meaning he wakes up every other night. * @apiSuccess {Object} options.roles.seer Game seer role's options. * @apiSuccess {Boolean} options.roles.seer.isTalkative=true If set to `true`, the game master must say out loud what the seer saw during her night, otherwise, he must mime the seen role to the seer. Default is `true`. + * @apiSuccess {Boolean} options.roles.seer.canSeeRoles=true If set to `true`, the seer can the exact `role` of the target, otherwise, she only sees the `side`. Default is `true`. + * @apiSuccess {Object} options.roles.littleGirl Game little girl role's options. + * @apiSuccess {Boolean} options.roles.littleGirl.isProtectedByGuard=false If set to `false`, the little girl won't be protected by the guard from the werewolves attacks. Default is `false`. + * @apiSuccess {Object} options.roles.guard Game guard role's options. + * @apiSuccess {Boolean} options.roles.guard.canProtectTwice=false If set to `true`, the guard can protect twice in a row the same target. Default is `false`. + * @apiSuccess {Object} options.roles.ancient Game ancient role's options. + * @apiSuccess {Number{>= 1 && <= 5}} options.roles.ancient.livesCountAgainstWerewolves=2 Number of lives ancient has against `werewolves`. Default is `2`. + * @apiSuccess {Boolean} options.roles.ancient.doesTakeHisRevenge=true If set to `true`, the `ancient` will make all players from the `villagers` side `powerless` if he dies from them. Default is `true`. + * @apiSuccess {Object} options.roles.idiot Game idiot role's options. + * @apiSuccess {Boolean} options.roles.idiot.doesDieOnAncientDeath=true If set to `true`, the idiot will die if he is revealed and the ancient is dead. Default is `true`. * @apiSuccess {Object} options.roles.twoSisters Game two sisters role's options. * @apiSuccess {Number{>= 0 && <= 5}} options.roles.twoSisters.wakingUpInterval=2 Since first `night`, interval of nights when the Two Sisters are waking up. Default is `2`, meaning they wake up every other night. If set to `0`, they are waking up the first night only. * @apiSuccess {Object} options.roles.threeBrothers Game three brothers role's options. * @apiSuccess {Number{>= 0 && <= 5}} options.roles.threeBrothers.wakingUpInterval=2 Since first `night`, interval of nights when the Three Brothers are waking up. Default is `2`, meaning they wake up every other night. If set to `0`, they are waking up the first night only. + * @apiSuccess {Object} options.roles.fox Game fox role's options. + * @apiSuccess {Boolean} options.roles.fox.isPowerlessIfMissesWerewolf=true If set to `true`, the fox will loose his power if he doesn't find a player from the `werewolves` side during his turn if he doesn't skip. Default is `true`. + * @apiSuccess {Object} options.roles.bearTamer Game bear tamer role's options. + * @apiSuccess {Boolean} options.roles.bearTamer.doesGrowlIfInfected=true If set to `true`, the bear tamer will have the `growls` attribute until he dies if he is `infected`. Default is `true`. + * @apiSuccess {Object} options.roles.stutteringJudge Game stuttering judge role's options. + * @apiSuccess {Number{>= 1 && <= 5}} options.roles.stutteringJudge.voteRequestsCount=1 Number of vote requests that the `stuttering-judge` can make during the game. Default is `1`. + * @apiSuccess {Object} options.roles.wildChild Game wild child role's options. + * @apiSuccess {Boolean} options.roles.wildChild.isTransformationRevealed=false If set to `true`, when `wild-child` joins the `werewolves` side because his model died, the game master will announce his transformation to other players. Default is `false`. + * @apiSuccess {Object} options.roles.dogWolf Game dog wolf role's options. + * @apiSuccess {Boolean} options.roles.dogWolf.isChosenSideRevealed=false If set to `true`, when `dog-wolf` chooses his side at the beginning of the game, the game master will announce the chosen side to other players. Default is `false`. + * @apiSuccess {Object} options.roles.thief Game thief role's options. + * @apiSuccess {Boolean} options.roles.thief.mustChooseBetweenWerewolves=true If set to `true`, if all `thief` additional cards are from the `werewolves` side, he can't skip and must choose one. Default is `true`. + * @apiSuccess {Number{>= 1 && <= 5}} options.roles.thief.additionalCardsCount=2 Number of additional cards for the `thief` at the beginning of the game. Default is `2`. + * @apiSuccess {Object} options.roles.piedPiper Game pied piper role's options. + * @apiSuccess {Number{>= 1 && <= 5}} options.roles.piedPiper.charmedPeopleCountPerNight=2 Number of charmed people by the `pied-piper` per night if there are enough targets (or number of not charmed players otherwise). Default is `2`. + * @apiSuccess {Boolean} options.roles.piedPiper.isPowerlessIfInfected=true If set to `true`, `pied-piper` will be powerless if he is infected by the `vile-father-of-wolves`. Default is `true`. * @apiSuccess {Object} options.roles.raven Game raven role's options. * @apiSuccess {Number{>= 1 && <= 5}} options.roles.raven.markPenalty=2 Penalty of votes against the player targeted by the raven mark for the next village's vote. Default is `2`, meaning that the raven marked player will have two votes against himself. * @apiSuccess {GameHistory[]} history Game's history. (_See: [Classes - Game History](#game-history-class)_) @@ -165,24 +201,57 @@ module.exports = app => { * @apiParam (Request Body Parameters) {Object[]} players Must contain between 4 and 40 players. * @apiParam (Request Body Parameters) {String{>= 1 && <= 30}} players.name Player's name. Must be unique in the array and between 1 and 30 characters long. * @apiParam (Request Body Parameters) {String} players.role Player's role. (_See [Codes - Player Roles](#player-roles)_) + * @apiParam (Request Body Parameters) {Number{>= 0}} [players.position] Player's unique position among all players. Maximum is `players.length - 1`. Either all players position must be set or none of them. In that last case, it will be generated automatically. * @apiParam (Request Body Parameters) {Object[]} [additionalCards] Game's additional cards. Must be set if role `thief` is in the game and contain 2 cards. * @apiParam (Request Body Parameters) {String} additionalCards.role Additional card's role. The role must be still available compared to the game's composition. (_See [Codes - Player Roles](#player-roles)_) * @apiParam (Request Body Parameters) {String} additionalCards.for Additional card's recipient. Must be equal to `thief`. * @apiParam (Request Body Parameters) {Object} [options] Game's options. + * @apiParam (Request Body Parameters) {Object} [options.repartition] Game repartition's options. + * @apiParam (Request Body Parameters) {Boolean} [options.repartition.isHidden=false] If set to `true`, game's repartition will be hidden to all players. * @apiParam (Request Body Parameters) {Object} [options.roles] Game roles options. + * @apiParam (Request Body Parameters) {Boolean} [options.roles.areRevealedOnDeath=true] If set to `true`, player's role is revealed when he's dead. * @apiParam (Request Body Parameters) {Object} [options.roles.sheriff] Game sheriff role's options. * @apiParam (Request Body Parameters) {Boolean} [options.roles.sheriff.isEnabled=true] If set to `true`, `sheriff` will be elected the first tick and the responsibility will be delegated when he dies. Otherwise, there will be no sheriff in the game and tie in votes will result in another vote between the tied players. In case of another equality, there will be no vote. + * @apiParam (Request Body Parameters) {Object} [options.roles.sheriff.electedAt] When the sheriff is elected during the game. + * @apiParam (Request Body Parameters) {Number{>= 1 && <= 5}} [options.roles.sheriff.electedAt.turn=1] When the sheriff is elected during the game. + * @apiParam (Request Body Parameters) {String{"night", "day"}} [options.roles.sheriff.electedAt.phase="night"] When the sheriff is elected during the game. * @apiParam (Request Body Parameters) {Boolean} [options.roles.sheriff.hasDoubledVote=true] If set to `true`, `sheriff` vote during the village's vote is doubled, otherwise, it's a regular vote. + * @apiParam (Request Body Parameters) {Object} [options.roles.bigBadWolf] Game big bad wolf role's options. + * @apiParam (Request Body Parameters) {Boolean} [options.roles.bigBadWolf.isPowerlessIfWerewolfDies=true] If set to `true`, `big-bad-wolf` won't wake up anymore during the night if at least one player from the `werewolves` side died. Default is `true`. + * @apiParam (Request Body Parameters) {Object} [options.roles.whiteWerewolf] Game white werewolf role's options. + * @apiParam (Request Body Parameters) {Number{>= 1 && <= 5}} [options.roles.whiteWerewolf.wakingUpInterval=2] Since first `night`, interval of nights when the `white-werewolf` is waking up. Default is `2`, meaning he wakes up every other night. * @apiParam (Request Body Parameters) {Object} [options.roles.seer] Game seer role's options. * @apiParam (Request Body Parameters) {Boolean} [options.roles.seer.isTalkative=true] If set to `true`, the game master must say out loud what the seer saw during her night, otherwise, he must mime the seen role to the seer. Default is `true`. + * @apiParam (Request Body Parameters) {Boolean} [options.roles.seer.canSeeRoles=true] If set to `true`, the seer can the exact `role` of the target, otherwise, she only sees the `side`. Default is `true`. * @apiParam (Request Body Parameters) {Object} [options.roles.littleGirl] Game little girl role's options. * @apiParam (Request Body Parameters) {Boolean} [options.roles.littleGirl.isProtectedByGuard=false] If set to `false`, the little girl won't be protected by the guard from the werewolves attacks. Default is `false`. + * @apiParam (Request Body Parameters) {Object} [options.roles.guard] Game guard role's options. + * @apiParam (Request Body Parameters) {Boolean} [options.roles.guard.canProtectTwice=false] If set to `true`, the guard can protect twice in a row the same target. Default is `false`. + * @apiParam (Request Body Parameters) {Object} [options.roles.ancient] Game ancient role's options. + * @apiParam (Request Body Parameters) {Number{>= 1 && <= 5}} [options.roles.ancient.livesCountAgainstWerewolves=2] Number of lives ancient has against `werewolves`. Default is `2`. + * @apiParam (Request Body Parameters) {Boolean} [options.roles.ancient.doesTakeHisRevenge=true] If set to `true`, the `ancient` will make all players from the `villagers` side `powerless` if he dies from them. Default is `true`. * @apiParam (Request Body Parameters) {Object} [options.roles.idiot] Game idiot role's options. * @apiParam (Request Body Parameters) {Boolean} [options.roles.idiot.doesDieOnAncientDeath=true] If set to `true`, the idiot will die if he is revealed and the ancient is dead. Default is `true`. * @apiParam (Request Body Parameters) {Object} [options.roles.twoSisters] Game two sisters role's options. * @apiParam (Request Body Parameters) {Number{>= 0 && <= 5}} [options.roles.twoSisters.wakingUpInterval=2] Since first `night`, interval of nights when the Two Sisters are waking up. Default is `2`, meaning they wake up every other night. If set to `0`, they are waking up the first night only. * @apiParam (Request Body Parameters) {Object} [options.roles.threeBrothers] Game three brothers role's options. * @apiParam (Request Body Parameters) {Number{>= 0 && <= 5}} [options.roles.threeBrothers.wakingUpInterval=2] Since first `night`, interval of nights when the Three Brothers are waking up. Default is `2`, meaning they wake up every other night. If set to `0`, they are waking up the first night only. + * @apiParam (Request Body Parameters) {Object} [options.roles.fox] Game fox's role options. + * @apiParam (Request Body Parameters) {Boolean} [options.roles.fox.isPowerlessIfMissesWerewolf=true] If set to `true`, the fox will loose his power if he doesn't find a player from the `werewolves` side during his turn if he doesn't skip. Default is `true`. + * @apiParam (Request Body Parameters) {Object} [options.roles.bearTamer] Game bear tamer's role options. + * @apiParam (Request Body Parameters) {Boolean} [options.roles.bearTamer.doesGrowlIfInfected=true] If set to `true`, the bear tamer will have the `growls` attribute until he dies if he is `infected`. Default is `true`. + * @apiParam (Request Body Parameters) {Object} [options.roles.stutteringJudge] Game stuttering judge's role options. + * @apiParam (Request Body Parameters) {Number{>= 1 && <= 5}} [options.roles.stutteringJudge.voteRequestsCount=1] Number of vote requests that the `stuttering-judge` can make during the game. Default is `1`. + * @apiParam (Request Body Parameters) {Object} [options.roles.wildChild] Game wild child's role options. + * @apiParam (Request Body Parameters) {Boolean} [options.roles.wildChild.isTransformationRevealed=false] If set to `true`, when `wild-child` joins the `werewolves` side because his model died, the game master will announce his transformation to other players. Default is `false`. + * @apiParam (Request Body Parameters) {Object} [options.roles.dogWolf] Game dog wolf's role options. + * @apiParam (Request Body Parameters) {Boolean} [options.roles.dogWolf.isChosenSideRevealed=false] If set to `true`, when `dog-wolf` chooses his side at the beginning of the game, the game master will announce the chosen side to other players. Default is `false`. + * @apiParam (Request Body Parameters) {Object} [options.roles.thief] Game thief's role options. + * @apiParam (Request Body Parameters) {Boolean} [options.roles.thief.mustChooseBetweenWerewolves=true] If set to `true`, if all `thief` additional cards are from the `werewolves` side, he can't skip and must choose one. Default is `true`. + * @apiParam (Request Body Parameters) {Number{>= 1 && <= 5}} [options.roles.thief.additionalCardsCount =2] Number of additional cards for the `thief` at the beginning of the game. Default is `2`. + * @apiParam (Request Body Parameters) {Object} [options.roles.piedPiper] Game pied piper's role options. + * @apiParam (Request Body Parameters) {Number{>= 1 && <= 5}} [options.roles.piedPiper.charmedPeopleCountPerNight=2] Number of charmed people by the `pied-piper` per night if there are enough targets (or number of not charmed players otherwise). Default is `2`. + * @apiParam (Request Body Parameters) {Boolean} [options.roles.piedPiper.isPowerlessIfInfected=true] If set to `true`, `pied-piper` will be powerless if he is infected by the `vile-father-of-wolves`. Default is `true`. * @apiParam (Request Body Parameters) {Object} [options.roles.raven] Game raven's role options. * @apiParam (Request Body Parameters) {Number{>= 1 && <= 5}} [options.roles.raven.markPenalty=2] Penalty of votes against the player targeted by the raven mark for the next village's vote. Default is `2`, meaning that the raven marked player will have two votes against himself. * @apiUse GameResponse @@ -199,33 +268,75 @@ module.exports = app => { body("players.*.role") .isString().withMessage("Must be a valid string") .isIn(getRoleNames()).withMessage(`Must be equal to one of the following values: ${getRoleNames()}`), + body("players.*.position") + .optional() + .isInt({ min: 0 }).withMessage("Must be a valid integer greater or equal than 0") + .toInt(), body("additionalCards") .optional() - .isArray().withMessage("Must be a valid array") - .custom(value => value.length === 2 ? Promise.resolve() : Promise.reject(new Error())) - .withMessage("Must contain 2 cards"), + .isArray().withMessage("Must be a valid array"), body("additionalCards.*.role") .isString().withMessage("Must be a valid string") .isIn(getRoleNames()).withMessage(`Must be equal to one of the following values: ${getRoleNames()}`), body("additionalCards.*.for") .isString().withMessage("Must be a valid string") .isIn(getAdditionalCardsForRoleNames()).withMessage(`Must be equal to one of the following values: ${getAdditionalCardsForRoleNames()}`), + body("options.repartition.isHidden") + .optional() + .isBoolean().withMessage("Must be a valid boolean") + .toBoolean(), + body("options.roles.areRevealedOnDeath") + .optional() + .isBoolean().withMessage("Must be a valid boolean") + .toBoolean(), body("options.roles.sheriff.isEnabled") .optional() .isBoolean().withMessage("Must be a valid boolean") .toBoolean(), + body("options.roles.sheriff.electedAt.turn") + .optional() + .isInt({ min: 1, max: 5 }).withMessage("Must be a valid integer between 1 and 5") + .toInt(), + body("options.roles.sheriff.electedAt.phase") + .optional() + .isString().withMessage("Must be a valid string") + .isIn(getGamePhases()).withMessage(`Must be equal to one of the following values: ${getGamePhases()}`), body("options.roles.sheriff.hasDoubledVote") .optional() .isBoolean().withMessage("Must be a valid boolean") .toBoolean(), + body("options.roles.bigBadWolf.isPowerlessIfWerewolfDies") + .optional() + .isBoolean().withMessage("Must be a valid boolean") + .toBoolean(), + body("options.roles.whiteWerewolf.wakingUpInterval") + .optional() + .isInt({ min: 1, max: 5 }).withMessage("Must be a valid integer between 1 and 5") + .toInt(), body("options.roles.seer.isTalkative") .optional() .isBoolean().withMessage("Must be a valid boolean") .toBoolean(), + body("options.roles.seer.canSeeRoles") + .optional() + .isBoolean().withMessage("Must be a valid boolean") + .toBoolean(), body("options.roles.littleGirl.isProtectedByGuard") .optional() .isBoolean().withMessage("Must be a valid boolean") .toBoolean(), + body("options.roles.guard.canProtectTwice") + .optional() + .isBoolean().withMessage("Must be a valid boolean") + .toBoolean(), + body("options.roles.ancient.livesCountAgainstWerewolves") + .optional() + .isInt({ min: 1, max: 5 }).withMessage("Must be a valid integer between 0 and 5") + .toInt(), + body("options.roles.ancient.doesTakeHisRevenge") + .optional() + .isBoolean().withMessage("Must be a valid boolean") + .toBoolean(), body("options.roles.idiot.doesDieOnAncientDeath") .optional() .isBoolean().withMessage("Must be a valid boolean") @@ -238,6 +349,42 @@ module.exports = app => { .optional() .isInt({ min: 0, max: 5 }).withMessage("Must be a valid integer between 0 and 5") .toInt(), + body("options.roles.fox.isPowerlessIfMissesWerewolf") + .optional() + .isBoolean().withMessage("Must be a valid boolean") + .toBoolean(), + body("options.roles.bearTamer.doesGrowlIfInfected") + .optional() + .isBoolean().withMessage("Must be a valid boolean") + .toBoolean(), + body("options.roles.stutteringJudge.voteRequestsCount") + .optional() + .isInt({ min: 1, max: 5 }).withMessage("Must be a valid integer between 1 and 5") + .toInt(), + body("options.roles.wildChild.isTransformationRevealed") + .optional() + .isBoolean().withMessage("Must be a valid boolean") + .toBoolean(), + body("options.roles.dogWolf.isChosenSideRevealed") + .optional() + .isBoolean().withMessage("Must be a valid boolean") + .toBoolean(), + body("options.roles.thief.mustChooseBetweenWerewolves") + .optional() + .isBoolean().withMessage("Must be a valid boolean") + .toBoolean(), + body("options.roles.thief.additionalCardsCount") + .optional() + .isInt({ min: 1, max: 5 }).withMessage("Must be a valid integer between 1 and 5") + .toInt(), + body("options.roles.piedPiper.charmedPeopleCountPerNight") + .optional() + .isInt({ min: 1, max: 5 }).withMessage("Must be a valid integer between 1 and 5") + .toInt(), + body("options.roles.piedPiper.isPowerlessIfInfected") + .optional() + .isBoolean().withMessage("Must be a valid boolean") + .toBoolean(), body("options.roles.raven.markPenalty") .optional() .isInt({ min: 1, max: 5 }).withMessage("Must be a valid integer between 1 and 5") diff --git a/src/routes/Role.js b/src/routes/Role.js index 9ff0d5e..39bc0fb 100644 --- a/src/routes/Role.js +++ b/src/routes/Role.js @@ -3,8 +3,11 @@ const { getRoles } = require("../helpers/functions/Role"); /** * @apiDefine RoleResponse * @apiSuccess {String} name Role's name. (_Possibilities: [Codes - Player Roles](#player-roles)_) - * @apiSuccess {String} group Role's group. (_Possibilities: [Codes - Player Groups](#player-groups)_) + * @apiSuccess {String} side Role's original side. (_Possibilities: [Codes - Player Sides](#player-sides)_) + * @apiSuccess {String} type Role's type. (_Possibilities: `villager`, `werewolf`, `ambiguous`, `lonely`_) + * @apiSuccess {Number} [minInGame] If the role is chosen by at least one player, then minimum X players must choose it to start the game. * @apiSuccess {Number} maxInGame Maximum of this role in a game. + * @apiSuccess {Number} [recommendedMinPlayers] It is recommended to have at least X players in game for choosing this role. */ module.exports = app => { diff --git a/src/routes/User.js b/src/routes/User.js index 0512291..b2429cf 100755 --- a/src/routes/User.js +++ b/src/routes/User.js @@ -22,6 +22,8 @@ module.exports = app => { * @apiDefine UserResponse * @apiSuccess {ObjectID} _id User's ID. * @apiSuccess {String} email User's email. + * @apiSuccess {Object} registration User's registration data. + * @apiSuccess {String} registration.method How the user registered himself. (_Possibilities: `local`, `facebook` or `google`_) * @apiSuccess {Date} createdAt When the user is created. * @apiSuccess {Date} updatedAt When the user is updated. */ @@ -95,4 +97,30 @@ module.exports = app => { .isString().withMessage("Must be a string") .isLength({ min: 5, max: 50 }).withMessage("Must be at least 5 characters long"), ], User.login); + + /** + * @api {POST} /users/login/facebook E] Login with Facebook + * @apiName LoginFacebookUser + * @apiGroup Users ๐Ÿ‘ค + * + * @apiParam (Request Body Parameters) {String} accessToken Facebook user's access token for the Werewolves Assistant app. + * @apiSuccess {String} token JSON Web Token to keep for further route authentication. + */ + app.post("/users/login/facebook", [ + body("accessToken") + .isString().withMessage("Must be a valid string"), + ], User.loginWithFacebook); + + /** + * @api {POST} /users/login/facebook F] Login with Google + * @apiName LoginGoogleUser + * @apiGroup Users ๐Ÿ‘ค + * + * @apiParam (Request Body Parameters) {String} idToken Google user's id token for the Werewolves Assistant app. + * @apiSuccess {String} token JSON Web Token to keep for further route authentication. + */ + app.post("/users/login/google", [ + body("idToken") + .isString().withMessage("Must be a valid string"), + ], User.loginWithGoogle); }; \ No newline at end of file diff --git a/src/routes/index.js b/src/routes/index.js index 90cde5f..a69ad4e 100755 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -12,7 +12,7 @@ module.exports = app => { * @apiSuccess {String} version API's version */ app.route("/").get((req, res) => { - res.status(200).json({ name: "๐Ÿบ Werewolves Assistant API", version: "0.10.0" }); + res.status(200).json({ name: "๐Ÿบ Werewolves Assistant API", version: "0.11.0" }); }); return; } diff --git a/tests/e2e/app.test.js b/tests/e2e/app.test.js index d4f2ae3..944fa38 100644 --- a/tests/e2e/app.test.js +++ b/tests/e2e/app.test.js @@ -5,15 +5,18 @@ const chaiHttp = require("chai-http"); chai.use(chaiHttp); const { expect } = chai; +let server; -describe("Testing main route", () => { - before(done => !app.isReady ? app.on("ready", done) : done); +describe("E2E - Testing main route", () => { + before(done => { + server = app.listen(3000, done); + }); it("๐Ÿ‘‹ Welcomes user with API name", done => { - chai.request(app) + chai.request(server) .get("/") .end((err, res) => { expect(res).to.have.status(200); - expect(res.body.name).to.equals("๐Ÿบ Werewolves Assistant API"); + expect(res.body.name).to.equal("๐Ÿบ Werewolves Assistant API"); done(); }); }); diff --git a/tests/e2e/game/ancient-infected.test.js b/tests/e2e/game/ancient-infected.test.js index 8ba7ba4..18fef17 100644 --- a/tests/e2e/game/ancient-infected.test.js +++ b/tests/e2e/game/ancient-infected.test.js @@ -16,13 +16,16 @@ let players = [ { name: "Dโ‚ฌg", role: "ancient" }, { name: "Dร‡g", role: "villager" }, ]; -let token, game; +let server, token, game; describe("Q - Game with an ancient who is infected after his first life and 2 protections", () => { before(done => resetDatabase(done)); + before(done => { + server = app.listen(3000, done); + }); after(done => resetDatabase(done)); it("๐Ÿ‘ค Creates new user (POST /users)", done => { - chai.request(app) + chai.request(server) .post("/users") .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -32,7 +35,7 @@ describe("Q - Game with an ancient who is infected after his first life and 2 pr }); }); it("๐Ÿ”‘ Logs in successfully (POST /users/login)", done => { - chai.request(app) + chai.request(server) .post(`/users/login`) .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -43,7 +46,7 @@ describe("Q - Game with an ancient who is infected after his first life and 2 pr }); }); it("๐ŸŽฒ Creates game with JWT auth (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players, options: { roles: { sheriff: { isEnabled: false } } } }) @@ -55,12 +58,12 @@ describe("Q - Game with an ancient who is infected after his first life and 2 pr }); it("๐ŸŒ™ Night falls", done => { players = game.players; - expect(game.phase).to.equals("night"); + expect(game.phase).to.equal("night"); done(); }); it("๐Ÿ›ก Guard protects himself (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "guard", action: "protect", targets: [{ player: players[1]._id }] }) @@ -69,13 +72,13 @@ describe("Q - Game with an ancient who is infected after his first life and 2 pr game = res.body; expect(game.players[1].attributes).to.deep.include({ name: "protected", source: "guard", remainingPhases: 1 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[1]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[1]._id); done(); }); }); it("๐Ÿบ Werewolf eats the ancient (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[3]._id }] }) @@ -83,14 +86,14 @@ describe("Q - Game with an ancient who is infected after his first life and 2 pr expect(res).to.have.status(200); game = res.body; expect(game.players[3].attributes).to.deep.include({ name: "eaten", source: "werewolves", remainingPhases: 1 }); - expect(game.players[3].side.current).to.equals("villagers"); + expect(game.players[3].side.current).to.equal("villagers"); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[3]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[3]._id); done(); }); }); it("๐Ÿช„ Witch skips (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "witch", action: "use-potion" }) @@ -101,14 +104,14 @@ describe("Q - Game with an ancient who is infected after his first life and 2 pr }); }); it("โ˜€๏ธ Sun is rising and ancient is alive because hhe has another life", done => { - expect(game.phase).to.equals("day"); + expect(game.phase).to.equal("day"); expect(game.players[3].isAlive).to.be.true; expect(game.players[3].role.isRevealed).to.be.false; done(); }); it("๐Ÿ‘ช All vote for the villager (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[1]._id, for: players[4]._id }] }) @@ -122,7 +125,7 @@ describe("Q - Game with an ancient who is infected after his first life and 2 pr }); it("๐Ÿ›ก Guard protects the ancient (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "guard", action: "protect", targets: [{ player: players[3]._id }] }) @@ -131,13 +134,13 @@ describe("Q - Game with an ancient who is infected after his first life and 2 pr game = res.body; expect(game.players[3].attributes).to.deep.include({ name: "protected", source: "guard", remainingPhases: 1 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[3]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[3]._id); done(); }); }); it("๐Ÿบ Werewolf eats the ancient again (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[3]._id }] }) @@ -145,12 +148,12 @@ describe("Q - Game with an ancient who is infected after his first life and 2 pr expect(res).to.have.status(200); game = res.body; expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[3]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[3]._id); done(); }); }); it("๐Ÿช„ Witch skips (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "witch", action: "use-potion" }) @@ -161,14 +164,14 @@ describe("Q - Game with an ancient who is infected after his first life and 2 pr }); }); it("โ˜€๏ธ Sun is rising and ancient is alive because guard saved him", done => { - expect(game.phase).to.equals("day"); + expect(game.phase).to.equal("day"); expect(game.players[3].isAlive).to.be.true; expect(game.players[3].role.isRevealed).to.be.false; done(); }); it("๐Ÿ‘ช All vote for the guard (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[2]._id, for: players[1]._id }] }) @@ -182,7 +185,7 @@ describe("Q - Game with an ancient who is infected after his first life and 2 pr }); it("๐Ÿบ Werewolf eats the ancient again, again (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[3]._id }] }) @@ -190,12 +193,12 @@ describe("Q - Game with an ancient who is infected after his first life and 2 pr expect(res).to.have.status(200); game = res.body; expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[3]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[3]._id); done(); }); }); it("๐Ÿช„ Witch uses her life potion on ancient (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "witch", action: "use-potion", targets: [{ player: players[3]._id, hasDrankLifePotion: true }] }) @@ -206,14 +209,14 @@ describe("Q - Game with an ancient who is infected after his first life and 2 pr }); }); it("โ˜€๏ธ Sun is rising and ancient is alive because witch saved him", done => { - expect(game.phase).to.equals("day"); + expect(game.phase).to.equal("day"); expect(game.players[3].isAlive).to.be.true; expect(game.players[3].role.isRevealed).to.be.false; done(); }); it("๐Ÿ‘ช All vote for the witch (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[2]._id, for: players[0]._id }] }) @@ -227,7 +230,7 @@ describe("Q - Game with an ancient who is infected after his first life and 2 pr }); it("๐Ÿบ Vile father of wolves infects the ancient because he knows he has no life ! (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[3]._id, isInfected: true }] }) @@ -235,12 +238,12 @@ describe("Q - Game with an ancient who is infected after his first life and 2 pr expect(res).to.have.status(200); game = res.body; expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[3]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[3]._id); done(); }); }); it("โ˜€๏ธ Sun is rising and ancient has become a werewolf", done => { - expect(game.phase).to.equals("day"); + expect(game.phase).to.equal("day"); expect(game.players[3].isAlive).to.be.true; expect(game.players[3].role.isRevealed).to.be.false; expect(game.players[3].side.current).to.equal("werewolves"); diff --git a/tests/e2e/game/ancient-revenge-game.test.js b/tests/e2e/game/ancient-revenge-game.test.js index c073143..3247cb2 100644 --- a/tests/e2e/game/ancient-revenge-game.test.js +++ b/tests/e2e/game/ancient-revenge-game.test.js @@ -31,18 +31,25 @@ let players = [ { name: "Dฮฉg", role: "dog-wolf" }, { name: "D#g", role: "cupid" }, { name: "Dยฑg", role: "thief" }, + { name: "Dยปg", role: "fox" }, + { name: "Dโ€“g", role: "bear-tamer" }, + { name: "Dโˆ‘g", role: "werewolf" }, + { name: "Dล’g", role: "rusty-sword-knight" }, ]; const additionalCards = [ { role: "werewolf", for: "thief" }, { role: "werewolf", for: "thief" }, ]; -let token, game; +let server, token, game; describe("L - Game with various villagers who loose their power because they kill ancient", () => { before(done => resetDatabase(done)); + before(done => { + server = app.listen(3000, done); + }); after(done => resetDatabase(done)); it("๐Ÿ‘ค Creates new user (POST /users)", done => { - chai.request(app) + chai.request(server) .post("/users") .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -52,7 +59,7 @@ describe("L - Game with various villagers who loose their power because they kil }); }); it("๐Ÿ”‘ Logs in successfully (POST /users/login)", done => { - chai.request(app) + chai.request(server) .post(`/users/login`) .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -63,7 +70,7 @@ describe("L - Game with various villagers who loose their power because they kil }); }); it("๐ŸŽฒ Creates game with JWT auth (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players, additionalCards }) @@ -75,12 +82,12 @@ describe("L - Game with various villagers who loose their power because they kil }); it("๐ŸŒ™ Night falls", done => { players = game.players; - expect(game.phase).to.equals("night"); + expect(game.phase).to.equal("night"); done(); }); it("๐Ÿ‘ช All elect the idiot as the sheriff (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "elect-sheriff", votes: [{ from: players[0]._id, for: players[15]._id }] }) @@ -90,10 +97,10 @@ describe("L - Game with various villagers who loose their power because they kil expect(game.players[15].attributes).to.deep.include({ name: "sheriff", source: "all" }); expect(game.history).to.be.an("array").to.have.lengthOf(1); expect(game.history[0].play.votes).to.exist; - expect(game.history[0].play.votes[0].from._id).to.equals(game.players[0]._id); - expect(game.history[0].play.votes[0].for._id).to.equals(game.players[15]._id); + expect(game.history[0].play.votes[0].from._id).to.equal(game.players[0]._id); + expect(game.history[0].play.votes[0].for._id).to.equal(game.players[15]._id); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(game.players[15]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(game.players[15]._id); expect(game.history[0].play.source.name).to.equal("all"); expect(game.history[0].play.source.players).to.be.an("array").to.have.lengthOf(players.length); expect(game.history[0].deadPlayers).to.not.exist; @@ -102,7 +109,7 @@ describe("L - Game with various villagers who loose their power because they kil }); it("๐Ÿ‘ช All vote for the ancient, revenge is on: all villagers are powerless (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[0]._id, for: players[12]._id }] }) @@ -123,7 +130,7 @@ describe("L - Game with various villagers who loose their power because they kil expect(game.players[9].attributes).to.deep.include({ name: "powerless", source: "ancient" }); expect(game.players[10].attributes).to.deep.include({ name: "powerless", source: "ancient" }); expect(game.players[11].attributes).to.deep.include({ name: "powerless", source: "ancient" }); - expect(game.players[11].side.current).to.equals("villagers"); + expect(game.players[11].side.current).to.equal("villagers"); expect(game.players[13].attributes).to.deep.include({ name: "powerless", source: "ancient" }); expect(game.players[14].attributes).to.deep.include({ name: "powerless", source: "ancient" }); expect(game.players[15].attributes).to.deep.include({ name: "powerless", source: "ancient" }); @@ -132,12 +139,15 @@ describe("L - Game with various villagers who loose their power because they kil expect(game.players[18].attributes).to.deep.include({ name: "powerless", source: "ancient" }); expect(game.players[19].attributes).to.deep.include({ name: "powerless", source: "ancient" }); expect(game.players[20].attributes).to.deep.include({ name: "powerless", source: "ancient" }); + expect(game.players[21].attributes).to.deep.include({ name: "powerless", source: "ancient" }); + expect(game.players[22].attributes).to.deep.include({ name: "powerless", source: "ancient" }); + expect(game.players[24].attributes).to.deep.include({ name: "powerless", source: "ancient" }); done(); }); }); it("๐Ÿบ Werewolf eats the angel (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[17]._id }] }) @@ -145,18 +155,19 @@ describe("L - Game with various villagers who loose their power because they kil expect(res).to.have.status(200); game = res.body; expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[17]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[17]._id); done(); }); }); it("โ˜€๏ธ Sun is rising", done => { - expect(game.phase).to.equals("day"); + expect(game.phase).to.equal("day"); expect(game.players[17].isAlive).to.be.false; + expect(game.players[22].attributes).to.not.deep.include({ name: "growls", source: "bear-tamer", remainingPhases: 1 }); done(); }); it("๐Ÿ‘ช All vote for one brother (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[1]._id, for: players[9]._id }] }) @@ -169,16 +180,16 @@ describe("L - Game with various villagers who loose their power because they kil }); it("๐Ÿบ Werewolf eats the guard (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[4]._id }] }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.players[4].side.current).to.equals("villagers"); + expect(game.players[4].side.current).to.equal("villagers"); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[4]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[4]._id); expect(game.players[4].isAlive).to.be.false; expect(game.players[4].attributes).to.deep.includes({ name: "powerless", source: "ancient" }); expect(game.players[4].attributes).to.not.deep.includes({ name: "eaten", source: "werewolves" }); @@ -187,7 +198,7 @@ describe("L - Game with various villagers who loose their power because they kil }); it("๐Ÿ‘ช All vote for the raven (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[0]._id, for: players[3]._id }] }) @@ -205,7 +216,7 @@ describe("L - Game with various villagers who loose their power because they kil }); it("๐Ÿบ Werewolf is the only one called during the night and eats the witch (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[2]._id }] }) @@ -213,13 +224,13 @@ describe("L - Game with various villagers who loose their power because they kil expect(res).to.have.status(200); game = res.body; expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[2]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[2]._id); done(); }); }); it("โš–๏ธ Stuttering judge can't request another vote if he is powerless (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -230,13 +241,13 @@ describe("L - Game with various villagers who loose their power because they kil }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("STUTTERING_JUDGE_POWERLESS"); + expect(res.body.type).to.equal("STUTTERING_JUDGE_POWERLESS"); done(); }); }); it("๐Ÿ‘ช Tie in vote between the idiot and one sister (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -259,7 +270,7 @@ describe("L - Game with various villagers who loose their power because they kil }); it("๐ŸŽ– Sheriff settles votes by choosing the sister (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "sheriff", action: "settle-votes", targets: [{ player: players[6]._id }] }) @@ -271,23 +282,24 @@ describe("L - Game with various villagers who loose their power because they kil done(); }); }); - it("๐Ÿบ Werewolf is the only one called during the night and eats the other sister (POST /games/:id/play)", done => { + it("๐Ÿบ Werewolf is the only one called during the night and eats the rusty sword knight (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) - .send({ source: "werewolves", action: "eat", targets: [{ player: players[7]._id }] }) + .send({ source: "werewolves", action: "eat", targets: [{ player: players[24]._id }] }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; + expect(game.players[5].attributes).to.not.exist; expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[7]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[24]._id); done(); }); }); it("๐Ÿ‘ช All vote for the idiot who is the sheriff, he dies from votes because he's powerless (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[5]._id, for: players[15]._id }] }) @@ -305,7 +317,7 @@ describe("L - Game with various villagers who loose their power because they kil }); it("๐ŸŽ– Sheriff delegates to the werewolf (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "sheriff", action: "delegate", targets: [{ player: players[5]._id }] }) @@ -314,7 +326,7 @@ describe("L - Game with various villagers who loose their power because they kil game = res.body; expect(game.players[5].attributes).to.deep.include({ name: "sheriff", source: "sheriff" }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(game.players[5]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(game.players[5]._id); done(); }); }); diff --git a/tests/e2e/game/bear-tamer-infected.test.js b/tests/e2e/game/bear-tamer-infected.test.js new file mode 100644 index 0000000..9ad0be8 --- /dev/null +++ b/tests/e2e/game/bear-tamer-infected.test.js @@ -0,0 +1,105 @@ +const { describe, it, before, after } = require("mocha"); +const chai = require("chai"); +const chaiHttp = require("chai-http"); +const app = require("../../../app"); +const Config = require("../../../config"); +const { resetDatabase } = require("../../../src/helpers/functions/Test"); + +chai.use(chaiHttp); +const { expect } = chai; + +const credentials = { email: "test@test.fr", password: "secret" }; +const originalPlayers = [ + { name: "Dag", role: "vile-father-of-wolves" }, + { name: "Dig", role: "villager" }, + { name: "Deg", role: "bear-tamer" }, + { name: "Dog", role: "villager" }, + { name: "Dรธg", role: "villager" }, +]; +let server, token, game, players; + +describe("X - Tiny game of 5 players in which the bear tamer is infected and so, growls every time", () => { + before(done => resetDatabase(done)); + before(done => { + server = app.listen(3000, done); + }); + after(done => resetDatabase(done)); + it("๐Ÿ‘ค Creates new user (POST /users)", done => { + chai.request(server) + .post("/users") + .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) + .send(credentials) + .end((err, res) => { + expect(res).to.have.status(200); + done(); + }); + }); + it("๐Ÿ”‘ Logs in successfully (POST /users/login)", done => { + chai.request(server) + .post(`/users/login`) + .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) + .send(credentials) + .end((err, res) => { + expect(res).to.have.status(200); + token = res.body.token; + done(); + }); + }); + it("๐ŸŽฒ Creates game with JWT auth (POST /games)", done => { + chai.request(server) + .post("/games") + .set({ Authorization: `Bearer ${token}` }) + .send({ players: originalPlayers, options: { roles: { sheriff: { isEnabled: false } } } }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + done(); + }); + }); + it("๐Ÿบ Vile father of wolf infected the bear tamer (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "werewolves", action: "eat", targets: [{ player: players[2]._id, isInfected: true }] }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + done(); + }); + }); + it("โ˜€๏ธ Sun is rising and bear tamer growls because he is infected, even if he doesn't have any werewolves neighbors", done => { + expect(game.players[2].attributes).to.deep.include({ name: "growls", source: "bear-tamer", remainingPhases: 1 }); + done(); + }); + it("๐Ÿ‘ช All vote for the vile father of wolves (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "all", action: "vote", votes: [{ from: players[1]._id, for: players[0]._id }] }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + expect(game.players[0].isAlive).to.be.false; + expect(game.players[0].murdered).to.deep.equals({ by: "all", of: "vote" }); + done(); + }); + }); + it("๐Ÿบ Infected bear tamer eats the first villager (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "werewolves", action: "eat", targets: [{ player: players[1]._id }] }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + done(); + }); + }); + it("โ˜€๏ธ Sun is rising and bear tamer growls because he is infected, even if he doesn't have any werewolves neighbors", done => { + expect(game.players[2].attributes).to.deep.include({ name: "growls", source: "bear-tamer", remainingPhases: 1 }); + done(); + }); +}); \ No newline at end of file diff --git a/tests/e2e/game/changes-during-night.test.js b/tests/e2e/game/changes-during-night.test.js index b1b9484..199327b 100644 --- a/tests/e2e/game/changes-during-night.test.js +++ b/tests/e2e/game/changes-during-night.test.js @@ -16,13 +16,16 @@ const originalPlayers = [ { name: "Dog", role: "seer" }, { name: "Dug", role: "witch" }, ]; -let token, game, players; +let server, token, game, players; describe("V - Tiny game of 5 players in which because of the early votes, actions during night change", () => { before(done => resetDatabase(done)); + before(done => { + server = app.listen(3000, done); + }); after(done => resetDatabase(done)); it("๐Ÿ‘ค Creates new user (POST /users)", done => { - chai.request(app) + chai.request(server) .post("/users") .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -32,7 +35,7 @@ describe("V - Tiny game of 5 players in which because of the early votes, action }); }); it("๐Ÿ”‘ Logs in successfully (POST /users/login)", done => { - chai.request(app) + chai.request(server) .post(`/users/login`) .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -43,7 +46,7 @@ describe("V - Tiny game of 5 players in which because of the early votes, action }); }); it("๐ŸŽฒ Creates game with JWT auth (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players: originalPlayers }) @@ -55,7 +58,7 @@ describe("V - Tiny game of 5 players in which because of the early votes, action }); it("๐Ÿ‘ช All elect the hunter as the sheriff (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "elect-sheriff", votes: [{ from: players[1]._id, for: players[2]._id }] }) @@ -67,7 +70,7 @@ describe("V - Tiny game of 5 players in which because of the early votes, action }); it("๐Ÿ‘ช All vote for the hunter (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[1]._id, for: players[2]._id }] }) @@ -90,7 +93,7 @@ describe("V - Tiny game of 5 players in which because of the early votes, action }); it("๐ŸŽ– Sheriff delegates to the witch (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "sheriff", action: "delegate", targets: [{ player: players[4]._id }] }) @@ -99,13 +102,13 @@ describe("V - Tiny game of 5 players in which because of the early votes, action game = res.body; expect(game.players[4].attributes).to.deep.include({ name: "sheriff", source: "sheriff" }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(game.players[4]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(game.players[4]._id); done(); }); }); it("๐Ÿ”ซ Hunter shoots at the witch (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "hunter", action: "shoot", targets: [{ player: players[4]._id }] }) diff --git a/tests/e2e/game/endless-votes.test.js b/tests/e2e/game/endless-votes.test.js index 6c1d925..79ee272 100644 --- a/tests/e2e/game/endless-votes.test.js +++ b/tests/e2e/game/endless-votes.test.js @@ -15,13 +15,16 @@ let players = [ { name: "Deg", role: "villager" }, { name: "Dog", role: "villager" }, ]; -let token, game; +let server, token, game; describe("S - Tiny game of 4 players in which there is no sheriff and a stuttering judge which make a lot of consecutive votes", () => { before(done => resetDatabase(done)); + before(done => { + server = app.listen(3000, done); + }); after(done => resetDatabase(done)); it("๐Ÿ‘ค Creates new user (POST /users)", done => { - chai.request(app) + chai.request(server) .post("/users") .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -31,7 +34,7 @@ describe("S - Tiny game of 4 players in which there is no sheriff and a stutteri }); }); it("๐Ÿ”‘ Logs in successfully (POST /users/login)", done => { - chai.request(app) + chai.request(server) .post(`/users/login`) .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -42,7 +45,7 @@ describe("S - Tiny game of 4 players in which there is no sheriff and a stutteri }); }); it("๐ŸŽฒ Creates game with JWT auth (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players, options: { roles: { sheriff: { isEnabled: false } } } }) @@ -53,7 +56,7 @@ describe("S - Tiny game of 4 players in which there is no sheriff and a stutteri }); }); it("โš–๏ธ Stuttering judge chooses sign (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "stuttering-judge", action: "choose-sign" }) @@ -65,19 +68,19 @@ describe("S - Tiny game of 4 players in which there is no sheriff and a stutteri }); it("โš–๏ธ Stuttering judge can't request a second vote if action is not 'vote' (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[3]._id }], doesJudgeRequestAnotherVote: true }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION_FOR_JUDGE_REQUEST"); + expect(res.body.type).to.equal("BAD_PLAY_ACTION_FOR_JUDGE_REQUEST"); done(); }); }); it("๐Ÿบ Werewolf eats a villager (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[3]._id }] }) @@ -89,7 +92,7 @@ describe("S - Tiny game of 4 players in which there is no sheriff and a stutteri }); it("๐Ÿ‘ช Tie in votes between werewolf and villager and stuttering judge requests another vote (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -101,7 +104,7 @@ describe("S - Tiny game of 4 players in which there is no sheriff and a stutteri .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.history[0].play.votesResult).to.equals("need-settlement"); + expect(game.history[0].play.votesResult).to.equal("need-settlement"); expect(game.players[0].isAlive).to.be.true; expect(game.players[2].isAlive).to.be.true; expect(game.waiting).to.be.an("array").lengthOf(2); @@ -112,7 +115,7 @@ describe("S - Tiny game of 4 players in which there is no sheriff and a stutteri }); it("๐Ÿ‘ช All vote for villager (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[0]._id, for: players[2]._id }] }) @@ -121,13 +124,13 @@ describe("S - Tiny game of 4 players in which there is no sheriff and a stutteri game = res.body; expect(game.players[2].isAlive).to.be.false; expect(game.players[2].murdered).to.deep.equals({ by: "all", of: "vote" }); - expect(game.history[0].play.votesResult).to.equals("death"); + expect(game.history[0].play.votesResult).to.equal("death"); done(); }); }); it("๐Ÿ‘ช Tie in votes between werewolf and stuttering judge (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -139,7 +142,7 @@ describe("S - Tiny game of 4 players in which there is no sheriff and a stutteri .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.history[0].play.votesResult).to.equals("need-settlement"); + expect(game.history[0].play.votesResult).to.equal("need-settlement"); expect(game.players[0].isAlive).to.be.true; expect(game.players[1].isAlive).to.be.true; expect(game.waiting).to.be.an("array").lengthOf(1); @@ -149,7 +152,7 @@ describe("S - Tiny game of 4 players in which there is no sheriff and a stutteri }); it("๐Ÿ‘ช All vote for stuttering judge (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[0]._id, for: players[1]._id }] }) @@ -158,7 +161,7 @@ describe("S - Tiny game of 4 players in which there is no sheriff and a stutteri game = res.body; expect(game.players[1].isAlive).to.be.false; expect(game.players[1].murdered).to.deep.equals({ by: "all", of: "vote" }); - expect(game.history[0].play.votesResult).to.equals("death"); + expect(game.history[0].play.votesResult).to.equal("death"); done(); }); }); diff --git a/tests/e2e/game/fox-lack-of-neighbors.test.js b/tests/e2e/game/fox-lack-of-neighbors.test.js new file mode 100644 index 0000000..fff1d4c --- /dev/null +++ b/tests/e2e/game/fox-lack-of-neighbors.test.js @@ -0,0 +1,119 @@ +const { describe, it, before, after } = require("mocha"); +const chai = require("chai"); +const chaiHttp = require("chai-http"); +const app = require("../../../app"); +const Config = require("../../../config"); +const { resetDatabase } = require("../../../src/helpers/functions/Test"); + +chai.use(chaiHttp); +const { expect } = chai; + +const credentials = { email: "test@test.fr", password: "secret" }; +const originalPlayers = [ + { name: "Dag", role: "fox" }, + { name: "Dig", role: "werewolf" }, + { name: "Deg", role: "villager" }, + { name: "Dog", role: "villager" }, +]; +let server, token, game, players; + +describe("W - Tiny game of 4 players in which fox has less and less neighbors to sniff", () => { + before(done => resetDatabase(done)); + before(done => { + server = app.listen(3000, done); + }); + after(done => resetDatabase(done)); + it("๐Ÿ‘ค Creates new user (POST /users)", done => { + chai.request(server) + .post("/users") + .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) + .send(credentials) + .end((err, res) => { + expect(res).to.have.status(200); + done(); + }); + }); + it("๐Ÿ”‘ Logs in successfully (POST /users/login)", done => { + chai.request(server) + .post(`/users/login`) + .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) + .send(credentials) + .end((err, res) => { + expect(res).to.have.status(200); + token = res.body.token; + done(); + }); + }); + it("๐ŸŽฒ Creates game with JWT auth (POST /games)", done => { + chai.request(server) + .post("/games") + .set({ Authorization: `Bearer ${token}` }) + .send({ players: originalPlayers, options: { roles: { sheriff: { isEnabled: false } } } }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + done(); + }); + }); + it("๐ŸฆŠ Fox sniffs himself (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "fox", action: "sniff", targets: [{ player: players[0]._id }] }) + .end((err, res) => { + game = res.body; + expect(res).to.have.status(200); + expect(game.history[0].play.targets).to.exist; + expect(game.history[0].play.targets).to.be.lengthOf(3); + expect(game.history[0].play.targets[0].player._id).to.equal(players[1]._id); + expect(game.history[0].play.targets[1].player._id).to.equal(players[0]._id); + expect(game.history[0].play.targets[2].player._id).to.equal(players[3]._id); + expect(game.players[0].attributes).to.not.exist; + done(); + }); + }); + it("๐Ÿบ Werewolf eats the first villager (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "werewolves", action: "eat", targets: [{ player: players[2]._id }] }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + done(); + }); + }); + it("๐Ÿ‘ช All vote for the second villager (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "all", action: "vote", votes: [{ from: players[0]._id, for: players[3]._id }] }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + expect(game.players[3].isAlive).to.be.false; + expect(game.players[3].murdered).to.deep.equals({ by: "all", of: "vote" }); + done(); + }); + }); + it("๐ŸฆŠ Fox sniffs the werewolf, only two targets are present because there is only two players (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "fox", action: "sniff", targets: [{ player: players[1]._id }] }) + .end((err, res) => { + game = res.body; + expect(res).to.have.status(200); + expect(game.history[0].play.targets).to.exist; + expect(game.history[0].play.targets).to.be.lengthOf(2); + expect(game.history[0].play.targets[0].player._id).to.equal(players[0]._id); + expect(game.history[0].play.targets[1].player._id).to.equal(players[1]._id); + expect(game.players[0].attributes).to.not.exist; + done(); + }); + }); +}); \ No newline at end of file diff --git a/tests/e2e/game/full-game.test.js b/tests/e2e/game/full-game.test.js index d74fdbe..d4082d3 100644 --- a/tests/e2e/game/full-game.test.js +++ b/tests/e2e/game/full-game.test.js @@ -39,19 +39,25 @@ let players = [ { name: "Dโ€ฐg", role: "stuttering-judge" }, { name: "D#g", role: "angel" }, { name: "Dยฑg", role: "thief" }, + { name: "Dฮฉg", role: "fox" }, + { name: "Dโˆ‘g", role: "rusty-sword-knight" }, + { name: "Dยฟg", role: "bear-tamer" }, ]; let additionalCards = [ { for: "thief", role: "werewolf" }, { for: "thief", role: "werewolf" }, ]; const options = { roles: { idiot: { doesDieOnAncientDeath: false } } }; -let token, game; +let server, token, game; -describe("B - Full game of 28 players with all roles", () => { +describe("B - Full game of 31 players with all roles", () => { before(done => resetDatabase(done)); + before(done => { + server = app.listen(3000, done); + }); after(done => resetDatabase(done)); it("๐Ÿ‘ค Creates new user (POST /users)", done => { - chai.request(app) + chai.request(server) .post("/users") .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -61,7 +67,7 @@ describe("B - Full game of 28 players with all roles", () => { }); }); it("๐Ÿ”‘ Logs in successfully (POST /users/login)", done => { - chai.request(app) + chai.request(server) .post(`/users/login`) .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -72,7 +78,7 @@ describe("B - Full game of 28 players with all roles", () => { }); }); it("๐ŸŽฒ Creates game with JWT auth (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players, options, additionalCards }) @@ -80,34 +86,54 @@ describe("B - Full game of 28 players with all roles", () => { expect(res).to.have.status(200); game = res.body; additionalCards = game.additionalCards; + expect(game.options.repartition.isHidden).to.be.false; + expect(game.options.roles.areRevealedOnDeath).to.be.true; + expect(game.options.roles.sheriff.electedAt.turn).to.be.equal(1); + expect(game.options.roles.sheriff.electedAt.phase).to.be.equal("night"); + expect(game.options.roles.bigBadWolf.isPowerlessIfWerewolfDies).to.be.true; + expect(game.options.roles.whiteWerewolf.wakingUpInterval).to.be.equal(2); + expect(game.options.roles.seer.canSeeRoles).to.be.true; + expect(game.options.roles.guard.canProtectTwice).to.be.false; + expect(game.options.roles.ancient.livesCountAgainstWerewolves).to.equal(2); + expect(game.options.roles.ancient.doesTakeHisRevenge).to.be.true; + expect(game.options.roles.fox.isPowerlessIfMissesWerewolf).to.be.true; + expect(game.options.roles.bearTamer.doesGrowlIfInfected).to.be.true; + expect(game.options.roles.stutteringJudge.voteRequestsCount).to.equal(1); + expect(game.options.roles.wildChild.isTransformationRevealed).to.be.false; + expect(game.options.roles.dogWolf.isChosenSideRevealed).to.be.false; + expect(game.options.roles.thief.mustChooseBetweenWerewolves).to.be.true; + expect(game.options.roles.thief.additionalCardsCount).to.be.equal(2); + expect(game.options.roles.piedPiper.charmedPeopleCountPerNight).to.equal(2); + expect(game.options.roles.piedPiper.isPowerlessIfInfected).to.be.true; + expect(game.options.roles.raven.markPenalty).to.be.equal(2); done(); }); }); it("๐Ÿ” Can't make a play if game's doesn't belong to user (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${new mongoose.Types.ObjectId()}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "elect-sheriff" }) .end((err, res) => { expect(res).to.have.status(401); - expect(res.body.type).to.equals("GAME_DOESNT_BELONG_TO_USER"); + expect(res.body.type).to.equal("GAME_DOESNT_BELONG_TO_USER"); done(); }); }); it("๐ŸŒŸ Can't update game review if its status is `playing` (PATCH /games/:id)", done => { - chai.request(app) + chai.request(server) .patch(`/games/${game._id}`) .set({ Authorization: `Bearer ${token}` }) .send({ review: { rating: 3 } }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_REQUEST"); + expect(res.body.type).to.equal("BAD_REQUEST"); done(); }); }); it("๐ŸŒ™ Night falls", done => { players = game.players; - expect(game.phase).to.equals("night"); + expect(game.phase).to.equal("night"); expect(players[0].role.isRevealed).to.be.false; expect(players[8].role.isRevealed).to.be.true; done(); @@ -117,52 +143,52 @@ describe("B - Full game of 28 players with all roles", () => { done(); }); it("๐Ÿ‘ช All can't elect sheriff if play's source is not 'all' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "seer", action: "look" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_SOURCE"); + expect(res.body.type).to.equal("BAD_PLAY_SOURCE"); done(); }); }); it("๐Ÿ‘ช All can't elect sheriff if play's action is not 'elect-sheriff' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "look" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION"); + expect(res.body.type).to.equal("BAD_PLAY_ACTION"); done(); }); }); it("๐Ÿ‘ช All can't elect sheriff if votes are not set (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "elect-sheriff" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("VOTES_REQUIRED"); + expect(res.body.type).to.equal("VOTES_REQUIRED"); done(); }); }); it("๐Ÿ‘ช All can't elect sheriff if votes are empty (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "elect-sheriff", votes: [] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("VOTES_CANT_BE_EMPTY"); + expect(res.body.type).to.equal("VOTES_CANT_BE_EMPTY"); done(); }); }); it("๐Ÿ‘ช All can't elect sheriff if one vote has same target and source (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -173,13 +199,13 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("SAME_VOTE_SOURCE_AND_TARGET"); + expect(res.body.type).to.equal("SAME_VOTE_SOURCE_AND_TARGET"); done(); }); }); it("๐Ÿ‘ช All can't elect sheriff if one vote has an unknown source (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -190,13 +216,13 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("CANT_VOTE"); + expect(res.body.type).to.equal("CANT_VOTE"); done(); }); }); it("๐Ÿ‘ช All can't elect sheriff if one vote has an unknown target (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -207,13 +233,13 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("CANT_BE_VOTE_TARGET"); + expect(res.body.type).to.equal("CANT_BE_VOTE_TARGET"); done(); }); }); it("๐Ÿ‘ช All can't elect sheriff if one player votes twice (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -224,13 +250,13 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("CANT_VOTE_MULTIPLE_TIMES"); + expect(res.body.type).to.equal("CANT_VOTE_MULTIPLE_TIMES"); done(); }); }); it("๐Ÿ‘ช All can't elect sheriff if there is a tie in votes (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -241,13 +267,13 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("TIE_IN_VOTES"); + expect(res.body.type).to.equal("TIE_IN_VOTES"); done(); }); }); it("๐Ÿ‘ช All elect the little girl as the sheriff (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -263,11 +289,11 @@ describe("B - Full game of 28 players with all roles", () => { expect(game.players[7].attributes).to.deep.include({ name: "sheriff", source: "all" }); expect(game.history).to.be.an("array").to.have.lengthOf(1); expect(game.history[0].play.votes).to.exist; - expect(game.history[0].play.votes[0].from._id).to.equals(game.players[0]._id); - expect(game.history[0].play.votes[0].for._id).to.equals(game.players[1]._id); - expect(game.history[0].play.votesResult).to.equals("election"); + expect(game.history[0].play.votes[0].from._id).to.equal(game.players[0]._id); + expect(game.history[0].play.votes[0].for._id).to.equal(game.players[1]._id); + expect(game.history[0].play.votesResult).to.equal("election"); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(game.players[7]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(game.players[7]._id); expect(game.history[0].play.source.name).to.equal("all"); expect(game.history[0].play.source.players).to.be.an("array").to.have.lengthOf(players.length); expect(game.history[0].deadPlayers).to.not.exist; @@ -279,29 +305,29 @@ describe("B - Full game of 28 players with all roles", () => { done(); }); it("๐Ÿ‘ช All can't vote if play's source is not 'all' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "seer", action: "look" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_SOURCE"); + expect(res.body.type).to.equal("BAD_PLAY_SOURCE"); done(); }); }); it("๐Ÿ‘ช All can't vote if play's action is not 'vote' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "look" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION"); + expect(res.body.type).to.equal("BAD_PLAY_ACTION"); done(); }); }); it("๐Ÿ‘ช All vote for one brother (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[0]._id, for: players[13]._id }] }) @@ -310,7 +336,7 @@ describe("B - Full game of 28 players with all roles", () => { game = res.body; expect(game.players[13].isAlive).to.be.false; expect(game.history).to.be.an("array").to.have.lengthOf(2); - expect(game.history[0].play.votesResult).to.equals("death"); + expect(game.history[0].play.votesResult).to.equal("death"); expect(game.history[0].deadPlayers).to.be.an("array").lengthOf(1); expect(game.history[0].deadPlayers[0]._id).to.be.equals(game.players[13]._id); done(); @@ -321,51 +347,51 @@ describe("B - Full game of 28 players with all roles", () => { done(); }); it("๐Ÿฆน Thief can't choose card if play's source is not 'thief' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "dog-wolf", action: "choose-card" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_SOURCE"); + expect(res.body.type).to.equal("BAD_PLAY_SOURCE"); done(); }); }); it("๐Ÿฆน Thief can't choose card if play's action is not 'choose-card' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "thief", action: "shoot" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION"); + expect(res.body.type).to.equal("BAD_PLAY_ACTION"); done(); }); }); it("๐Ÿฆน Thief can't skip if the two additional cards are from the 'werewolves' side (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "thief", action: "choose-card" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("THIEF_MUST_STEAL"); + expect(res.body.type).to.equal("THIEF_MUST_STEAL"); done(); }); }); it("๐Ÿฆน Thief can't choose an unknown card (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "thief", action: "choose-card", card: new mongoose.Types.ObjectId() }) .end((err, res) => { expect(res).to.have.status(404); - expect(res.body.type).to.equals("CHOSEN_CARD_NOT_FOUND"); + expect(res.body.type).to.equal("CHOSEN_CARD_NOT_FOUND"); done(); }); }); it("๐Ÿฆน Thief chooses the first werewolf card (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "thief", action: "choose-card", card: additionalCards[0]._id }) @@ -373,8 +399,8 @@ describe("B - Full game of 28 players with all roles", () => { expect(res).to.have.status(200); game = res.body; additionalCards = game.additionalCards; - expect(game.players[27].role.current).to.equals("werewolf"); - expect(game.players[27].side.current).to.equals("werewolves"); + expect(game.players[27].role.current).to.equal("werewolf"); + expect(game.players[27].side.current).to.equal("werewolves"); expect(game.history[0].play.card).to.deep.equals(additionalCards[0]); done(); }); @@ -384,53 +410,53 @@ describe("B - Full game of 28 players with all roles", () => { done(); }); it("๐Ÿ• Dog-wolf can't choose side if play's source is not 'dog-wolf' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "villager-villager", action: "choose-side" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_SOURCE"); + expect(res.body.type).to.equal("BAD_PLAY_SOURCE"); done(); }); }); it("๐Ÿ• Dog-wolf can't choose side if play's action is not 'choose-side' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "dog-wolf", action: "shoot" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION"); + expect(res.body.type).to.equal("BAD_PLAY_ACTION"); done(); }); }); it("๐Ÿ• Dog-wolf can't choose side if side is not set (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "dog-wolf", action: "choose-side" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("DOG_WOLF_MUST_CHOOSE_SIDE"); + expect(res.body.type).to.equal("DOG_WOLF_MUST_CHOOSE_SIDE"); done(); }); }); it("๐Ÿ• Dog-wolf chooses `werewolves` side (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "dog-wolf", action: "choose-side", side: "werewolves" }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.players[16].side.original).to.equals("villagers"); - expect(game.players[16].side.current).to.equals("werewolves"); + expect(game.players[16].side.original).to.equal("villagers"); + expect(game.players[16].side.current).to.equal("werewolves"); expect(game.history).to.be.an("array").to.have.lengthOf(3); - expect(game.history[0].play.side).to.equals("werewolves"); + expect(game.history[0].play.side).to.equal("werewolves"); expect(game.history[0].play.source.name).to.equal("dog-wolf"); expect(game.history[0].play.source.players).to.be.an("array").to.have.lengthOf(1); - expect(game.history[0].play.source.players[0]._id).to.equals(players[16]._id); + expect(game.history[0].play.source.players[0]._id).to.equal(players[16]._id); done(); }); }); @@ -439,75 +465,75 @@ describe("B - Full game of 28 players with all roles", () => { done(); }); it("๐Ÿน Cupid can't choose side if action is not 'choose-side' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "cupid", action: "charm", side: "werewolves" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION_FOR_SIDE_CHOICE"); + expect(res.body.type).to.equal("BAD_PLAY_ACTION_FOR_SIDE_CHOICE"); done(); }); }); it("๐Ÿน Cupid can't charm if play's source is not 'cupid' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "witch", action: "charm" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_SOURCE"); + expect(res.body.type).to.equal("BAD_PLAY_SOURCE"); done(); }); }); it("๐Ÿน Cupid can't charm if play's action is not 'charm' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "cupid", action: "shoot" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION"); + expect(res.body.type).to.equal("BAD_PLAY_ACTION"); done(); }); }); it("๐Ÿน Cupid can't charm if targets are not set (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "cupid", action: "charm" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("TARGETS_REQUIRED"); + expect(res.body.type).to.equal("TARGETS_REQUIRED"); done(); }); }); it("๐Ÿน Cupid can't charm if targets are empty (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "cupid", action: "charm", targets: [] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("TARGETS_CANT_BE_EMPTY"); + expect(res.body.type).to.equal("TARGETS_CANT_BE_EMPTY"); done(); }); }); it("๐Ÿน Cupid can't charm just one target (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "cupid", action: "charm", targets: [{ player: players[0]._id }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_TARGETS_LENGTH"); + expect(res.body.type).to.equal("BAD_TARGETS_LENGTH"); done(); }); }); it("๐Ÿน Cupid can't charm more than two targets (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -519,12 +545,12 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_TARGETS_LENGTH"); + expect(res.body.type).to.equal("BAD_TARGETS_LENGTH"); done(); }); }); it("๐Ÿน Cupid can't charm unknown targets (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -535,13 +561,13 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NOT_TARGETABLE"); + expect(res.body.type).to.equal("NOT_TARGETABLE"); done(); }); }); it("๐Ÿน Cupid can't charm the same targets (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -552,13 +578,13 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NON_UNIQUE_TARGETS"); + expect(res.body.type).to.equal("NON_UNIQUE_TARGETS"); done(); }); }); it("๐Ÿน Cupid can't infect a player (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -569,13 +595,13 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION_FOR_INFECTION"); + expect(res.body.type).to.equal("BAD_PLAY_ACTION_FOR_INFECTION"); done(); }); }); it("๐Ÿน Cupid can't use life potion on a target (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -586,13 +612,13 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION_FOR_POTION"); + expect(res.body.type).to.equal("BAD_PLAY_ACTION_FOR_POTION"); done(); }); }); it("๐Ÿน Cupid can't choose a card (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -603,13 +629,13 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION_FOR_CHOSEN_CARD"); + expect(res.body.type).to.equal("BAD_PLAY_ACTION_FOR_CHOSEN_CARD"); done(); }); }); it("๐Ÿน Cupid can't use death potion on a target (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -620,13 +646,13 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION_FOR_POTION"); + expect(res.body.type).to.equal("BAD_PLAY_ACTION_FOR_POTION"); done(); }); }); it("๐Ÿน Cupid charms himself and the little girl (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -642,190 +668,261 @@ describe("B - Full game of 28 players with all roles", () => { expect(game.players[9].attributes).to.deep.include({ name: "in-love", source: "cupid" }); expect(game.history).to.be.an("array").to.have.lengthOf(3); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[7]._id); - expect(game.history[0].play.targets[1].player._id).to.equals(players[9]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[7]._id); + expect(game.history[0].play.targets[1].player._id).to.equal(players[9]._id); expect(game.history[0].play.source.name).to.equal("cupid"); expect(game.history[0].play.source.players).to.be.an("array").to.have.lengthOf(1); - expect(game.history[0].play.source.players[0]._id).to.equals(players[9]._id); + expect(game.history[0].play.source.players[0]._id).to.equal(players[9]._id); done(); }); }); - it("๐ŸŽฒ Game is waiting for 'lovers' to 'meet-each-other'", done => { - expect(game.waiting[0]).to.deep.equals({ for: "lovers", to: "meet-each-other" }); + it("๐ŸŽฒ Game is waiting for 'seer' to 'look'", done => { + expect(game.waiting[0]).to.deep.equals({ for: "seer", to: "look" }); done(); }); - it("๐Ÿ’• Lovers can't meet each other if play's source is not 'lovers' (POST /games/:id/play)", done => { - chai.request(app) + it("๐Ÿ”ฎ Seer can't look if play's source is not 'seer' (POST /games/:id/play)", done => { + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) - .send({ source: "all", action: "meet-each-other" }) + .send({ source: "all", action: "look" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_SOURCE"); + expect(res.body.type).to.equal("BAD_PLAY_SOURCE"); done(); }); }); - it("๐Ÿ’• Lovers can't meet each other if play's action is not 'meet-each-other' (POST /games/:id/play)", done => { - chai.request(app) + it("๐Ÿ”ฎ Seer can't look if play's action is not 'look' (POST /games/:id/play)", done => { + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) - .send({ source: "lovers", action: "vote" }) + .send({ source: "seer", action: "elect-sheriff" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION"); + expect(res.body.type).to.equal("BAD_PLAY_ACTION"); done(); }); }); - it("๐Ÿ’• Lovers meet each other (POST /games/:id/play)", done => { - chai.request(app) + it("๐Ÿ”ฎ Seer can't look if targets are not set (POST /games/:id/play)", done => { + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) - .send({ source: "lovers", action: "meet-each-other" }) + .send({ source: "seer", action: "look" }) .end((err, res) => { - game = res.body; - expect(res).to.have.status(200); - expect(game.history).to.be.an("array").to.have.lengthOf(3); + expect(res).to.have.status(400); + expect(res.body.type).to.equal("TARGETS_REQUIRED"); done(); }); }); - it("๐ŸŽฒ Game is waiting for 'stuttering-judge' to 'choose-sign'", done => { - expect(game.waiting[0]).to.deep.equals({ for: "stuttering-judge", to: "choose-sign" }); - done(); - }); - it("โš–๏ธ Stuttering judge can't choose sign if play's source is not 'stuttering-judge' (POST /games/:id/play)", done => { - chai.request(app) + it("๐Ÿ”ฎ Seer can't look if targets are empty (POST /games/:id/play)", done => { + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) - .send({ source: "witch", action: "choose-sign" }) + .send({ source: "seer", action: "look", targets: [] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_SOURCE"); + expect(res.body.type).to.equal("TARGETS_CANT_BE_EMPTY"); done(); }); }); - it("โš–๏ธ Stuttering judge can't choose sign if play's action is not 'choose-sign' (POST /games/:id/play)", done => { - chai.request(app) + it("๐Ÿ”ฎ Seer can't look at multiple targets (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) - .send({ source: "stuttering-judge", action: "vote" }) + .send({ + source: "seer", action: "look", targets: [ + { player: players[0]._id }, + { player: players[1]._id }, + ], + }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION"); + expect(res.body.type).to.equal("BAD_TARGETS_LENGTH"); done(); }); }); - it("โš–๏ธ Stuttering judge chooses sign (POST /games/:id/play)", done => { - chai.request(app) + it("๐Ÿ”ฎ Seer can't look at unknown target (POST /games/:id/play)", done => { + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) - .send({ source: "stuttering-judge", action: "choose-sign" }) + .send({ source: "seer", action: "look", targets: [{ player: new mongoose.Types.ObjectId() }] }) .end((err, res) => { - game = res.body; - expect(res).to.have.status(200); + expect(res).to.have.status(400); + expect(res.body.type).to.equal("NOT_TARGETABLE"); done(); }); }); - it("๐ŸŽฒ Game is waiting for 'seer' to 'look'", done => { - expect(game.waiting[0]).to.deep.equals({ for: "seer", to: "look" }); - done(); - }); - it("๐Ÿ”ฎ Seer can't look if play's source is not 'seer' (POST /games/:id/play)", done => { - chai.request(app) + it("๐Ÿ”ฎ Seer can't look at herself (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) - .send({ source: "all", action: "look" }) + .send({ source: "seer", action: "look", targets: [{ player: players[1]._id }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_SOURCE"); + expect(res.body.type).to.equal("CANT_LOOK_AT_HERSELF"); done(); }); }); - it("๐Ÿ”ฎ Seer can't look if play's action is not 'look' (POST /games/:id/play)", done => { - chai.request(app) + it("๐Ÿ”ฎ Seer looks at the witch (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) - .send({ source: "seer", action: "elect-sheriff" }) + .send({ source: "seer", action: "look", targets: [{ player: players[0]._id }] }) .end((err, res) => { - expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION"); + expect(res).to.have.status(200); + game = res.body; + expect(game.players[0].attributes).to.deep.include({ name: "seen", source: "seer", remainingPhases: 1 }); + expect(game.history).to.be.an("array").to.have.lengthOf(3); + expect(game.history[0].play.targets).to.exist; + expect(game.history[0].play.targets[0].player._id).to.equal(players[0]._id); done(); }); }); - it("๐Ÿ”ฎ Seer can't look if targets are not set (POST /games/:id/play)", done => { - chai.request(app) + it("๐ŸŽฒ Game is waiting for 'fox' to 'sniff'", done => { + expect(game.waiting[0]).to.deep.equals({ for: "fox", to: "sniff" }); + done(); + }); + it("๐ŸฆŠ Fox can't sniff if play's source is not 'fox' (POST /games/:id/play)", done => { + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) - .send({ source: "seer", action: "look" }) + .send({ source: "witch", action: "sniff" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("TARGETS_REQUIRED"); + expect(res.body.type).to.equal("BAD_PLAY_SOURCE"); done(); }); }); - it("๐Ÿ”ฎ Seer can't look if targets are empty (POST /games/:id/play)", done => { - chai.request(app) + it("๐ŸฆŠ Fox can't sniff if play's action is not 'sniff' (POST /games/:id/play)", done => { + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) - .send({ source: "seer", action: "look", targets: [] }) + .send({ source: "fox", action: "eat" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("TARGETS_CANT_BE_EMPTY"); + expect(res.body.type).to.equal("BAD_PLAY_ACTION"); done(); }); }); - it("๐Ÿ”ฎ Seer can't look at multiple targets (POST /games/:id/play)", done => { + it("๐ŸฆŠ Fox can't sniff at multiple targets (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ - source: "seer", action: "look", targets: [ + source: "fox", action: "sniff", targets: [ { player: players[0]._id }, { player: players[1]._id }, ], }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_TARGETS_LENGTH"); + expect(res.body.type).to.equal("BAD_TARGETS_LENGTH"); done(); }); }); - it("๐Ÿ”ฎ Seer can't look at unknown target (POST /games/:id/play)", done => { - chai.request(app) + it("๐ŸฆŠ Fox can't sniff at unknown target (POST /games/:id/play)", done => { + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) - .send({ source: "seer", action: "look", targets: [{ player: new mongoose.Types.ObjectId() }] }) + .send({ source: "fox", action: "sniff", targets: [{ player: new mongoose.Types.ObjectId() }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NOT_TARGETABLE"); + expect(res.body.type).to.equal("NOT_TARGETABLE"); done(); }); }); - it("๐Ÿ”ฎ Seer can't look at herself (POST /games/:id/play)", done => { - players = game.players; - chai.request(app) + it("๐ŸฆŠ Fox sniffs the werewolf on the fifth position (POST /games/:id/play)", done => { + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) - .send({ source: "seer", action: "look", targets: [{ player: players[1]._id }] }) + .send({ source: "fox", action: "sniff", targets: [{ player: players[5]._id }] }) + .end((err, res) => { + game = res.body; + expect(res).to.have.status(200); + expect(game.history[0].play.targets).to.exist; + expect(game.history[0].play.targets).to.be.lengthOf(3); + expect(game.history[0].play.targets[0].player._id).to.equal(players[6]._id); + expect(game.history[0].play.targets[1].player._id).to.equal(players[5]._id); + expect(game.history[0].play.targets[2].player._id).to.equal(players[4]._id); + expect(game.players[28].attributes).to.not.exist; + done(); + }); + }); + it("๐ŸŽฒ Game is waiting for 'lovers' to 'meet-each-other'", done => { + expect(game.waiting[0]).to.deep.equals({ for: "lovers", to: "meet-each-other" }); + done(); + }); + it("๐Ÿ’• Lovers can't meet each other if play's source is not 'lovers' (POST /games/:id/play)", done => { + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "all", action: "meet-each-other" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("CANT_LOOK_AT_HERSELF"); + expect(res.body.type).to.equal("BAD_PLAY_SOURCE"); done(); }); }); - it("๐Ÿ”ฎ Seer looks at the witch (POST /games/:id/play)", done => { - players = game.players; - chai.request(app) + it("๐Ÿ’• Lovers can't meet each other if play's action is not 'meet-each-other' (POST /games/:id/play)", done => { + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) - .send({ source: "seer", action: "look", targets: [{ player: players[0]._id }] }) + .send({ source: "lovers", action: "vote" }) + .end((err, res) => { + expect(res).to.have.status(400); + expect(res.body.type).to.equal("BAD_PLAY_ACTION"); + done(); + }); + }); + it("๐Ÿ’• Lovers meet each other (POST /games/:id/play)", done => { + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "lovers", action: "meet-each-other" }) .end((err, res) => { - expect(res).to.have.status(200); game = res.body; - expect(game.players[0].attributes).to.deep.include({ name: "seen", source: "seer", remainingPhases: 1 }); + expect(res).to.have.status(200); expect(game.history).to.be.an("array").to.have.lengthOf(3); - expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[0]._id); + done(); + }); + }); + it("๐ŸŽฒ Game is waiting for 'stuttering-judge' to 'choose-sign'", done => { + expect(game.waiting[0]).to.deep.equals({ for: "stuttering-judge", to: "choose-sign" }); + done(); + }); + it("โš–๏ธ Stuttering judge can't choose sign if play's source is not 'stuttering-judge' (POST /games/:id/play)", done => { + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "witch", action: "choose-sign" }) + .end((err, res) => { + expect(res).to.have.status(400); + expect(res.body.type).to.equal("BAD_PLAY_SOURCE"); + done(); + }); + }); + it("โš–๏ธ Stuttering judge can't choose sign if play's action is not 'choose-sign' (POST /games/:id/play)", done => { + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "stuttering-judge", action: "vote" }) + .end((err, res) => { + expect(res).to.have.status(400); + expect(res.body.type).to.equal("BAD_PLAY_ACTION"); + done(); + }); + }); + it("โš–๏ธ Stuttering judge chooses sign (POST /games/:id/play)", done => { + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "stuttering-judge", action: "choose-sign" }) + .end((err, res) => { + game = res.body; + expect(res).to.have.status(200); done(); }); }); @@ -834,29 +931,29 @@ describe("B - Full game of 28 players with all roles", () => { done(); }); it("๐Ÿ‘ญ The two sisters can't meet each other if play's source is not 'two-sisters' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "lovers", action: "meet-each-other" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_SOURCE"); + expect(res.body.type).to.equal("BAD_PLAY_SOURCE"); done(); }); }); it("๐Ÿ‘ญ The two sisters can't meet each other if play's action is not 'meet-each-other' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "two-sisters", action: "use-potion" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION"); + expect(res.body.type).to.equal("BAD_PLAY_ACTION"); done(); }); }); it("๐Ÿ‘ญ The two sisters meet each other (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "two-sisters", action: "meet-each-other" }) @@ -871,29 +968,29 @@ describe("B - Full game of 28 players with all roles", () => { done(); }); it("๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ฆ The three brothers can't meet each other if play's source is not 'three-brothers' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "meet-each-other" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_SOURCE"); + expect(res.body.type).to.equal("BAD_PLAY_SOURCE"); done(); }); }); it("๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ฆ The three brothers can't meet each other if play's action is not 'meet-each-other' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "three-brothers", action: "delegate" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION"); + expect(res.body.type).to.equal("BAD_PLAY_ACTION"); done(); }); }); it("๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ฆ The three brothers meet each other (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "three-brothers", action: "meet-each-other" }) @@ -908,52 +1005,52 @@ describe("B - Full game of 28 players with all roles", () => { done(); }); it("๐Ÿ’ Wild child can't choose model if play's source is not 'wild-child' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "little-girl", action: "choose-model" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_SOURCE"); + expect(res.body.type).to.equal("BAD_PLAY_SOURCE"); done(); }); }); it("๐Ÿ’ Wild child can't choose model if play's action is not 'choose-model' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "wild-child", action: "use-potion" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION"); + expect(res.body.type).to.equal("BAD_PLAY_ACTION"); done(); }); }); it("๐Ÿ’ Wild child can't choose model if targets are not set (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "wild-child", action: "choose-model" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("TARGETS_REQUIRED"); + expect(res.body.type).to.equal("TARGETS_REQUIRED"); done(); }); }); it("๐Ÿ’ Wild child can't choose model if targets are empty (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "wild-child", action: "choose-model", targets: [] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("TARGETS_CANT_BE_EMPTY"); + expect(res.body.type).to.equal("TARGETS_CANT_BE_EMPTY"); done(); }); }); it("๐Ÿ’ Wild child can't choose multiple models (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -964,36 +1061,36 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_TARGETS_LENGTH"); + expect(res.body.type).to.equal("BAD_TARGETS_LENGTH"); done(); }); }); it("๐Ÿ’ Wild child can't choose an unknown model (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "wild-child", action: "choose-model", targets: [{ player: new mongoose.Types.ObjectId() }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NOT_TARGETABLE"); + expect(res.body.type).to.equal("NOT_TARGETABLE"); done(); }); }); it("๐Ÿ’ Wild child can't choose himself as a model (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "wild-child", action: "choose-model", targets: [{ player: players[15]._id }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("WILD_CHILD_CANT_CHOOSE_HIMSELF"); + expect(res.body.type).to.equal("WILD_CHILD_CANT_CHOOSE_HIMSELF"); done(); }); }); it("๐Ÿ’ Wild child chooses the werewolf as a model (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "wild-child", action: "choose-model", targets: [{ player: players[5]._id }] }) @@ -1002,7 +1099,7 @@ describe("B - Full game of 28 players with all roles", () => { game = res.body; expect(game.players[5].attributes).to.deep.include({ name: "worshiped", source: "wild-child" }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[5]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[5]._id); done(); }); }); @@ -1011,30 +1108,30 @@ describe("B - Full game of 28 players with all roles", () => { done(); }); it("๐Ÿชถ Raven can't mark if play's source is not 'raven' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "villager", action: "mark" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_SOURCE"); + expect(res.body.type).to.equal("BAD_PLAY_SOURCE"); done(); }); }); it("๐Ÿชถ Raven can't mark if play's action is not 'mark' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "raven", action: "use-potion" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION"); + expect(res.body.type).to.equal("BAD_PLAY_ACTION"); done(); }); }); it("๐Ÿชถ Raven can't mark multiple targets (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -1045,24 +1142,24 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_TARGETS_LENGTH"); + expect(res.body.type).to.equal("BAD_TARGETS_LENGTH"); done(); }); }); it("๐Ÿชถ Raven can't mark an unknown target (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "raven", action: "mark", targets: [{ player: new mongoose.Types.ObjectId() }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NOT_TARGETABLE"); + expect(res.body.type).to.equal("NOT_TARGETABLE"); done(); }); }); it("๐Ÿชถ Raven marks the villager (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "raven", action: "mark", targets: [{ player: players[6]._id }] }) @@ -1071,7 +1168,7 @@ describe("B - Full game of 28 players with all roles", () => { game = res.body; expect(game.players[6].attributes).to.deep.include({ name: "raven-marked", source: "raven", remainingPhases: 2 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[6]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[6]._id); done(); }); }); @@ -1080,52 +1177,52 @@ describe("B - Full game of 28 players with all roles", () => { done(); }); it("๐Ÿ›ก Guard can't protect if play's source is not 'guard' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "raven", action: "protect" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_SOURCE"); + expect(res.body.type).to.equal("BAD_PLAY_SOURCE"); done(); }); }); it("๐Ÿ›ก Guard can't protect if play's action is not 'protect' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "guard", action: "vote" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION"); + expect(res.body.type).to.equal("BAD_PLAY_ACTION"); done(); }); }); it("๐Ÿ›ก Guard can't protect if targets are not set (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "guard", action: "protect" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("TARGETS_REQUIRED"); + expect(res.body.type).to.equal("TARGETS_REQUIRED"); done(); }); }); it("๐Ÿ›ก Guard can't protect if targets are empty (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "guard", action: "protect", targets: [] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("TARGETS_CANT_BE_EMPTY"); + expect(res.body.type).to.equal("TARGETS_CANT_BE_EMPTY"); done(); }); }); it("๐Ÿ›ก Guard can't protect multiple targets (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -1136,24 +1233,24 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_TARGETS_LENGTH"); + expect(res.body.type).to.equal("BAD_TARGETS_LENGTH"); done(); }); }); it("๐Ÿ›ก Guard can't protect an unknown target (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "guard", action: "protect", targets: [{ player: new mongoose.Types.ObjectId() }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NOT_TARGETABLE"); + expect(res.body.type).to.equal("NOT_TARGETABLE"); done(); }); }); it("๐Ÿ›ก Guard protects the werewolf (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "guard", action: "protect", targets: [{ player: players[5]._id }] }) @@ -1162,7 +1259,7 @@ describe("B - Full game of 28 players with all roles", () => { game = res.body; expect(game.players[5].attributes).to.deep.include({ name: "protected", source: "guard", remainingPhases: 1 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[5]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[5]._id); done(); }); }); @@ -1171,52 +1268,52 @@ describe("B - Full game of 28 players with all roles", () => { done(); }); it("๐Ÿบ Werewolves can't eat if play's source is not 'werewolves' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "seer", action: "eat" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_SOURCE"); + expect(res.body.type).to.equal("BAD_PLAY_SOURCE"); done(); }); }); it("๐Ÿบ Werewolves can't eat if play's action is not 'eat' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "shoot" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION"); + expect(res.body.type).to.equal("BAD_PLAY_ACTION"); done(); }); }); it("๐Ÿบ Werewolves can't eat if targets are not set (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("TARGETS_REQUIRED"); + expect(res.body.type).to.equal("TARGETS_REQUIRED"); done(); }); }); it("๐Ÿบ Werewolves can't eat if targets are empty (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("TARGETS_CANT_BE_EMPTY"); + expect(res.body.type).to.equal("TARGETS_CANT_BE_EMPTY"); done(); }); }); it("๐Ÿบ Werewolves can't eat multiple targets (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -1227,48 +1324,48 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_TARGETS_LENGTH"); + expect(res.body.type).to.equal("BAD_TARGETS_LENGTH"); done(); }); }); it("๐Ÿบ Werewolves can't eat an unknown target (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: new mongoose.Types.ObjectId() }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NOT_TARGETABLE"); + expect(res.body.type).to.equal("NOT_TARGETABLE"); done(); }); }); it("๐Ÿบ Werewolves can't eat another werewolf (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[5]._id }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("CANT_EAT_EACH_OTHER"); + expect(res.body.type).to.equal("CANT_EAT_EACH_OTHER"); done(); }); }); it("๐Ÿบ Werewolves can't eat the dog-wolf because he chose the `werewolves` side (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[16]._id }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("CANT_EAT_EACH_OTHER"); + expect(res.body.type).to.equal("CANT_EAT_EACH_OTHER"); done(); }); }); it("๐Ÿบ Werewolves eat the guard (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[2]._id }] }) @@ -1277,7 +1374,7 @@ describe("B - Full game of 28 players with all roles", () => { game = res.body; expect(game.players[2].attributes).to.deep.include({ name: "eaten", source: "werewolves", remainingPhases: 1 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[2]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[2]._id); done(); }); }); @@ -1286,30 +1383,30 @@ describe("B - Full game of 28 players with all roles", () => { done(); }); it("๐Ÿบ White werewolf can't eat if play's source is not 'white-werewolf' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "hunter", action: "eat" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_SOURCE"); + expect(res.body.type).to.equal("BAD_PLAY_SOURCE"); done(); }); }); it("๐Ÿบ White werewolf can't eat if play's action is not 'eat' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "white-werewolf", action: "shoot" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION"); + expect(res.body.type).to.equal("BAD_PLAY_ACTION"); done(); }); }); it("๐Ÿบ White werewolf can't eat multiple targets (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -1320,48 +1417,48 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_TARGETS_LENGTH"); + expect(res.body.type).to.equal("BAD_TARGETS_LENGTH"); done(); }); }); it("๐Ÿบ White werewolf can't eat an unknown target (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "white-werewolf", action: "eat", targets: [{ player: new mongoose.Types.ObjectId() }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NOT_TARGETABLE"); + expect(res.body.type).to.equal("NOT_TARGETABLE"); done(); }); }); it("๐Ÿบ White werewolf can't eat a player in the `villagers` side (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "white-werewolf", action: "eat", targets: [{ player: players[0]._id }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("MUST_EAT_WEREWOLF"); + expect(res.body.type).to.equal("MUST_EAT_WEREWOLF"); done(); }); }); it("๐Ÿบ White werewolf can't eat himself (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "white-werewolf", action: "eat", targets: [{ player: players[23]._id }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("CANT_EAT_HIMSELF"); + expect(res.body.type).to.equal("CANT_EAT_HIMSELF"); done(); }); }); it("๐Ÿบ White werewolf skips (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "white-werewolf", action: "eat", targets: [] }) @@ -1376,52 +1473,52 @@ describe("B - Full game of 28 players with all roles", () => { done(); }); it("๐Ÿบ Big bad wolf can't eat if play's source is not 'big-bad-wolf' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "seer", action: "eat" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_SOURCE"); + expect(res.body.type).to.equal("BAD_PLAY_SOURCE"); done(); }); }); it("๐Ÿบ Big bad wolf can't eat if play's action is not 'eat' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "big-bad-wolf", action: "shoot" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION"); + expect(res.body.type).to.equal("BAD_PLAY_ACTION"); done(); }); }); it("๐Ÿบ Big bad wolf can't eat if targets are not set (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "big-bad-wolf", action: "eat" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("TARGETS_REQUIRED"); + expect(res.body.type).to.equal("TARGETS_REQUIRED"); done(); }); }); it("๐Ÿบ Big bad wolf can't eat if targets are empty (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "big-bad-wolf", action: "eat", targets: [] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("TARGETS_CANT_BE_EMPTY"); + expect(res.body.type).to.equal("TARGETS_CANT_BE_EMPTY"); done(); }); }); it("๐Ÿบ Big bad wolf can't eat multiple targets (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -1432,72 +1529,72 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_TARGETS_LENGTH"); + expect(res.body.type).to.equal("BAD_TARGETS_LENGTH"); done(); }); }); it("๐Ÿบ Big bad wolf can't eat an unknown target (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "big-bad-wolf", action: "eat", targets: [{ player: new mongoose.Types.ObjectId() }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NOT_TARGETABLE"); + expect(res.body.type).to.equal("NOT_TARGETABLE"); done(); }); }); it("๐Ÿบ Big bad wolf can't eat another werewolf (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "big-bad-wolf", action: "eat", targets: [{ player: players[5]._id }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("CANT_EAT_EACH_OTHER"); + expect(res.body.type).to.equal("CANT_EAT_EACH_OTHER"); done(); }); }); it("๐Ÿบ Big bad wolf can't eat the dog-wolf because he chose the `werewolves` side (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "big-bad-wolf", action: "eat", targets: [{ player: players[16]._id }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("CANT_EAT_EACH_OTHER"); + expect(res.body.type).to.equal("CANT_EAT_EACH_OTHER"); done(); }); }); it("๐Ÿบ Big bad wolf can't eat the target chosen by the `werewolves` side (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "big-bad-wolf", action: "eat", targets: [{ player: players[2]._id }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("TARGET_ALREADY_EATEN"); + expect(res.body.type).to.equal("TARGET_ALREADY_EATEN"); done(); }); }); it("๐Ÿบ Vile father of wolves can't infect the big bad wolf's target (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "big-bad-wolf", action: "eat", targets: [{ player: players[14]._id, isInfected: true }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("TARGET_MUST_BE_EATEN_BY_WEREWOLVES"); + expect(res.body.type).to.equal("TARGET_MUST_BE_EATEN_BY_WEREWOLVES"); done(); }); }); it("๐Ÿบ Big bad wolf eats the third brother (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "big-bad-wolf", action: "eat", targets: [{ player: players[14]._id }] }) @@ -1506,7 +1603,7 @@ describe("B - Full game of 28 players with all roles", () => { game = res.body; expect(game.players[14].attributes).to.deep.include({ name: "eaten", source: "big-bad-wolf", remainingPhases: 1 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[14]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[14]._id); done(); }); }); @@ -1515,65 +1612,65 @@ describe("B - Full game of 28 players with all roles", () => { done(); }); it("๐Ÿช„ Witch can't use potion if play's source is not 'witch' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "use-potion" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_SOURCE"); + expect(res.body.type).to.equal("BAD_PLAY_SOURCE"); done(); }); }); it("๐Ÿช„ Witch can't use potion if play's action is not 'use-potion' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "witch", action: "eat" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION"); + expect(res.body.type).to.equal("BAD_PLAY_ACTION"); done(); }); }); it("๐Ÿช„ Witch can't use potion if one target have both `hasDrankLifePotion` and `hasDrankDeathPotion` fields set to `true` (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "witch", action: "use-potion", targets: [{ player: players[0]._id, hasDrankLifePotion: true, hasDrankDeathPotion: true }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_TARGET_STRUCTURE"); + expect(res.body.type).to.equal("BAD_TARGET_STRUCTURE"); done(); }); }); it("๐Ÿช„ Witch can't use potion on unknown target (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "witch", action: "use-potion", targets: [{ player: new mongoose.Types.ObjectId(), hasDrankLifePotion: true }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NOT_TARGETABLE"); + expect(res.body.type).to.equal("NOT_TARGETABLE"); done(); }); }); it("๐Ÿช„ Witch can't use life potion on player not eaten by werewolves (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "witch", action: "use-potion", targets: [{ player: players[0]._id, hasDrankLifePotion: true }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_LIFE_POTION_USE"); + expect(res.body.type).to.equal("BAD_LIFE_POTION_USE"); done(); }); }); it("๐Ÿช„ Witch can't use life potion and death potion on same target (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -1584,13 +1681,13 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NON_UNIQUE_TARGETS"); + expect(res.body.type).to.equal("NON_UNIQUE_TARGETS"); done(); }); }); it("๐Ÿช„ Witch can't use death potion twice (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -1601,13 +1698,13 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("ONLY_ONE_DEATH_POTION"); + expect(res.body.type).to.equal("ONLY_ONE_DEATH_POTION"); done(); }); }); it("๐Ÿช„ Witch uses life potion on guard (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "witch", action: "use-potion", targets: [{ player: players[2]._id, hasDrankLifePotion: true }] }) @@ -1615,7 +1712,7 @@ describe("B - Full game of 28 players with all roles", () => { expect(res).to.have.status(200); game = res.body; expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[2]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[2]._id); expect(game.history[0].play.targets[0].hasDrankLifePotion).to.be.true; done(); }); @@ -1625,63 +1722,63 @@ describe("B - Full game of 28 players with all roles", () => { done(); }); it("๐Ÿ“ฃ Pied piper can't charm if play's source is not 'pied-piper' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "cupid", action: "charm" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_SOURCE"); + expect(res.body.type).to.equal("BAD_PLAY_SOURCE"); done(); }); }); it("๐Ÿ“ฃ Pied piper can't charm if play's action is not 'charm' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "pied-piper", action: "eat" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION"); + expect(res.body.type).to.equal("BAD_PLAY_ACTION"); done(); }); }); it("๐Ÿ“ฃ Pied piper can't charm if targets are not set (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "pied-piper", action: "charm" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("TARGETS_REQUIRED"); + expect(res.body.type).to.equal("TARGETS_REQUIRED"); done(); }); }); it("๐Ÿ“ฃ Pied piper can't charm if targets are empty (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "pied-piper", action: "charm", targets: [] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("TARGETS_CANT_BE_EMPTY"); + expect(res.body.type).to.equal("TARGETS_CANT_BE_EMPTY"); done(); }); }); it("๐Ÿ“ฃ Pied piper can't charm only one target (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "pied-piper", action: "charm", targets: [{ player: players[0]._id }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_TARGETS_LENGTH"); + expect(res.body.type).to.equal("BAD_TARGETS_LENGTH"); done(); }); }); it("๐Ÿ“ฃ Pied piper can't charm an unknown target (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -1692,12 +1789,12 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NOT_TARGETABLE"); + expect(res.body.type).to.equal("NOT_TARGETABLE"); done(); }); }); it("๐Ÿ“ฃ Pied piper can't charm himself (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -1708,12 +1805,12 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("CANT_CHARM_HIMSELF"); + expect(res.body.type).to.equal("CANT_CHARM_HIMSELF"); done(); }); }); it("๐Ÿ“ฃ Pied piper charms third brother and witch (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -1735,29 +1832,29 @@ describe("B - Full game of 28 players with all roles", () => { done(); }); it("๐Ÿ•บ๏ธ Charmed players can't meet each other if play's source is not 'charmed' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "witch", action: "meet-each-other" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_SOURCE"); + expect(res.body.type).to.equal("BAD_PLAY_SOURCE"); done(); }); }); it("๐Ÿ•บ๏ธ Charmed players can't meet each other if play's action is not 'meet-each-other' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "charmed", action: "vote" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION"); + expect(res.body.type).to.equal("BAD_PLAY_ACTION"); done(); }); }); it("๐Ÿ•บ๏ธ Charmed players meet each other (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "charmed", action: "meet-each-other" }) @@ -1769,23 +1866,24 @@ describe("B - Full game of 28 players with all roles", () => { }); }); it("โ˜€๏ธ Sun is rising", done => { - expect(game.phase).to.equals("day"); + expect(game.phase).to.equal("day"); expect(game.players[6].attributes).to.deep.include({ name: "raven-marked", source: "raven", remainingPhases: 1 }); expect(game.players[2].attributes).to.not.deep.include({ name: "drank-life-potion", source: "witch", remainingPhases: 1 }); expect(game.players[2].attributes).to.not.deep.include({ name: "eaten", source: "werewolves", remainingPhases: 1 }); expect(game.players[14].attributes).to.not.deep.include({ name: "eaten", source: "big-bad-wolf", remainingPhases: 1 }); expect(game.players[5].attributes).to.not.deep.include({ name: "protected", source: "guard", remainingPhases: 1 }); expect(game.players[0].attributes).to.not.deep.include({ name: "seen", source: "seer", remainingPhases: 1 }); + expect(game.players[30].attributes).to.not.exist; expect(game.players[2].isAlive).to.be.true; expect(game.players[2].role.isRevealed).to.be.false; expect(game.players[14].isAlive).to.be.false; expect(game.players[14].role.isRevealed).to.be.true; - expect(game.players[14].murdered.of).to.equals("eat"); - expect(game.players[14].murdered.by).to.equals("big-bad-wolf"); + expect(game.players[14].murdered.of).to.equal("eat"); + expect(game.players[14].murdered.by).to.equal("big-bad-wolf"); expect(game.history[0].deadPlayers).to.be.an("array").to.be.lengthOf(1); - expect(game.history[0].deadPlayers[0]._id).to.equals(players[14]._id); - expect(game.history[0].deadPlayers[0].murdered.of).to.equals("eat"); - expect(game.history[0].deadPlayers[0].murdered.by).to.equals("big-bad-wolf"); + expect(game.history[0].deadPlayers[0]._id).to.equal(players[14]._id); + expect(game.history[0].deadPlayers[0].murdered.of).to.equal("eat"); + expect(game.history[0].deadPlayers[0].murdered.by).to.equal("big-bad-wolf"); done(); }); it("๐ŸŽฒ Game is waiting for 'all' to 'vote'", done => { @@ -1793,52 +1891,52 @@ describe("B - Full game of 28 players with all roles", () => { done(); }); it("๐Ÿ‘ช All can't vote if play's source is not 'all' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "seer", action: "vote" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_SOURCE"); + expect(res.body.type).to.equal("BAD_PLAY_SOURCE"); done(); }); }); it("๐Ÿ‘ช All can't vote if play's action is not 'vote' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "eat" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION"); + expect(res.body.type).to.equal("BAD_PLAY_ACTION"); done(); }); }); it("๐Ÿ‘ช All can't vote if votes are not set (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("VOTES_REQUIRED"); + expect(res.body.type).to.equal("VOTES_REQUIRED"); done(); }); }); it("๐Ÿ‘ช All can't vote if votes are empty (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("VOTES_CANT_BE_EMPTY"); + expect(res.body.type).to.equal("VOTES_CANT_BE_EMPTY"); done(); }); }); it("๐Ÿ‘ช All can't vote if one vote has same target and source (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -1849,13 +1947,13 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("SAME_VOTE_SOURCE_AND_TARGET"); + expect(res.body.type).to.equal("SAME_VOTE_SOURCE_AND_TARGET"); done(); }); }); it("๐Ÿ‘ช All can't vote if one vote has an unknown source (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -1866,13 +1964,13 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("CANT_VOTE"); + expect(res.body.type).to.equal("CANT_VOTE"); done(); }); }); it("๐Ÿ‘ช All can't vote if one vote has an unknown target (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -1883,13 +1981,13 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("CANT_BE_VOTE_TARGET"); + expect(res.body.type).to.equal("CANT_BE_VOTE_TARGET"); done(); }); }); it("๐Ÿ‘ช All can't vote if one player votes twice (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -1900,13 +1998,13 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("CANT_VOTE_MULTIPLE_TIMES"); + expect(res.body.type).to.equal("CANT_VOTE_MULTIPLE_TIMES"); done(); }); }); it("๐Ÿ‘ช Tie in votes between villager and werewolf [Reason: villager is raven-marked ๐Ÿชถ and little girl, the sheriff, has double vote], then scapegoat dies (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -1927,9 +2025,9 @@ describe("B - Full game of 28 players with all roles", () => { expect(game.players[5].isAlive).to.be.true; expect(game.players[6].isAlive).to.be.true; expect(game.history[0].play.votes).to.exist; - expect(game.history[0].play.votesResult).to.equals("death"); + expect(game.history[0].play.votesResult).to.equal("death"); expect(game.history[0].deadPlayers).to.be.an("array").lengthOf(1); - expect(game.history[0].deadPlayers[0]._id).to.equals(game.players[20]._id); + expect(game.history[0].deadPlayers[0]._id).to.equal(game.players[20]._id); done(); }); }); @@ -1938,51 +2036,51 @@ describe("B - Full game of 28 players with all roles", () => { done(); }); it("๐Ÿ Scapegoat can't ban voting if play's source is not 'scapegoat' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "big-bad-wolf", action: "ban-voting" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_SOURCE"); + expect(res.body.type).to.equal("BAD_PLAY_SOURCE"); done(); }); }); it("๐Ÿ Scapegoat can't ban voting if play's action is not 'settle-votes' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "scapegoat", action: "look" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION"); + expect(res.body.type).to.equal("BAD_PLAY_ACTION"); done(); }); }); it("๐Ÿ Scapegoat can't ban voting an unknown target (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "scapegoat", action: "ban-voting", targets: [{ player: new mongoose.Types.ObjectId() }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NOT_TARGETABLE"); + expect(res.body.type).to.equal("NOT_TARGETABLE"); done(); }); }); it("๐Ÿ Scapegoat can't ban voting a dead target (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "scapegoat", action: "ban-voting", targets: [{ player: players[20]._id }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NOT_TARGETABLE"); + expect(res.body.type).to.equal("NOT_TARGETABLE"); done(); }); }); it("๐Ÿ Scapegoat bans voting the witch, the seer and the guard (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -2004,13 +2102,13 @@ describe("B - Full game of 28 players with all roles", () => { expect(game.players[2].attributes).to.exist; expect(game.players[2].attributes).to.deep.includes(cantVoteAttribute); expect(game.history[0].play.source.players).to.be.an("array").lengthOf(1); - expect(game.history[0].play.source.players[0]._id).to.equals(game.players[20]._id); + expect(game.history[0].play.source.players[0]._id).to.equal(game.players[20]._id); done(); }); }); it("๐ŸŒ™ Night falls", done => { - expect(game.phase).to.equals("night"); - expect(game.turn).to.equals(2); + expect(game.phase).to.equal("night"); + expect(game.turn).to.equal(2); done(); }); it("๐ŸŽฒ Game is waiting for 'seer' to 'look'", done => { @@ -2019,19 +2117,19 @@ describe("B - Full game of 28 players with all roles", () => { }); it("๐Ÿ”ฎ Seer can't look at dead target (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "seer", action: "look", targets: [{ player: players[20]._id }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NOT_TARGETABLE"); + expect(res.body.type).to.equal("NOT_TARGETABLE"); done(); }); }); it("๐Ÿ”ฎ Seer looks at the guard (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "seer", action: "look", targets: [{ player: players[2]._id }] }) @@ -2040,7 +2138,40 @@ describe("B - Full game of 28 players with all roles", () => { game = res.body; expect(game.players[2].attributes).to.deep.include({ name: "seen", source: "seer", remainingPhases: 1 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[2]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[2]._id); + done(); + }); + }); + it("๐ŸŽฒ Game is waiting for 'fox' to 'sniff'", done => { + expect(game.waiting[0]).to.deep.equals({ for: "fox", to: "sniff" }); + done(); + }); + it("๐ŸฆŠ Fox can't sniff at a dead target (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "fox", action: "sniff", targets: [{ player: players[20]._id }] }) + .end((err, res) => { + expect(res).to.have.status(400); + expect(res.body.type).to.equal("NOT_TARGETABLE"); + done(); + }); + }); + it("๐ŸฆŠ Fox sniffs the ancient on the 19th position which has a werewolf neighbor and a dead neighbor (POST /games/:id/play)", done => { + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "fox", action: "sniff", targets: [{ player: players[19]._id }] }) + .end((err, res) => { + game = res.body; + expect(res).to.have.status(200); + expect(game.history[0].play.targets).to.exist; + expect(game.history[0].play.targets).to.be.lengthOf(3); + expect(game.history[0].play.targets[0].player._id).to.equal(players[21]._id); + expect(game.history[0].play.targets[1].player._id).to.equal(players[19]._id); + expect(game.history[0].play.targets[2].player._id).to.equal(players[18]._id); + expect(game.players[28].attributes).to.not.exist; done(); }); }); @@ -2050,18 +2181,18 @@ describe("B - Full game of 28 players with all roles", () => { }); it("๐Ÿชถ Raven can't mark a dead target (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "raven", action: "mark", targets: [{ player: players[20]._id }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NOT_TARGETABLE"); + expect(res.body.type).to.equal("NOT_TARGETABLE"); done(); }); }); it("๐Ÿชถ Raven skips (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "raven", action: "mark" }) @@ -2078,31 +2209,31 @@ describe("B - Full game of 28 players with all roles", () => { }); it("๐Ÿ›ก Guard can't protect a dead target (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "guard", action: "protect", targets: [{ player: players[20]._id }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NOT_TARGETABLE"); + expect(res.body.type).to.equal("NOT_TARGETABLE"); done(); }); }); it("๐Ÿ›ก Guard can't protect the same player twice in a row (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "guard", action: "protect", targets: [{ player: players[5]._id }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("CANT_PROTECT_TWICE"); + expect(res.body.type).to.equal("CANT_PROTECT_TWICE"); done(); }); }); it("๐Ÿ›ก Guard protects himself (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "guard", action: "protect", targets: [{ player: players[2]._id }] }) @@ -2111,7 +2242,7 @@ describe("B - Full game of 28 players with all roles", () => { game = res.body; expect(game.players[2].attributes).to.deep.include({ name: "protected", source: "guard", remainingPhases: 1 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[2]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[2]._id); done(); }); }); @@ -2121,19 +2252,19 @@ describe("B - Full game of 28 players with all roles", () => { }); it("๐Ÿบ Werewolves can't eat a dead target (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[20]._id }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NOT_TARGETABLE"); + expect(res.body.type).to.equal("NOT_TARGETABLE"); done(); }); }); it("๐Ÿบ Werewolves eat the guard (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[2]._id }] }) @@ -2142,7 +2273,7 @@ describe("B - Full game of 28 players with all roles", () => { game = res.body; expect(game.players[2].attributes).to.deep.include({ name: "eaten", source: "werewolves", remainingPhases: 1 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[2]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[2]._id); done(); }); }); @@ -2152,19 +2283,19 @@ describe("B - Full game of 28 players with all roles", () => { }); it("๐Ÿบ Big bad wolf can't eat a dead target (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "big-bad-wolf", action: "eat", targets: [{ player: players[20]._id }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NOT_TARGETABLE"); + expect(res.body.type).to.equal("NOT_TARGETABLE"); done(); }); }); it("๐Ÿบ Big bad wolf eats one of the two sisters (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "big-bad-wolf", action: "eat", targets: [{ player: players[11]._id }] }) @@ -2173,7 +2304,7 @@ describe("B - Full game of 28 players with all roles", () => { game = res.body; expect(game.players[11].attributes).to.deep.include({ name: "eaten", source: "big-bad-wolf", remainingPhases: 1 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[11]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[11]._id); done(); }); }); @@ -2183,31 +2314,31 @@ describe("B - Full game of 28 players with all roles", () => { }); it("๐Ÿช„ Witch can't use death potion on dead target (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "witch", action: "use-potion", targets: [{ player: players[20]._id, hasDrankDeathPotion: true }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NOT_TARGETABLE"); + expect(res.body.type).to.equal("NOT_TARGETABLE"); done(); }); }); it("๐Ÿช„ Witch can't use life potion twice (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "witch", action: "use-potion", targets: [{ player: players[2]._id, hasDrankLifePotion: true }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("ONLY_ONE_LIFE_POTION"); + expect(res.body.type).to.equal("ONLY_ONE_LIFE_POTION"); done(); }); }); it("๐Ÿช„ Witch uses death potion on seer (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "witch", action: "use-potion", targets: [{ player: players[1]._id, hasDrankDeathPotion: true }] }) @@ -2215,13 +2346,13 @@ describe("B - Full game of 28 players with all roles", () => { expect(res).to.have.status(200); game = res.body; expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[1]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[1]._id); expect(game.history[0].play.targets[0].hasDrankDeathPotion).to.be.true; done(); }); }); it("๐Ÿ“ฃ Pied piper can't charm a dead player (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -2232,12 +2363,12 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NOT_TARGETABLE"); + expect(res.body.type).to.equal("NOT_TARGETABLE"); done(); }); }); it("๐Ÿ“ฃ Pied piper can't charm an already charmed player (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -2248,12 +2379,12 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("ALREADY_CHARMED"); + expect(res.body.type).to.equal("ALREADY_CHARMED"); done(); }); }); it("๐Ÿ“ฃ Pied piper charms seer and guard (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -2275,7 +2406,7 @@ describe("B - Full game of 28 players with all roles", () => { done(); }); it("๐Ÿ•บ๏ธ Charmed players meet each other (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "charmed", action: "meet-each-other" }) @@ -2286,11 +2417,12 @@ describe("B - Full game of 28 players with all roles", () => { }); }); it("โ˜€๏ธ Sun is rising", done => { - expect(game.phase).to.equals("day"); + expect(game.phase).to.equal("day"); expect(game.players[1].attributes).to.not.deep.include({ name: "drank-death-potion", source: "witch", remainingPhases: 1 }); expect(game.players[2].attributes).to.not.deep.include({ name: "seen", source: "seer", remainingPhases: 1 }); expect(game.players[2].attributes).to.not.deep.include({ name: "eaten", source: "werewolves", remainingPhases: 1 }); expect(game.players[2].attributes).to.not.deep.include({ name: "protected", source: "guard", remainingPhases: 1 }); + expect(game.players[30].attributes).to.not.exist; expect(game.players[1].isAlive).to.be.false; expect(game.players[1].murdered).to.deep.equals({ by: "witch", of: "use-potion" }); expect(game.players[2].isAlive).to.be.true; @@ -2310,7 +2442,7 @@ describe("B - Full game of 28 players with all roles", () => { }); it("๐Ÿ‘ช All can't vote if one vote has a dead source (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -2321,13 +2453,13 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("CANT_VOTE"); + expect(res.body.type).to.equal("CANT_VOTE"); done(); }); }); it("๐Ÿ‘ช All can't vote if one vote has a dead target (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -2338,13 +2470,13 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("CANT_BE_VOTE_TARGET"); + expect(res.body.type).to.equal("CANT_BE_VOTE_TARGET"); done(); }); }); it("๐Ÿ‘ช All can't vote if one voter has the `cant-vote` attribute (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -2355,13 +2487,13 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("CANT_VOTE"); + expect(res.body.type).to.equal("CANT_VOTE"); done(); }); }); it("๐Ÿ‘ช Tie in votes between villager-villager and pied piper (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -2373,7 +2505,7 @@ describe("B - Full game of 28 players with all roles", () => { .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.history[0].play.votesResult).to.equals("need-settlement"); + expect(game.history[0].play.votesResult).to.equal("need-settlement"); expect(game.players[8].isAlive).to.be.true; expect(game.players[22].isAlive).to.be.true; done(); @@ -2384,52 +2516,52 @@ describe("B - Full game of 28 players with all roles", () => { done(); }); it("๐ŸŽ– Sheriff can't settle votes if play's source is not 'sheriff' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "villager", action: "settle-votes" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_SOURCE"); + expect(res.body.type).to.equal("BAD_PLAY_SOURCE"); done(); }); }); it("๐ŸŽ– Sheriff can't settle votes if play's action is not 'settle-votes' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "sheriff", action: "eat" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION"); + expect(res.body.type).to.equal("BAD_PLAY_ACTION"); done(); }); }); it("๐ŸŽ– Sheriff can't settle votes if targets are not set (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "sheriff", action: "settle-votes" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("TARGETS_REQUIRED"); + expect(res.body.type).to.equal("TARGETS_REQUIRED"); done(); }); }); it("๐ŸŽ– Sheriff can't settle votes if targets are empty (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "sheriff", action: "settle-votes", targets: [] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("TARGETS_CANT_BE_EMPTY"); + expect(res.body.type).to.equal("TARGETS_CANT_BE_EMPTY"); done(); }); }); it("๐ŸŽ– Sheriff can't settle votes with multiple targets (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -2440,36 +2572,36 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_TARGETS_LENGTH"); + expect(res.body.type).to.equal("BAD_TARGETS_LENGTH"); done(); }); }); it("๐ŸŽ– Sheriff can't settle votes with unknown target (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "sheriff", action: "settle-votes", targets: [{ player: new mongoose.Types.ObjectId() }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NOT_TARGETABLE"); + expect(res.body.type).to.equal("NOT_TARGETABLE"); done(); }); }); it("๐ŸŽ– Sheriff can't settle votes with player who was not in previous tie in votes (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "sheriff", action: "settle-votes", targets: [{ player: players[0]._id }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("CANT_BE_CHOSEN_AS_TIEBREAKER"); + expect(res.body.type).to.equal("CANT_BE_CHOSEN_AS_TIEBREAKER"); done(); }); }); it("๐ŸŽ– Sheriff settles votes by choosing pied piper (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "sheriff", action: "settle-votes", targets: [{ player: players[22]._id }] }) @@ -2481,27 +2613,48 @@ describe("B - Full game of 28 players with all roles", () => { expect(game.players[22].murdered).to.deep.equals({ by: "sheriff", of: "settle-votes" }); expect(game.history[0].play.targets).to.exist; expect(game.history[0].deadPlayers).to.be.an("array").to.be.lengthOf(1); - expect(game.history[0].deadPlayers[0]._id).to.equals(players[22]._id); + expect(game.history[0].deadPlayers[0]._id).to.equal(players[22]._id); expect(game.history[0].deadPlayers[0].murdered).to.deep.equals({ by: "sheriff", of: "settle-votes" }); done(); }); }); it("๐ŸŒ™ Night falls", done => { - expect(game.phase).to.equals("night"); - expect(game.turn).to.equals(3); + expect(game.phase).to.equal("night"); + expect(game.turn).to.equal(3); const cantVoteAttribute = { name: "cant-vote", source: "scapegoat", remainingPhases: 1, activeAt: { turn: 2 } }; expect(game.players[0].attributes).to.not.deep.includes(cantVoteAttribute); expect(game.players[1].attributes).to.not.deep.includes(cantVoteAttribute); expect(game.players[2].attributes).to.not.deep.includes(cantVoteAttribute); done(); }); + it("๐ŸŽฒ Game is waiting for 'fox' to 'sniff'", done => { + expect(game.waiting[0]).to.deep.equals({ for: "fox", to: "sniff" }); + done(); + }); + it("๐ŸฆŠ Fox sniffs the ancient on the 19th position which has a werewolf neighbor and a dead neighbor (POST /games/:id/play)", done => { + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "fox", action: "sniff", targets: [{ player: players[19]._id }] }) + .end((err, res) => { + game = res.body; + expect(res).to.have.status(200); + expect(game.history[0].play.targets).to.exist; + expect(game.history[0].play.targets).to.be.lengthOf(3); + expect(game.history[0].play.targets[0].player._id).to.equal(players[21]._id); + expect(game.history[0].play.targets[1].player._id).to.equal(players[19]._id); + expect(game.history[0].play.targets[2].player._id).to.equal(players[18]._id); + expect(game.players[28].attributes).to.not.exist; + done(); + }); + }); it("๐ŸŽฒ Game is waiting for 'raven' to 'mark'", done => { expect(game.waiting[0]).to.deep.equals({ for: "raven", to: "mark" }); done(); }); it("๐Ÿชถ Raven marks the hunter (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "raven", action: "mark", targets: [{ player: players[4]._id }] }) @@ -2510,7 +2663,7 @@ describe("B - Full game of 28 players with all roles", () => { game = res.body; expect(game.players[4].attributes).to.deep.include({ name: "raven-marked", source: "raven", remainingPhases: 2 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[4]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[4]._id); done(); }); }); @@ -2520,7 +2673,7 @@ describe("B - Full game of 28 players with all roles", () => { }); it("๐Ÿ›ก Guard protects the little girl (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "guard", action: "protect", targets: [{ player: players[7]._id }] }) @@ -2529,7 +2682,7 @@ describe("B - Full game of 28 players with all roles", () => { game = res.body; expect(game.players[7].attributes).to.deep.include({ name: "protected", source: "guard", remainingPhases: 1 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[7]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[7]._id); done(); }); }); @@ -2539,7 +2692,7 @@ describe("B - Full game of 28 players with all roles", () => { }); it("๐Ÿบ Werewolves eat the little girl (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[7]._id }] }) @@ -2548,7 +2701,7 @@ describe("B - Full game of 28 players with all roles", () => { game = res.body; expect(game.players[7].attributes).to.deep.include({ name: "eaten", source: "werewolves", remainingPhases: 1 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[7]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[7]._id); done(); }); }); @@ -2558,7 +2711,7 @@ describe("B - Full game of 28 players with all roles", () => { }); it("๐Ÿบ White werewolf skips (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "white-werewolf", action: "eat" }) @@ -2570,7 +2723,7 @@ describe("B - Full game of 28 players with all roles", () => { }); it("๐Ÿบ Big bad wolf eats the angel (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "big-bad-wolf", action: "eat", targets: [{ player: players[26]._id }] }) @@ -2579,7 +2732,7 @@ describe("B - Full game of 28 players with all roles", () => { game = res.body; expect(game.players[26].attributes).to.deep.include({ name: "eaten", source: "big-bad-wolf", remainingPhases: 1 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[26]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[26]._id); done(); }); }); @@ -2589,18 +2742,18 @@ describe("B - Full game of 28 players with all roles", () => { }); it("๐Ÿช„ Witch can't use death potion twice (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "witch", action: "use-potion", targets: [{ player: players[3]._id, hasDrankDeathPotion: true }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("ONLY_ONE_DEATH_POTION"); + expect(res.body.type).to.equal("ONLY_ONE_DEATH_POTION"); done(); }); }); it("๐Ÿช„ Witch skips (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "witch", action: "use-potion" }) @@ -2612,13 +2765,14 @@ describe("B - Full game of 28 players with all roles", () => { }); }); it("โ˜€๏ธ Sun is rising, little girl is eaten even if protected by guard and cupid dies from broken heart ๐Ÿ’”", done => { - expect(game.phase).to.equals("day"); + expect(game.phase).to.equal("day"); expect(game.players[4].attributes).to.deep.include({ name: "raven-marked", source: "raven", remainingPhases: 1 }); expect(game.players[7].isAlive).to.be.false; expect(game.players[7].murdered).to.deep.equals({ by: "werewolves", of: "eat" }); expect(game.players[9].isAlive).to.be.false; expect(game.players[9].murdered).to.deep.equals({ by: "cupid", of: "charm" }); expect(game.players[7].attributes).to.not.deep.include({ name: "eaten", source: "werewolves", remainingPhases: 1 }); + expect(game.players[30].attributes).to.not.exist; done(); }); it("๐ŸŽฒ Game is waiting for 'sheriff' to 'delegate'", done => { @@ -2626,52 +2780,52 @@ describe("B - Full game of 28 players with all roles", () => { done(); }); it("๐ŸŽ– Sheriff can't delegate if play's source is not 'sheriff' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "villager", action: "delegate" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_SOURCE"); + expect(res.body.type).to.equal("BAD_PLAY_SOURCE"); done(); }); }); it("๐ŸŽ– Sheriff can't delegate if play's action is not 'delegate' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "sheriff", action: "eat" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION"); + expect(res.body.type).to.equal("BAD_PLAY_ACTION"); done(); }); }); it("๐ŸŽ– Sheriff can't delegate if targets are not set (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "sheriff", action: "delegate" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("TARGETS_REQUIRED"); + expect(res.body.type).to.equal("TARGETS_REQUIRED"); done(); }); }); it("๐ŸŽ– Sheriff can't delegate if targets are empty (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "sheriff", action: "delegate", targets: [] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("TARGETS_CANT_BE_EMPTY"); + expect(res.body.type).to.equal("TARGETS_CANT_BE_EMPTY"); done(); }); }); it("๐ŸŽ– Sheriff can't delegate to multiple targets (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -2682,36 +2836,36 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_TARGETS_LENGTH"); + expect(res.body.type).to.equal("BAD_TARGETS_LENGTH"); done(); }); }); it("๐ŸŽ– Sheriff can't delegate to unknown target (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "sheriff", action: "delegate", targets: [{ player: new mongoose.Types.ObjectId() }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NOT_TARGETABLE"); + expect(res.body.type).to.equal("NOT_TARGETABLE"); done(); }); }); it("๐ŸŽ– Sheriff can't delegate to a dead target (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "sheriff", action: "delegate", targets: [{ player: players[22]._id }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NOT_TARGETABLE"); + expect(res.body.type).to.equal("NOT_TARGETABLE"); done(); }); }); it("๐ŸŽ– Sheriff delegates to the hunter (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "sheriff", action: "delegate", targets: [{ player: players[4]._id }] }) @@ -2721,20 +2875,20 @@ describe("B - Full game of 28 players with all roles", () => { expect(game.players[0].attributes).to.not.deep.include({ name: "sheriff", source: "all" }); expect(game.players[4].attributes).to.deep.include({ name: "sheriff", source: "sheriff" }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(game.players[4]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(game.players[4]._id); done(); }); }); it("๐Ÿ‘ช All vote for hunter and stuttering judge request another vote (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[3]._id, for: players[4]._id }], doesJudgeRequestAnotherVote: true }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.history[0].play.votesResult).to.equals("death"); + expect(game.history[0].play.votesResult).to.equal("death"); expect(game.players[4].isAlive).to.be.false; expect(game.players[4].murdered).to.deep.equals({ by: "all", of: "vote" }); done(); @@ -2751,7 +2905,7 @@ describe("B - Full game of 28 players with all roles", () => { }); it("๐ŸŽ– Sheriff delegates to the raven (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "sheriff", action: "delegate", targets: [{ player: players[3]._id }] }) @@ -2761,57 +2915,57 @@ describe("B - Full game of 28 players with all roles", () => { expect(game.players[4].attributes).to.not.deep.include({ name: "sheriff", source: "all" }); expect(game.players[3].attributes).to.deep.include({ name: "sheriff", source: "sheriff" }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(game.players[3]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(game.players[3]._id); done(); }); }); it("๐Ÿ”ซ Hunter can't shoot if play's source is not 'hunter' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "raven", action: "shoot" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_SOURCE"); + expect(res.body.type).to.equal("BAD_PLAY_SOURCE"); done(); }); }); it("๐Ÿ”ซ Hunter can't shoot if play's action is not 'shoot' (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "hunter", action: "use-potion" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_PLAY_ACTION"); + expect(res.body.type).to.equal("BAD_PLAY_ACTION"); done(); }); }); it("๐Ÿ”ซ Hunter can't shoot if targets are not set (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "hunter", action: "shoot" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("TARGETS_REQUIRED"); + expect(res.body.type).to.equal("TARGETS_REQUIRED"); done(); }); }); it("๐Ÿ”ซ Hunter can't shoot if targets are empty (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "hunter", action: "shoot", targets: [] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("TARGETS_CANT_BE_EMPTY"); + expect(res.body.type).to.equal("TARGETS_CANT_BE_EMPTY"); done(); }); }); it("๐Ÿ”ซ Hunter can't shoot at multiple targets (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -2822,36 +2976,36 @@ describe("B - Full game of 28 players with all roles", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_TARGETS_LENGTH"); + expect(res.body.type).to.equal("BAD_TARGETS_LENGTH"); done(); }); }); it("๐Ÿ”ซ Hunter can't shoot at an unknown target (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "hunter", action: "shoot", targets: [{ player: new mongoose.Types.ObjectId() }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NOT_TARGETABLE"); + expect(res.body.type).to.equal("NOT_TARGETABLE"); done(); }); }); it("๐Ÿ”ซ Hunter can't shoot at a dead target (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "hunter", action: "shoot", targets: [{ player: players[22]._id }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NOT_TARGETABLE"); + expect(res.body.type).to.equal("NOT_TARGETABLE"); done(); }); }); it("๐Ÿ”ซ Hunter shoots at the werewolf (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "hunter", action: "shoot", targets: [{ player: players[5]._id }] }) @@ -2861,13 +3015,13 @@ describe("B - Full game of 28 players with all roles", () => { expect(game.players[5].isAlive).to.be.false; expect(game.players[5].murdered).to.deep.equals({ by: "hunter", of: "shoot" }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(game.players[5]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(game.players[5]._id); done(); }); }); it("๐Ÿ’ Wild child changed his side to `werewolves` because his model (the werewolf) just died", done => { - expect(game.players[15].side.original).to.equals("villagers"); - expect(game.players[15].side.current).to.equals("werewolves"); + expect(game.players[15].side.original).to.equal("villagers"); + expect(game.players[15].side.current).to.equal("werewolves"); done(); }); it("๐ŸŽฒ Game is waiting for 'all' to 'vote'", done => { @@ -2876,86 +3030,99 @@ describe("B - Full game of 28 players with all roles", () => { }); it("โš–๏ธ Stuttering judge can't request another vote if he already requested it (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[3]._id, for: players[4]._id }], doesJudgeRequestAnotherVote: true }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("ONLY_ONE_SECOND_VOTE_REQUEST"); + expect(res.body.type).to.equal("VOTE_REQUESTS_EXCEEDED"); done(); }); }); it("๐Ÿ‘ช All vote for the stuttering judge (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[3]._id, for: players[25]._id }] }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.history[0].play.votesResult).to.equals("death"); + expect(game.history[0].play.votesResult).to.equal("death"); expect(game.players[25].isAlive).to.be.false; expect(game.players[25].murdered).to.deep.equals({ by: "all", of: "vote" }); done(); }); }); it("๐ŸŒ™ Night falls", done => { - expect(game.phase).to.equals("night"); - expect(game.turn).to.equals(4); + expect(game.phase).to.equal("night"); + expect(game.turn).to.equal(4); done(); }); it("๐ŸŽฒ Get game with full history (GET /games/:id?history-limit=0)", done => { - chai.request(app) + chai.request(server) .get(`/games/${game._id}?history-limit=0`) .set({ Authorization: `Bearer ${token}` }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.history.length).to.equals(42); + expect(game.history.length).to.equal(45); done(); }); }); it("๐Ÿ“œ Get only full game history (GET /games/:id/history)", done => { - chai.request(app) + chai.request(server) .get(`/games/${game._id}/history`) .set({ Authorization: `Bearer ${token}` }) .end((err, res) => { expect(res).to.have.status(200); const history = res.body; - expect(history.length).to.equals(42); + expect(history.length).to.equal(45); done(); }); }); it("๐Ÿ“œ Get only witch plays in game history (GET /games/:id/history?play-source=witch)", done => { - chai.request(app) + chai.request(server) .get(`/games/${game._id}/history?play-source=witch`) .set({ Authorization: `Bearer ${token}` }) .end((err, res) => { expect(res).to.have.status(200); const history = res.body; - expect(history.length).to.equals(3); - expect(history[0].play.source.name).to.equals("witch"); - expect(history[1].play.source.name).to.equals("witch"); - expect(history[2].play.source.name).to.equals("witch"); + expect(history.length).to.equal(3); + expect(history[0].play.source.name).to.equal("witch"); + expect(history[1].play.source.name).to.equal("witch"); + expect(history[2].play.source.name).to.equal("witch"); done(); }); }); it("๐Ÿ“œ Get only choose-side plays in game history (GET /games/:id/history?play-action=choose-side)", done => { - chai.request(app) + chai.request(server) .get(`/games/${game._id}/history?play-action=choose-side`) .set({ Authorization: `Bearer ${token}` }) .end((err, res) => { expect(res).to.have.status(200); const history = res.body; - expect(history.length).to.equals(1); - expect(history[0].play.action).to.equals("choose-side"); + expect(history.length).to.equal(1); + expect(history[0].play.action).to.equal("choose-side"); + done(); + }); + }); + it("๐ŸฆŠ Fox skips (POST /games/:id/play)", done => { + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "fox", action: "sniff", targets: [] }) + .end((err, res) => { + game = res.body; + expect(res).to.have.status(200); + expect(game.history[0].play.targets).to.not.exist; + expect(game.players[28].attributes).to.not.exist; done(); }); }); it("๐Ÿชถ Raven skips (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "raven", action: "mark" }) @@ -2968,7 +3135,7 @@ describe("B - Full game of 28 players with all roles", () => { }); it("๐Ÿ›ก Guard protects himself (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "guard", action: "protect", targets: [{ player: players[2]._id }] }) @@ -2977,25 +3144,25 @@ describe("B - Full game of 28 players with all roles", () => { game = res.body; expect(game.players[2].attributes).to.deep.include({ name: "protected", source: "guard", remainingPhases: 1 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[2]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[2]._id); done(); }); }); it("๐Ÿบ Werewolves can't eat the wild child because he is a fresh new werewolf (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[15]._id }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("CANT_EAT_EACH_OTHER"); + expect(res.body.type).to.equal("CANT_EAT_EACH_OTHER"); done(); }); }); it("๐Ÿบ Werewolves eat the witch, but vile father of wolves infects her before (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[0]._id, isInfected: true }] }) @@ -3003,15 +3170,15 @@ describe("B - Full game of 28 players with all roles", () => { expect(res).to.have.status(200); game = res.body; expect(game.players[0].attributes).to.not.deep.include({ name: "eaten", source: "werewolves", remainingPhases: 1 }); - expect(game.players[0].side.current).to.equals("werewolves"); + expect(game.players[0].side.current).to.equal("werewolves"); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[0]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[0]._id); expect(game.history[0].play.targets[0].isInfected).to.be.true; done(); }); }); it("๐Ÿช„ Witch skips (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "witch", action: "use-potion" }) @@ -3023,44 +3190,61 @@ describe("B - Full game of 28 players with all roles", () => { }); }); it("โ˜€๏ธ Sun is rising", done => { - expect(game.phase).to.equals("day"); + expect(game.phase).to.equal("day"); expect(game.players[0].isAlive).to.be.true; + expect(game.players[30].attributes).to.deep.include({ name: "growls", source: "bear-tamer", remainingPhases: 1 }); done(); }); it("โš–๏ธ Stuttering judge can't request another vote if he is dead (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[2]._id, for: players[15]._id }], doesJudgeRequestAnotherVote: true }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("STUTTERING_JUDGE_ABSENT"); + expect(res.body.type).to.equal("STUTTERING_JUDGE_ABSENT"); done(); }); }); it("๐Ÿ‘ช All vote for wild child (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[2]._id, for: players[15]._id }] }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.history[0].play.votesResult).to.equals("death"); + expect(game.history[0].play.votesResult).to.equal("death"); expect(game.players[15].isAlive).to.be.false; expect(game.players[15].murdered).to.deep.equals({ by: "all", of: "vote" }); done(); }); }); it("๐ŸŒ™ Night falls", done => { - expect(game.phase).to.equals("night"); - expect(game.turn).to.equals(5); + expect(game.phase).to.equal("night"); + expect(game.turn).to.equal(5); done(); }); + it("๐ŸฆŠ Fox sniffs the witch which is an infected werewolf (POST /games/:id/play)", done => { + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "fox", action: "sniff", targets: [{ player: players[0]._id }] }) + .end((err, res) => { + game = res.body; + expect(res).to.have.status(200); + expect(game.history[0].play.targets).to.exist; + expect(game.history[0].play.targets).to.be.lengthOf(3); + expect(game.history[0].play.targets[0].player._id).to.equal(players[2]._id); + expect(game.history[0].play.targets[1].player._id).to.equal(players[0]._id); + expect(game.history[0].play.targets[2].player._id).to.equal(players[30]._id); + done(); + }); + }); it("๐Ÿชถ Raven skips (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "raven", action: "mark" }) @@ -3073,7 +3257,7 @@ describe("B - Full game of 28 players with all roles", () => { }); it("๐Ÿ›ก Guard protects a werewolf (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "guard", action: "protect", targets: [{ player: players[24]._id }] }) @@ -3082,25 +3266,25 @@ describe("B - Full game of 28 players with all roles", () => { game = res.body; expect(game.players[24].attributes).to.deep.include({ name: "protected", source: "guard", remainingPhases: 1 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[24]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[24]._id); done(); }); }); it("๐Ÿบ Vile father of wolves can't infect twice (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[10]._id, isInfected: true }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("ONLY_ONE_INFECTION"); + expect(res.body.type).to.equal("ONLY_ONE_INFECTION"); done(); }); }); it("๐Ÿบ Werewolves eat one of the two sisters (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[10]._id }] }) @@ -3108,24 +3292,24 @@ describe("B - Full game of 28 players with all roles", () => { expect(res).to.have.status(200); game = res.body; expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[10]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[10]._id); done(); }); }); it("๐Ÿบ White werewolf can't eat a dead target (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "white-werewolf", action: "eat", targets: [{ player: players[5]._id }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NOT_TARGETABLE"); + expect(res.body.type).to.equal("NOT_TARGETABLE"); done(); }); }); it("๐Ÿบ White werewolf eats one werewolf (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "white-werewolf", action: "eat", targets: [{ player: players[24]._id }] }) @@ -3134,12 +3318,12 @@ describe("B - Full game of 28 players with all roles", () => { game = res.body; expect(game.players[24].attributes).to.deep.include({ name: "eaten", source: "white-werewolf", remainingPhases: 1 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[24]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[24]._id); done(); }); }); it("๐Ÿช„ Witch skips (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "witch", action: "use-potion", targets: [] }) @@ -3151,35 +3335,53 @@ describe("B - Full game of 28 players with all roles", () => { }); }); it("โ˜€๏ธ Sun is rising", done => { - expect(game.phase).to.equals("day"); + expect(game.phase).to.equal("day"); expect(game.players[10].isAlive).to.be.false; expect(game.players[24].attributes).to.not.deep.include({ name: "eaten", source: "white-werewolf", remainingPhases: 1 }); expect(game.players[24].isAlive).to.be.false; expect(game.players[24].murdered).to.deep.equals({ by: "white-werewolf", of: "eat" }); + expect(game.players[30].attributes).to.deep.include({ name: "growls", source: "bear-tamer", remainingPhases: 1 }); done(); }); it("๐Ÿ‘ช All vote for vile father of wolves (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[2]._id, for: players[18]._id }] }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.history[0].play.votesResult).to.equals("death"); + expect(game.history[0].play.votesResult).to.equal("death"); expect(game.players[18].isAlive).to.be.false; expect(game.players[18].murdered).to.deep.equals({ by: "all", of: "vote" }); done(); }); }); it("๐ŸŒ™ Night falls", done => { - expect(game.phase).to.equals("night"); - expect(game.turn).to.equals(6); + expect(game.phase).to.equal("night"); + expect(game.turn).to.equal(6); done(); }); + it("๐ŸฆŠ Fox sniffs the player on the 8th position, which has no alive werewolves neighbors and so, fox becomes powerless (POST /games/:id/play)", done => { + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "fox", action: "sniff", targets: [{ player: players[8]._id }] }) + .end((err, res) => { + game = res.body; + expect(res).to.have.status(200); + expect(game.history[0].play.targets).to.exist; + expect(game.history[0].play.targets).to.be.lengthOf(3); + expect(game.history[0].play.targets[0].player._id).to.equal(players[12]._id); + expect(game.history[0].play.targets[1].player._id).to.equal(players[8]._id); + expect(game.history[0].play.targets[2].player._id).to.equal(players[6]._id); + expect(game.players[28].attributes).to.deep.include({ name: "powerless", source: "fox" }); + done(); + }); + }); it("๐Ÿชถ Raven skips (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "raven", action: "mark" }) @@ -3192,7 +3394,7 @@ describe("B - Full game of 28 players with all roles", () => { }); it("๐Ÿ›ก Guard protects himself (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "guard", action: "protect", targets: [{ player: players[2]._id }] }) @@ -3201,25 +3403,25 @@ describe("B - Full game of 28 players with all roles", () => { game = res.body; expect(game.players[2].attributes).to.deep.include({ name: "protected", source: "guard", remainingPhases: 1 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[2]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[2]._id); done(); }); }); it("๐Ÿบ Vile father of wolves can't infect because he is dead (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[12]._id, isInfected: true }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("ABSENT_VILE_FATHER_OF_WOLVES"); + expect(res.body.type).to.equal("ABSENT_VILE_FATHER_OF_WOLVES"); done(); }); }); it("๐Ÿบ Werewolves eat the ancient (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[19]._id }] }) @@ -3227,12 +3429,12 @@ describe("B - Full game of 28 players with all roles", () => { expect(res).to.have.status(200); game = res.body; expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[19]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[19]._id); done(); }); }); it("๐Ÿช„ Witch skips (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "witch", action: "use-potion", targets: [] }) @@ -3244,34 +3446,35 @@ describe("B - Full game of 28 players with all roles", () => { }); }); it("โ˜€๏ธ Sun is rising, ancient is not dead because he has another life", done => { - expect(game.phase).to.equals("day"); + expect(game.phase).to.equal("day"); expect(game.players[19].role.isRevealed).to.be.false; expect(game.players[19].isAlive).to.be.true; expect(game.history[0].revealedPlayers).to.not.exist; + expect(game.players[30].attributes).to.deep.include({ name: "growls", source: "bear-tamer", remainingPhases: 1 }); done(); }); it("๐Ÿ‘ช All vote for the big bad wolf (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[2]._id, for: players[17]._id }] }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.history[0].play.votesResult).to.equals("death"); + expect(game.history[0].play.votesResult).to.equal("death"); expect(game.players[17].isAlive).to.be.false; expect(game.players[17].murdered).to.deep.equals({ by: "all", of: "vote" }); done(); }); }); it("๐ŸŒ™ Night falls", done => { - expect(game.phase).to.equals("night"); - expect(game.turn).to.equals(7); + expect(game.phase).to.equal("night"); + expect(game.turn).to.equal(7); done(); }); it("๐Ÿชถ Raven skips (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "raven", action: "mark" }) @@ -3284,7 +3487,7 @@ describe("B - Full game of 28 players with all roles", () => { }); it("๐Ÿ›ก Guard protects the raven (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "guard", action: "protect", targets: [{ player: players[3]._id }] }) @@ -3293,13 +3496,13 @@ describe("B - Full game of 28 players with all roles", () => { game = res.body; expect(game.players[3].attributes).to.deep.include({ name: "protected", source: "guard", remainingPhases: 1 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[3]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[3]._id); done(); }); }); it("๐Ÿบ Werewolves eat the raven but it's protected by the guard (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[3]._id }] }) @@ -3307,12 +3510,12 @@ describe("B - Full game of 28 players with all roles", () => { expect(res).to.have.status(200); game = res.body; expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[3]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[3]._id); done(); }); }); it("๐Ÿบ White werewolf eats the dog-wolf (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "white-werewolf", action: "eat", targets: [{ player: players[16]._id }] }) @@ -3323,7 +3526,7 @@ describe("B - Full game of 28 players with all roles", () => { }); }); it("๐Ÿช„ Witch skips (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "witch", action: "use-potion", targets: [] }) @@ -3335,34 +3538,35 @@ describe("B - Full game of 28 players with all roles", () => { }); }); it("โ˜€๏ธ Sun is rising", done => { - expect(game.phase).to.equals("day"); + expect(game.phase).to.equal("day"); expect(game.players[3].isAlive).to.be.true; expect(game.players[16].isAlive).to.be.false; expect(game.players[16].murdered).to.deep.equals({ by: "white-werewolf", of: "eat" }); + expect(game.players[30].attributes).to.deep.include({ name: "growls", source: "bear-tamer", remainingPhases: 1 }); done(); }); it("๐Ÿ‘ช All vote for the white werewolf (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[2]._id, for: players[23]._id }] }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.history[0].play.votesResult).to.equals("death"); + expect(game.history[0].play.votesResult).to.equal("death"); expect(game.players[23].isAlive).to.be.false; expect(game.players[23].murdered).to.deep.equals({ by: "all", of: "vote" }); done(); }); }); it("๐ŸŒ™ Night falls", done => { - expect(game.phase).to.equals("night"); - expect(game.turn).to.equals(8); + expect(game.phase).to.equal("night"); + expect(game.turn).to.equal(8); done(); }); it("๐Ÿชถ Raven skips (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "raven", action: "mark" }) @@ -3375,7 +3579,7 @@ describe("B - Full game of 28 players with all roles", () => { }); it("๐Ÿ›ก Guard protects himself (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "guard", action: "protect", targets: [{ player: players[2]._id }] }) @@ -3384,13 +3588,13 @@ describe("B - Full game of 28 players with all roles", () => { game = res.body; expect(game.players[2].attributes).to.deep.include({ name: "protected", source: "guard", remainingPhases: 1 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[2]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[2]._id); done(); }); }); it("๐Ÿบ Werewolves eat the raven again (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[3]._id }] }) @@ -3398,12 +3602,12 @@ describe("B - Full game of 28 players with all roles", () => { expect(res).to.have.status(200); game = res.body; expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[3]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[3]._id); done(); }); }); it("๐Ÿช„ Witch skips (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "witch", action: "use-potion", targets: [] }) @@ -3416,7 +3620,7 @@ describe("B - Full game of 28 players with all roles", () => { }); it("๐ŸŽ– Sheriff delegates to the idiot (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "sheriff", action: "delegate", targets: [{ player: players[21]._id }] }) @@ -3426,25 +3630,26 @@ describe("B - Full game of 28 players with all roles", () => { expect(game.players[3].attributes).to.not.deep.include({ name: "sheriff", source: "all" }); expect(game.players[21].attributes).to.deep.include({ name: "sheriff", source: "sheriff" }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(game.players[21]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(game.players[21]._id); done(); }); }); it("โ˜€๏ธ Sun is rising", done => { - expect(game.phase).to.equals("day"); + expect(game.phase).to.equal("day"); expect(game.players[3].isAlive).to.be.false; + expect(game.players[30].attributes).to.deep.include({ name: "growls", source: "bear-tamer", remainingPhases: 1 }); done(); }); it("๐Ÿ‘ช All vote for the idiot but he doesn't die, only his role is revealed and he can't vote for the rest of the game (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[2]._id, for: players[21]._id }] }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.history[0].play.votesResult).to.equals("no-death"); + expect(game.history[0].play.votesResult).to.equal("no-death"); expect(game.players[21].role.isRevealed).to.be.true; expect(game.players[21].attributes).to.deep.include({ name: "cant-vote", source: "all" }); expect(game.players[21].isAlive).to.be.true; @@ -3452,13 +3657,13 @@ describe("B - Full game of 28 players with all roles", () => { }); }); it("๐ŸŒ™ Night falls", done => { - expect(game.phase).to.equals("night"); - expect(game.turn).to.equals(9); + expect(game.phase).to.equal("night"); + expect(game.turn).to.equal(9); done(); }); it("๐Ÿ›ก Guard protects the witch (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "guard", action: "protect", targets: [{ player: players[0]._id }] }) @@ -3467,13 +3672,13 @@ describe("B - Full game of 28 players with all roles", () => { game = res.body; expect(game.players[0].attributes).to.deep.include({ name: "protected", source: "guard", remainingPhases: 1 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[0]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[0]._id); done(); }); }); it("๐Ÿบ Werewolves eat the ancient again and will die (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[19]._id }] }) @@ -3481,12 +3686,12 @@ describe("B - Full game of 28 players with all roles", () => { expect(res).to.have.status(200); game = res.body; expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[19]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[19]._id); done(); }); }); it("๐Ÿช„ Witch skips (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "witch", action: "use-potion", targets: [] }) @@ -3498,39 +3703,40 @@ describe("B - Full game of 28 players with all roles", () => { }); }); it("โ˜€๏ธ Sun is rising, ancient is dead this time and idiot is still alive because of game option", done => { - expect(game.phase).to.equals("day"); + expect(game.phase).to.equal("day"); expect(game.players[19].role.isRevealed).to.be.true; expect(game.players[19].isAlive).to.be.false; expect(game.history[0].revealedPlayers).to.exist; expect(game.history[0].revealedPlayers).to.be.an("array").lengthOf(1); - expect(game.history[0].revealedPlayers[0]._id).to.equals(players[19]._id); + expect(game.history[0].revealedPlayers[0]._id).to.equal(players[19]._id); expect(game.history[0].deadPlayers).to.exist; expect(game.history[0].deadPlayers).to.be.an("array").lengthOf(1); - expect(game.history[0].deadPlayers[0]._id).to.equals(players[19]._id); + expect(game.history[0].deadPlayers[0]._id).to.equal(players[19]._id); + expect(game.players[30].attributes).to.deep.include({ name: "growls", source: "bear-tamer", remainingPhases: 1 }); done(); }); it("๐Ÿ‘ช All can't vote if the idiot who is banned from votes tries anyway (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[21]._id, for: players[0]._id }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("CANT_VOTE"); + expect(res.body.type).to.equal("CANT_VOTE"); done(); }); }); it("๐Ÿ‘ช All vote for the idiot again, which die this time and doesn't delegate his sheriff power because he's an idiot (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[2]._id, for: players[21]._id }] }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.history[0].play.votesResult).to.equals("death"); + expect(game.history[0].play.votesResult).to.equal("death"); expect(game.players[21].isAlive).to.be.false; expect(game.players[21].murdered).to.deep.equals({ by: "all", of: "vote" }); expect(game.players[21].attributes).to.deep.includes({ name: "sheriff", source: "sheriff" }); @@ -3538,13 +3744,13 @@ describe("B - Full game of 28 players with all roles", () => { }); }); it("๐ŸŒ™ Night falls", done => { - expect(game.phase).to.equals("night"); - expect(game.turn).to.equals(10); + expect(game.phase).to.equal("night"); + expect(game.turn).to.equal(10); done(); }); it("๐Ÿ›ก Guard protects himself (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "guard", action: "protect", targets: [{ player: players[2]._id }] }) @@ -3553,26 +3759,26 @@ describe("B - Full game of 28 players with all roles", () => { game = res.body; expect(game.players[2].attributes).to.deep.include({ name: "protected", source: "guard", remainingPhases: 1 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[2]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[2]._id); done(); }); }); - it("๐Ÿบ Werewolves eat the last brother (POST /games/:id/play)", done => { + it("๐Ÿบ Werewolves eat the rusty sword knight (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) - .send({ source: "werewolves", action: "eat", targets: [{ player: players[12]._id }] }) + .send({ source: "werewolves", action: "eat", targets: [{ player: players[29]._id }] }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[12]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[29]._id); done(); }); }); it("๐Ÿช„ Witch skips (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "witch", action: "use-potion", targets: [] }) @@ -3584,33 +3790,39 @@ describe("B - Full game of 28 players with all roles", () => { }); }); it("โ˜€๏ธ Sun is rising", done => { - expect(game.phase).to.equals("day"); - expect(game.players[12].isAlive).to.be.false; + expect(game.phase).to.equal("day"); + expect(game.players[29].isAlive).to.be.false; + expect(game.players[30].attributes).to.deep.include({ name: "growls", source: "bear-tamer", remainingPhases: 1 }); + expect(game.players[0].attributes).to.deep.include({ name: "contaminated", source: "rusty-sword-knight", remainingPhases: 1 }); done(); }); - it("๐Ÿ‘ช All vote for the witch, which joined the werewolf side earlier (POST /games/:id/play)", done => { + it("๐Ÿ‘ช All vote for the last brother (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) - .send({ source: "all", action: "vote", votes: [{ from: players[2]._id, for: players[0]._id }] }) + .send({ source: "all", action: "vote", votes: [{ from: players[2]._id, for: players[12]._id }] }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.history[0].play.votesResult).to.equals("death"); + expect(game.history[0].play.votesResult).to.equal("death"); expect(game.players[0].isAlive).to.be.false; - expect(game.players[0].murdered).to.deep.equals({ by: "all", of: "vote" }); + expect(game.players[0].murdered).to.deep.equals({ by: "rusty-sword-knight", of: "disease" }); + expect(game.players[12].isAlive).to.be.false; + expect(game.players[12].murdered).to.deep.equals({ by: "all", of: "vote" }); done(); }); }); it("๐ŸŒ™ Night falls", done => { - expect(game.phase).to.equals("night"); - expect(game.turn).to.equals(11); + expect(game.phase).to.equal("night"); + expect(game.players[12].isAlive).to.be.false; + expect(game.players[0].isAlive).to.be.false; + expect(game.turn).to.equal(11); done(); }); it("๐Ÿ›ก Guard protects the thief (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "guard", action: "protect", targets: [{ player: players[27]._id }] }) @@ -3619,13 +3831,13 @@ describe("B - Full game of 28 players with all roles", () => { game = res.body; expect(game.players[27].attributes).to.deep.include({ name: "protected", source: "guard", remainingPhases: 1 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[27]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[27]._id); done(); }); }); it("๐Ÿบ Werewolves eat the guard (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[2]._id }] }) @@ -3633,96 +3845,82 @@ describe("B - Full game of 28 players with all roles", () => { expect(res).to.have.status(200); game = res.body; expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[2]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[2]._id); done(); }); }); it("โ˜€๏ธ Sun is rising", done => { - expect(game.phase).to.equals("day"); + expect(game.phase).to.equal("day"); expect(game.players[2].isAlive).to.be.false; + expect(game.players[30].attributes).to.not.deep.include({ name: "growls", source: "bear-tamer", remainingPhases: 1 }); done(); }); it("๐Ÿ‘ช All vote for the thief, which joined the werewolf side by choosing a werewolf card (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[6]._id, for: players[27]._id }] }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.history[0].play.votesResult).to.equals("death"); - expect(game.players[0].isAlive).to.be.false; - expect(game.players[0].murdered).to.deep.equals({ by: "all", of: "vote" }); + expect(game.history[0].play.votesResult).to.equal("death"); + expect(game.players[27].isAlive).to.be.false; + expect(game.players[27].murdered).to.deep.equals({ by: "all", of: "vote" }); done(); }); }); it("๐ŸŽฒ Game is WON by 'villagers'!!", done => { - expect(game.status).to.equals("done"); - expect(game.won.by).to.equals("villagers"); + expect(game.status).to.equal("done"); + expect(game.won.by).to.equal("villagers"); done(); }); it("๐Ÿ” Can't make a play if game's done (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "sheriff", action: "delegate" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NO_MORE_PLAY_ALLOWED"); + expect(res.body.type).to.equal("NO_MORE_PLAY_ALLOWED"); done(); }); }); it("๐ŸŒŸ Can't update game review if `rating` is absent (PATCH /games/:id)", done => { - chai.request(app) + chai.request(server) .patch(`/games/${game._id}`) .set({ Authorization: `Bearer ${token}` }) .send({ review: { comment: "That was ok.." } }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_REQUEST"); + expect(res.body.type).to.equal("BAD_REQUEST"); done(); }); }); it("๐ŸŒŸ Setting game review of 3.5 stars (PATCH /games/:id)", done => { - chai.request(app) + chai.request(server) .patch(`/games/${game._id}`) .set({ Authorization: `Bearer ${token}` }) .send({ review: { rating: 3.5, comment: "That was ok..", dysfunctionFound: true } }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.review.rating).to.equals(3.5); + expect(game.review.rating).to.equal(3.5); expect(game.review.dysfunctionFound).to.be.true; done(); }); }); it("๐ŸŒŸ Can update game review (PATCH /games/:id)", done => { - chai.request(app) + chai.request(server) .patch(`/games/${game._id}`) .set({ Authorization: `Bearer ${token}` }) .send({ review: { rating: 3.5, comment: "That was ok..", dysfunctionFound: true } }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.review.rating).to.equals(3.5); + expect(game.review.rating).to.equal(3.5); expect(game.review.dysfunctionFound).to.be.true; done(); }); }); -}); - -/* - * const players = [ - * { name: "0Dig", role: "witch" }, - * { name: "1Doug", role: "seer" }, - * { name: "2Dag", role: "guard" }, - * { name: "3Dug", role: "raven" }, - * { name: "4Dyg", role: "hunter" }, - * { name: "5Deg", role: "werewolf" }, - * { name: "6Dog", role: "villager" }, - * { name: "7Diig", role: "little-girl" }, - * { name: "8Diig", role: "villager-villager" }, - * { name: "9Dรชg", role: "cupid" }, - * ]; - */ \ No newline at end of file +}); \ No newline at end of file diff --git a/tests/e2e/game/game-creation.test.js b/tests/e2e/game/game-creation.test.js index b88d276..6602b5b 100644 --- a/tests/e2e/game/game-creation.test.js +++ b/tests/e2e/game/game-creation.test.js @@ -12,12 +12,12 @@ const { expect } = chai; const credentials = { email: "test@test.fr", password: "secret" }; const credentials2 = { email: "test@test.frbis", password: "secret" }; const players = [ - { name: "

Dig

", role: "witch" }, - { name: "Doug", role: "seer" }, - { name: "Dag", role: "guard" }, - { name: "Dug", role: "raven" }, - { name: "Dyg", role: "hunter" }, - { name: "Deg", role: "werewolf" }, + { name: "

Dig

", role: "witch", position: 1 }, + { name: "Doug", role: "seer", position: 4 }, + { name: "Dag", role: "guard", position: 3 }, + { name: "Dug", role: "raven", position: 2 }, + { name: "Dyg", role: "hunter", position: 5 }, + { name: "Deg", role: "werewolf", position: 0 }, ]; const playersWithoutWerewolves = [ { name: "Dig", role: "witch" }, @@ -90,13 +90,16 @@ const tooMuchPlayers = [ { name: "40", role: "villager" }, { name: "41", role: "werewolf" }, ]; -let token, token2, game, game2, queryString; +let server, token, token2, game, game2, queryString; describe("A - Game creation", () => { before(done => resetDatabase(done)); + before(done => { + server = app.listen(3000, done); + }); after(done => resetDatabase(done)); it("๐Ÿ‘ค Creates new user (POST /users)", done => { - chai.request(app) + chai.request(server) .post("/users") .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -106,7 +109,7 @@ describe("A - Game creation", () => { }); }); it("๐Ÿ”‘ Logs in successfully (POST /users/login)", done => { - chai.request(app) + chai.request(server) .post(`/users/login`) .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -117,7 +120,7 @@ describe("A - Game creation", () => { }); }); it("๐Ÿ‘ค Creates another user (POST /users)", done => { - chai.request(app) + chai.request(server) .post("/users") .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials2) @@ -127,7 +130,7 @@ describe("A - Game creation", () => { }); }); it("๐Ÿ”‘ Logs in successfully for second user (POST /users/login)", done => { - chai.request(app) + chai.request(server) .post(`/users/login`) .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials2) @@ -138,7 +141,7 @@ describe("A - Game creation", () => { }); }); it("๐Ÿ” Can't create game without JWT auth (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .end((err, res) => { expect(res).to.have.status(401); @@ -146,245 +149,317 @@ describe("A - Game creation", () => { }); }); it("๐Ÿคผ Can't create game with less than 4 players (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players: [{ name: "Doug", role: "witch" }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_REQUEST"); + expect(res.body.type).to.equal("BAD_REQUEST"); done(); }); }); it("๐Ÿคผ Can't create game with more than 40 players (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players: tooMuchPlayers }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_REQUEST"); + expect(res.body.type).to.equal("BAD_REQUEST"); done(); }); }); it("๐Ÿบ Can't create game without werewolves (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players: playersWithoutWerewolves }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NO_WEREWOLF_IN_GAME_COMPOSITION"); + expect(res.body.type).to.equal("NO_WEREWOLF_IN_GAME_COMPOSITION"); done(); }); }); it("๐Ÿ‘ช Can't create game without villagers (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players: playersWithoutVillagers }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NO_VILLAGER_IN_GAME_COMPOSITION"); + expect(res.body.type).to.equal("NO_VILLAGER_IN_GAME_COMPOSITION"); done(); }); }); it("๐Ÿ‘ช Can't create game with no unique player names (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players: [...players, { name: "Doug", role: "werewolf" }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("PLAYERS_NAME_NOT_UNIQUE"); + expect(res.body.type).to.equal("PLAYERS_NAME_NOT_UNIQUE"); done(); }); }); it("๐Ÿ‘ช Can't create game a player with a too long name (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players: [...players, { name: "IAmLaSuperKouisteMoumouneDig!!!", role: "werewolf" }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_REQUEST"); + expect(res.body.type).to.equal("BAD_REQUEST"); + done(); + }); + }); + it("๐Ÿ‘ช Can't create game with one player without position and others with a position (POST /games)", done => { + chai.request(server) + .post("/games") + .set({ Authorization: `Bearer ${token}` }) + .send({ players: [...players, { name: "Dล“g", role: "werewolf" }] }) + .end((err, res) => { + expect(res).to.have.status(400); + expect(res.body.type).to.equal("ALL_PLAYERS_POSITION_NOT_SET"); + done(); + }); + }); + it("๐Ÿ‘ช Can't create game with two players with the same position (POST /games)", done => { + chai.request(server) + .post("/games") + .set({ Authorization: `Bearer ${token}` }) + .send({ players: [...players, { name: "Dล“g", role: "werewolf", position: 5 }] }) + .end((err, res) => { + expect(res).to.have.status(400); + expect(res.body.type).to.equal("PLAYERS_POSITION_NOT_UNIQUE"); + done(); + }); + }); + it("๐Ÿ‘ช Can't create game with one player with a position that exceeds the maximum position (POST /games)", done => { + chai.request(server) + .post("/games") + .set({ Authorization: `Bearer ${token}` }) + .send({ players: [...players, { name: "Dล“g", role: "werewolf", position: 7 }] }) + .end((err, res) => { + expect(res).to.have.status(400); + expect(res.body.type).to.equal("PLAYER_POSITION_TOO_HIGH"); done(); }); }); it("๐Ÿ›ก Can't create game with two guards (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) - .send({ players: [...players, { name: "Dล“gd", role: "guard" }] }) + .send({ players: [...players, { name: "Dล“g", role: "guard", position: 6 }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("TOO_MUCH_PLAYERS_WITH_ROLE"); + expect(res.body.type).to.equal("TOO_MUCH_PLAYERS_WITH_ROLE"); done(); }); }); it("๐Ÿ‘ญ Can't create game a with only one sister (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players: playersWithOnlyOneSister }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("MIN_PLAYERS_NOT_REACHED_FOR_ROLE"); + expect(res.body.type).to.equal("MIN_PLAYERS_NOT_REACHED_FOR_ROLE"); done(); }); }); it("๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ฆ Can't create game a with only two brothers (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players: playersWithOnlyTwoBrothers }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("MIN_PLAYERS_NOT_REACHED_FOR_ROLE"); + expect(res.body.type).to.equal("MIN_PLAYERS_NOT_REACHED_FOR_ROLE"); done(); }); }); it("๐Ÿƒ Can't create game with additional cards when there is no thief in game (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players, additionalCards: [{ role: "seer", for: "thief" }, { role: "witch", for: "thief" }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("ADDITIONAL_CARDS_NOT_ALLOWED"); + expect(res.body.type).to.equal("ADDITIONAL_CARDS_NOT_ALLOWED"); done(); }); }); it("๐Ÿƒ Can't create game with additional cards when one additional role card is forbidden for thief (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) - .send({ players: [...players, { name: "Chipper", role: "thief" }], additionalCards: [{ role: "two-sisters", for: "thief" }, { role: "witch", for: "thief" }] }) + .send({ players: [...players, { name: "Chipper", role: "thief", position: 6 }], additionalCards: [{ role: "two-sisters", for: "thief" }, { role: "witch", for: "thief" }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("FORBIDDEN_ADDITIONAL_CARD_ROLE_FOR_THIEF"); + expect(res.body.type).to.equal("FORBIDDEN_ADDITIONAL_CARD_ROLE_FOR_THIEF"); done(); }); }); it("๐Ÿƒ Can't create game with additional cards when one additional role card makes exceed the max in game for this role (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) - .send({ players: [...players, { name: "Chipper", role: "thief" }], additionalCards: [{ role: "seer", for: "thief" }, { role: "witch", for: "thief" }] }) + .send({ players: [...players, { name: "Chipper", role: "thief", position: 6 }], additionalCards: [{ role: "seer", for: "thief" }, { role: "witch", for: "thief" }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("TOO_MUCH_PLAYERS_WITH_ROLE"); + expect(res.body.type).to.equal("TOO_MUCH_PLAYERS_WITH_ROLE"); done(); }); }); it("๐Ÿƒ Can't create game with additional cards when one additional role card makes exceed the max in game for this role (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ - players: [...players, { name: "Chipper", role: "thief" }], additionalCards: [ + players: [...players, { name: "Chipper", role: "thief", position: 6 }], additionalCards: [ { role: "wild-child", for: "thief" }, { role: "wild-child", for: "thief" }, ], }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("TOO_MUCH_PLAYERS_WITH_ROLE"); + expect(res.body.type).to.equal("TOO_MUCH_PLAYERS_WITH_ROLE"); + done(); + }); + }); + it("๐Ÿƒ Can't create game with too much additional cards for thief (POST /games)", done => { + chai.request(server) + .post("/games") + .set({ Authorization: `Bearer ${token}` }) + .send({ players: [...players, { name: "Chipper", role: "thief", position: 6 }], additionalCards: [{ role: "werewolf", for: "thief" }] }) + .end((err, res) => { + expect(res).to.have.status(400); + expect(res.body.type).to.equal("THIEF_ADDITIONAL_CARDS_COUNT_NOT_RESPECTED"); + done(); + }); + }); + it("๐Ÿƒ Can't create game with too much additional cards for thief (POST /games)", done => { + chai.request(server) + .post("/games") + .set({ Authorization: `Bearer ${token}` }) + .send({ + players: [...players, { name: "Chipper", role: "thief", position: 6 }], additionalCards: [ + { role: "werewolf", for: "thief" }, + { role: "werewolf", for: "thief" }, + { role: "werewolf", for: "thief" }, + ], + }) + .end((err, res) => { + expect(res).to.have.status(400); + expect(res.body.type).to.equal("THIEF_ADDITIONAL_CARDS_COUNT_NOT_RESPECTED"); done(); }); }); it("๐Ÿƒ Can't create game without additional cards if thief is in the game (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) - .send({ players: [...players, { name: "Chipper", role: "thief" }] }) + .send({ players: [...players, { name: "Chipper", role: "thief", position: 6 }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NEED_ADDITIONAL_CARDS_FOR_THIEF"); + expect(res.body.type).to.equal("NEED_ADDITIONAL_CARDS_FOR_THIEF"); done(); }); }); it("๐ŸŽฒ User1 creates game with JWT auth (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.status).to.equals("playing"); - expect(game.turn).to.equals(1); - expect(game.phase).to.equals("night"); - expect(game.tick).to.equals(1); + expect(game.status).to.equal("playing"); + expect(game.turn).to.equal(1); + expect(game.phase).to.equal("night"); + expect(game.tick).to.equal(1); expect(game.options.roles.sheriff.hasDoubledVote).to.be.true; expect(game.options.roles.seer.isTalkative).to.be.true; - expect(game.options.roles.twoSisters.wakingUpInterval).to.equals(2); - expect(game.options.roles.threeBrothers.wakingUpInterval).to.equals(2); + expect(game.options.roles.twoSisters.wakingUpInterval).to.equal(2); + expect(game.options.roles.threeBrothers.wakingUpInterval).to.equal(2); expect(game.waiting[0]).to.deep.equals({ for: "all", to: "elect-sheriff" }); expect(game.history).to.deep.equals([]); expect(Array.isArray(game.players)).to.be.true; - expect(game.players[0].name).to.equals("Dig"); + expect(game.players[0].name).to.equal("Deg"); + expect(game.players[0].position).to.equal(0); + expect(game.players[1].name).to.equal("Dig"); + expect(game.players[1].position).to.equal(1); + expect(game.players[2].name).to.equal("Dug"); + expect(game.players[2].position).to.equal(2); + expect(game.players[3].name).to.equal("Dag"); + expect(game.players[3].position).to.equal(3); + expect(game.players[4].name).to.equal("Doug"); + expect(game.players[4].position).to.equal(4); + expect(game.players[5].name).to.equal("Dyg"); + expect(game.players[5].position).to.equal(5); done(); }); }); it("๐ŸŽฒ User2 creates game with JWT auth (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token2}` }) .send({ players }) .end((err, res) => { expect(res).to.have.status(200); game2 = res.body; - expect(game2.status).to.equals("playing"); + expect(game2.status).to.equal("playing"); done(); }); }); it("๐ŸŽฒ Can't create another game if one is already playing (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("GAME_MASTER_HAS_ON_GOING_GAMES"); + expect(res.body.type).to.equal("GAME_MASTER_HAS_ON_GOING_GAMES"); done(); }); }); it("๐ŸŽฒ Cancels game (PATCH /games/:id)", done => { - chai.request(app) + chai.request(server) .patch(`/games/${game._id}`) .set({ Authorization: `Bearer ${token}` }) .send({ status: "canceled" }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.status).to.equals("canceled"); + expect(game.status).to.equal("canceled"); done(); }); }); it("๐Ÿ” Can't make a play if game's canceled (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "elect-sheriff" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("NO_MORE_PLAY_ALLOWED"); + expect(res.body.type).to.equal("NO_MORE_PLAY_ALLOWED"); done(); }); }); it("๐Ÿ” Game can't be reset if status is 'canceled' (PATCH /games/:id/reset)", done => { - chai.request(app) + chai.request(server) .patch(`/games/${game._id}/reset`) .set({ Authorization: `Bearer ${token}` }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("CANT_BE_RESET"); + expect(res.body.type).to.equal("CANT_BE_RESET"); done(); }); }); it("๐ŸŽฒ Creates another game because all others are cancelled (POST /games)", done => { - chai.request(app) + chai.request(server) .post(`/games`) .set({ Authorization: `Bearer ${token}` }) .send({ players }) @@ -395,7 +470,7 @@ describe("A - Game creation", () => { }); }); it("๐Ÿ” Can't get games without authentication (GET /games)", done => { - chai.request(app) + chai.request(server) .get(`/games`) .end((err, res) => { expect(res).to.have.status(401); @@ -403,20 +478,20 @@ describe("A - Game creation", () => { }); }); it("๐ŸŽฒ User1 gets his games with JWT auth (GET /games)", done => { - chai.request(app) + chai.request(server) .get("/games") .set({ Authorization: `Bearer ${token}` }) .end((err, res) => { expect(res).to.have.status(200); const games = res.body; expect(Array.isArray(games)).to.be.true; - expect(games.length).to.equals(2); + expect(games.length).to.equal(2); done(); }); }); it(`๐ŸŽฒ User1 gets his games with playing status with JWT auth (GET /games?${queryString})`, done => { queryString = stringify({ status: "playing" }); - chai.request(app) + chai.request(server) .get(`/games?${queryString}`) .set({ Authorization: `Bearer ${token}` }) .send({ players }) @@ -424,13 +499,13 @@ describe("A - Game creation", () => { expect(res).to.have.status(200); const games = res.body; expect(Array.isArray(games)).to.be.true; - expect(games.length).to.equals(1); + expect(games.length).to.equal(1); done(); }); }); it(`๐ŸŽฒ User2 gets his games with canceled status with JWT auth (GET /games?${queryString})`, done => { queryString = stringify({ status: "canceled" }); - chai.request(app) + chai.request(server) .get(`/games?${queryString}`) .set({ Authorization: `Bearer ${token2}` }) .send({ players }) @@ -438,12 +513,12 @@ describe("A - Game creation", () => { expect(res).to.have.status(200); const games = res.body; expect(Array.isArray(games)).to.be.true; - expect(games.length).to.equals(0); + expect(games.length).to.equal(0); done(); }); }); it(`๐ŸŽฒ Gets all games with basic auth (GET /games)`, done => { - chai.request(app) + chai.request(server) .get(`/games`) .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send({ players }) @@ -451,45 +526,45 @@ describe("A - Game creation", () => { expect(res).to.have.status(200); const games = res.body; expect(Array.isArray(games)).to.be.true; - expect(games.length).to.equals(3); + expect(games.length).to.equal(3); done(); }); }); it(`๐ŸŽฒ Gets a game with basic auth (GET /games)`, done => { - chai.request(app) + chai.request(server) .get(`/games/${game._id}`) .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game._id).to.equals(game._id); + expect(game._id).to.equal(game._id); done(); }); }); it(`๐ŸŽฒ User1 gets his last game with JWT auth (GET /games/:id)`, done => { - chai.request(app) + chai.request(server) .get(`/games/${game._id}`) .set({ Authorization: `Bearer ${token}` }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game._id).to.equals(game._id); + expect(game._id).to.equal(game._id); done(); }); }); it(`๐ŸŽฒ User1 can't get a game created by user2 with JWT auth (GET /games/:id)`, done => { - chai.request(app) + chai.request(server) .get(`/games/${game2._id}`) .set({ Authorization: `Bearer ${token}` }) .end((err, res) => { expect(res).to.have.status(401); - expect(res.body.type).to.equals("GAME_DOESNT_BELONG_TO_USER"); + expect(res.body.type).to.equal("GAME_DOESNT_BELONG_TO_USER"); done(); }); }); it("๐Ÿ‘ค Get games with only _id and waiting in response (GET /games?fields=_id,waiting)", done => { queryString = stringify({ fields: "_id,waiting" }); - chai.request(app) + chai.request(server) .get(`/games?${queryString}`) .set({ Authorization: `Bearer ${token}` }) .end((err, res) => { diff --git a/tests/e2e/game/game-options.test.js b/tests/e2e/game/game-options.test.js index 64cc877..986d48a 100644 --- a/tests/e2e/game/game-options.test.js +++ b/tests/e2e/game/game-options.test.js @@ -10,22 +10,31 @@ const { expect } = chai; const credentials = { email: "test@test.fr", password: "secret" }; const originalPlayers = [ - { name: "Dag", role: "werewolf" }, { name: "Dig", role: "two-sisters" }, { name: "Deg", role: "two-sisters" }, { name: "Dog", role: "three-brothers" }, { name: "Dug", role: "three-brothers" }, { name: "Dyg", role: "three-brothers" }, + { name: "Dag", role: "vile-father-of-wolves" }, { name: "Dฯ€g", role: "villager" }, { name: "Dล“g", role: "raven" }, + { name: "Dโ€“g", role: "guard" }, + { name: "Dรg", role: "little-girl" }, + { name: "Dลธg", role: "fox" }, + { name: "Dรธg", role: "bear-tamer" }, + { name: "Dโ€žg", role: "stuttering-judge" }, + { name: "Dโ€ฐg", role: "thief" }, ]; -let token, game, players; +let server, token, game, players; describe("K - Game options", () => { before(done => resetDatabase(done)); + before(done => { + server = app.listen(3000, done); + }); after(done => resetDatabase(done)); it("๐Ÿ‘ค Creates new user (POST /users)", done => { - chai.request(app) + chai.request(server) .post("/users") .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -35,7 +44,7 @@ describe("K - Game options", () => { }); }); it("๐Ÿ”‘ Logs in successfully (POST /users/login)", done => { - chai.request(app) + chai.request(server) .post(`/users/login`) .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -45,36 +54,57 @@ describe("K - Game options", () => { done(); }); }); - it("๐ŸŽฒ Creates game with brothers and sisters waking up every night, sheriff has regular vote, seer is talkative and raven penalty to 3 with JWT auth (POST /games)", done => { - chai.request(app) + it("๐ŸŽฒ Creates game with brothers and sisters waking up every night, sheriff has regular vote, seer is talkative and raven penalty to 3 and other options with JWT auth (POST /games)", done => { + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players: originalPlayers, options: { + repartition: { isHidden: true }, roles: { + areRevealedOnDeath: false, sheriff: { hasDoubledVote: false }, - seer: { isTalkative: false }, + seer: { isTalkative: false, canSeeRoles: false }, + guard: { canProtectTwice: true }, twoSisters: { wakingUpInterval: 1 }, threeBrothers: { wakingUpInterval: 1 }, + fox: { isPowerlessIfMissesWerewolf: false }, + bearTamer: { doesGrowlIfInfected: false }, + stutteringJudge: { voteRequestsCount: 2 }, + wildChild: { isTransformationRevealed: true }, + dogWolf: { isChosenSideRevealed: true }, + thief: { mustChooseBetweenWerewolves: false, additionalCardsCount: 3 }, raven: { markPenalty: 3 }, }, }, + additionalCards: [{ role: "werewolf", for: "thief" }, { role: "werewolf", for: "thief" }, { role: "werewolf", for: "thief" }], }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; + expect(game.options.repartition.isHidden).to.be.true; + expect(game.options.roles.areRevealedOnDeath).to.be.false; expect(game.options.roles.sheriff.hasDoubledVote).to.be.false; expect(game.options.roles.seer.isTalkative).to.be.false; - expect(game.options.roles.twoSisters.wakingUpInterval).to.equals(1); - expect(game.options.roles.threeBrothers.wakingUpInterval).to.equals(1); - expect(game.options.roles.raven.markPenalty).to.equals(3); + expect(game.options.roles.seer.canSeeRoles).to.be.false; + expect(game.options.roles.guard.canProtectTwice).to.be.true; + expect(game.options.roles.twoSisters.wakingUpInterval).to.equal(1); + expect(game.options.roles.threeBrothers.wakingUpInterval).to.equal(1); + expect(game.options.roles.fox.isPowerlessIfMissesWerewolf).to.be.false; + expect(game.options.roles.bearTamer.doesGrowlIfInfected).to.be.false; + expect(game.options.roles.stutteringJudge.voteRequestsCount).to.equal(2); + expect(game.options.roles.wildChild.isTransformationRevealed).to.be.true; + expect(game.options.roles.dogWolf.isChosenSideRevealed).to.be.true; + expect(game.options.roles.thief.mustChooseBetweenWerewolves).to.be.false; + expect(game.options.roles.thief.additionalCardsCount).to.be.equal(3); + expect(game.options.roles.raven.markPenalty).to.equal(3); done(); }); }); it("๐Ÿ‘ช All elect the werewolf as the sheriff (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -89,8 +119,55 @@ describe("K - Game options", () => { done(); }); }); + it("๐Ÿฆน๏ธ Thief can skip his turn thanks to options (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "thief", action: "choose-card" }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + expect(game.players[13].role.current).to.equal("thief"); + expect(game.players[13].side.current).to.equal("villagers"); + expect(game.history[0].play.card).to.not.exist; + done(); + }); + }); + it("๐ŸฆŠ Fox sniffs no werewolf but is not powerless thanks to option (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "fox", action: "sniff", targets: [{ player: players[2]._id }] }) + .end((err, res) => { + game = res.body; + expect(res).to.have.status(200); + expect(game.history[0].play.targets).to.exist; + expect(game.history[0].play.targets).to.be.lengthOf(3); + expect(game.history[0].play.targets[0].player._id).to.equal(players[3]._id); + expect(game.history[0].play.targets[1].player._id).to.equal(players[2]._id); + expect(game.history[0].play.targets[2].player._id).to.equal(players[1]._id); + expect(game.history[0].play.targets[0].player.side.current).to.equal("villagers"); + expect(game.history[0].play.targets[1].player.side.current).to.equal("villagers"); + expect(game.history[0].play.targets[2].player.side.current).to.equal("villagers"); + expect(game.players[10].attributes).to.not.exist; + done(); + }); + }); + it("โš–๏ธ Stuttering judge chooses sign (POST /games/:id/play)", done => { + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "stuttering-judge", action: "choose-sign" }) + .end((err, res) => { + game = res.body; + expect(res).to.have.status(200); + done(); + }); + }); it("๐Ÿ‘ญ The two sisters meet each other (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "two-sisters", action: "meet-each-other" }) @@ -101,7 +178,7 @@ describe("K - Game options", () => { }); }); it("๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ฆ The three brothers meet each other (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "three-brothers", action: "meet-each-other" }) @@ -113,48 +190,65 @@ describe("K - Game options", () => { }); it("๐Ÿชถ Raven marks the werewolf (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) - .send({ source: "raven", action: "mark", targets: [{ player: players[0]._id }] }) + .send({ source: "raven", action: "mark", targets: [{ player: players[5]._id }] }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.players[0].attributes).to.deep.include({ name: "raven-marked", source: "raven", remainingPhases: 2 }); + expect(game.players[5].attributes).to.deep.include({ name: "raven-marked", source: "raven", remainingPhases: 2 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[0]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[5]._id); done(); }); }); - it("๐Ÿบ Werewolf eats the villager (POST /games/:id/play)", done => { + it("๐Ÿ›ก Guard protects himself (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) - .send({ source: "werewolves", action: "eat", targets: [{ player: players[6]._id }] }) + .send({ source: "guard", action: "protect", targets: [{ player: players[8]._id }] }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; + expect(game.players[8].attributes).to.deep.include({ name: "protected", source: "guard", remainingPhases: 1 }); + expect(game.history[0].play.targets).to.exist; + expect(game.history[0].play.targets[0].player._id).to.equal(players[8]._id); done(); }); }); - it("โ˜€๏ธ Sun is rising", done => { - expect(game.phase).to.equals("day"); + it("๐Ÿบ Werewolf infects the bear tamer (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "werewolves", action: "eat", targets: [{ player: players[11]._id, isInfected: true }] }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + expect(game.players[11].side.current).to.equal("werewolves"); + done(); + }); + }); + it("โ˜€๏ธ Sun is rising, bear tamer doesn't growl even if he is infected because of option", done => { + expect(game.phase).to.equal("day"); + expect(game.players[11].attributes).to.not.exist; done(); }); it("๐Ÿ‘ช Werewolf (sheriff) and three other players votes for one brother and the brother votes for the werewolf (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [ - { from: players[0]._id, for: players[5]._id }, - { from: players[1]._id, for: players[5]._id }, - { from: players[2]._id, for: players[5]._id }, - { from: players[3]._id, for: players[5]._id }, - { from: players[5]._id, for: players[0]._id }, - ], + { from: players[0]._id, for: players[4]._id }, + { from: players[1]._id, for: players[4]._id }, + { from: players[2]._id, for: players[4]._id }, + { from: players[3]._id, for: players[4]._id }, + { from: players[4]._id, for: players[5]._id }, + ], doesJudgeRequestAnotherVote: true, }) .end((err, res) => { expect(res).to.have.status(200); @@ -169,20 +263,48 @@ describe("K - Game options", () => { }); it("๐ŸŽ– Sheriff settles votes by choosing villager (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) - .send({ source: "sheriff", action: "settle-votes", targets: [{ player: players[5]._id }] }) + .send({ source: "sheriff", action: "settle-votes", targets: [{ player: players[4]._id }] }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.players[5].isAlive).to.be.false; + expect(game.players[4].isAlive).to.be.false; + done(); + }); + }); + it("๐Ÿ‘ช All vote for the fox and stuttering judge request another vote again (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "all", action: "vote", votes: [{ from: players[0]._id, for: players[10]._id }], doesJudgeRequestAnotherVote: true }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + expect(game.players[10].isAlive).to.be.false; + expect(game.players[10].murdered).to.deep.equals({ by: "all", of: "vote" }); + done(); + }); + }); + it("๐Ÿ‘ช All vote for the stuttering judge (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "all", action: "vote", votes: [{ from: players[0]._id, for: players[12]._id }] }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + expect(game.players[12].isAlive).to.be.false; + expect(game.players[12].murdered).to.deep.equals({ by: "all", of: "vote" }); done(); }); }); it("๐ŸŒ™ Night falls", done => { - expect(game.phase).to.equals("night"); - expect(game.turn).to.equals(2); + expect(game.phase).to.equal("night"); + expect(game.turn).to.equal(2); done(); }); it("๐ŸŽฒ Game is waiting for 'two-sisters' to 'meet-each-other' because they wake up every night according to game options", done => { @@ -190,7 +312,7 @@ describe("K - Game options", () => { done(); }); it("๐Ÿ‘ญ The two sisters meet each other (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "two-sisters", action: "meet-each-other" }) @@ -205,7 +327,7 @@ describe("K - Game options", () => { done(); }); it("๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ฆ The three brothers meet each other (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "three-brothers", action: "meet-each-other" }) @@ -217,7 +339,7 @@ describe("K - Game options", () => { }); it("๐Ÿชถ Raven skips (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "raven", action: "mark" }) @@ -227,9 +349,24 @@ describe("K - Game options", () => { done(); }); }); + it("๐Ÿ›ก Guard protects himself again because option allows him (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "guard", action: "protect", targets: [{ player: players[8]._id }] }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + expect(game.players[8].attributes).to.deep.include({ name: "protected", source: "guard", remainingPhases: 1 }); + expect(game.history[0].play.targets).to.exist; + expect(game.history[0].play.targets[0].player._id).to.equal(players[8]._id); + done(); + }); + }); it("๐Ÿบ Werewolf eats one of the two sisters (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[1]._id }] }) @@ -240,25 +377,25 @@ describe("K - Game options", () => { }); }); it("โ˜€๏ธ Sun is rising", done => { - expect(game.phase).to.equals("day"); + expect(game.phase).to.equal("day"); done(); }); it("๐Ÿ‘ช Werewolf (sheriff) votes for one of the two remaining brothers (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) - .send({ source: "all", action: "vote", votes: [{ from: players[0]._id, for: players[4]._id }] }) + .send({ source: "all", action: "vote", votes: [{ from: players[0]._id, for: players[3]._id }] }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.players[4].isAlive).to.be.false; + expect(game.players[3].isAlive).to.be.false; done(); }); }); it("๐ŸŒ™ Night falls", done => { - expect(game.phase).to.equals("night"); - expect(game.turn).to.equals(3); + expect(game.phase).to.equal("night"); + expect(game.turn).to.equal(3); done(); }); it("๐ŸŽฒ Game is waiting for 'raven' to 'mark' because sisters and brothers are all alone", done => { @@ -266,53 +403,91 @@ describe("K - Game options", () => { done(); }); it("๐ŸŽฒ Cancels game (PATCH /games/:id)", done => { - chai.request(app) + chai.request(server) .patch(`/games/${game._id}`) .set({ Authorization: `Bearer ${token}` }) .send({ status: "canceled" }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.status).to.equals("canceled"); + expect(game.status).to.equal("canceled"); done(); }); }); it("๐ŸŽฒ Creates game with brothers and sisters never waking up after first night (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ - players: originalPlayers, + players: [ + ...originalPlayers, + { name: "Dโˆ†g", role: "big-bad-wolf" }, + { name: "Dรฅg", role: "white-werewolf" }, + { name: "Dยชg", role: "pied-piper" }, + ], options: { - brothersWakingUpInterval: 0, - sistersWakingUpInterval: 0, + roles: { + sheriff: { electedAt: { turn: 1, phase: "day" } }, + bigBadWolf: { isPowerlessIfWerewolfDies: false }, + whiteWerewolf: { wakingUpInterval: 1 }, + twoSisters: { wakingUpInterval: 0 }, + threeBrothers: { wakingUpInterval: 0 }, + thief: { mustChooseBetweenWerewolves: false }, + piedPiper: { charmedPeopleCountPerNight: 1, isPowerlessIfInfected: false }, + }, }, + additionalCards: [{ role: "werewolf", for: "thief" }, { role: "werewolf", for: "thief" }], }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; + expect(game.options.roles.bigBadWolf.isPowerlessIfWerewolfDies).to.be.false; + expect(game.options.roles.whiteWerewolf.wakingUpInterval).to.be.equal(1); + expect(game.options.roles.piedPiper.charmedPeopleCountPerNight).to.equal(1); + expect(game.options.roles.piedPiper.isPowerlessIfInfected).to.be.false; done(); }); }); - it("๐Ÿ‘ช All elect the werewolf as the sheriff (POST /games/:id/play)", done => { + it("๐Ÿฆน๏ธ Thief can skip his turn thanks to options (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) - .send({ - source: "all", action: "elect-sheriff", votes: [ - { from: players[2]._id, for: players[0]._id }, - { from: players[3]._id, for: players[0]._id }, - ], - }) + .send({ source: "thief", action: "choose-card" }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; + expect(game.players[13].role.current).to.equal("thief"); + expect(game.players[13].side.current).to.equal("villagers"); + expect(game.history[0].play.card).to.not.exist; + done(); + }); + }); + it("๐ŸฆŠ Fox skips (POST /games/:id/play)", done => { + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "fox", action: "sniff" }) + .end((err, res) => { + game = res.body; + expect(res).to.have.status(200); + expect(game.history[0].play.targets).to.not.exist; + done(); + }); + }); + it("โš–๏ธ Stuttering judge chooses sign (POST /games/:id/play)", done => { + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "stuttering-judge", action: "choose-sign" }) + .end((err, res) => { + game = res.body; + expect(res).to.have.status(200); done(); }); }); it("๐Ÿ‘ญ The two sisters meet each other (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "two-sisters", action: "meet-each-other" }) @@ -323,7 +498,7 @@ describe("K - Game options", () => { }); }); it("๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ฆ The three brothers meet each other (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "three-brothers", action: "meet-each-other" }) @@ -335,7 +510,7 @@ describe("K - Game options", () => { }); it("๐Ÿชถ Raven skips (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "raven", action: "mark" }) @@ -345,12 +520,78 @@ describe("K - Game options", () => { done(); }); }); - it("๐Ÿบ Werewolf eats the villager (POST /games/:id/play)", done => { + it("๐Ÿ›ก Guard protects himself again because option allows him (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "guard", action: "protect", targets: [{ player: players[8]._id }] }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + expect(game.players[8].attributes).to.deep.include({ name: "protected", source: "guard", remainingPhases: 1 }); + expect(game.history[0].play.targets).to.exist; + expect(game.history[0].play.targets[0].player._id).to.equal(players[8]._id); + done(); + }); + }); + it("๐Ÿบ Werewolf infects the pied piper (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) - .send({ source: "werewolves", action: "eat", targets: [{ player: players[6]._id }] }) + .send({ source: "werewolves", action: "eat", targets: [{ player: players[16]._id, isInfected: true }] }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + expect(game.players[16].role.current).to.equal("pied-piper"); + expect(game.players[16].side.current).to.equal("werewolves"); + done(); + }); + }); + it("๐Ÿบ White werewolf skips (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "white-werewolf", action: "eat" }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + done(); + }); + }); + it("๐Ÿบ Big bad wolf eats the third brother (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "big-bad-wolf", action: "eat", targets: [{ player: players[4]._id }] }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + expect(game.history[0].play.targets).to.exist; + expect(game.history[0].play.targets[0].player._id).to.equal(players[4]._id); + done(); + }); + }); + it("๐Ÿ“ฃ Pied piper charms one brother (POST /games/:id/play)", done => { + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "pied-piper", action: "charm", targets: [{ player: players[2]._id }] }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + expect(game.players[2].attributes).to.deep.include({ name: "charmed", source: "pied-piper" }); + done(); + }); + }); + it("๐Ÿ•บ๏ธ Charmed players meet each other (POST /games/:id/play)", done => { + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "charmed", action: "meet-each-other" }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; @@ -358,62 +599,150 @@ describe("K - Game options", () => { }); }); it("โ˜€๏ธ Sun is rising", done => { - expect(game.phase).to.equals("day"); + expect(game.phase).to.equal("day"); done(); }); - it("๐Ÿ‘ช Werewolf (sheriff) votes for one of the two remaining brothers (POST /games/:id/play)", done => { + it("๐Ÿ‘ช All elect the big bad wolf as the sheriff on first day because of options (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) - .send({ source: "all", action: "vote", votes: [{ from: players[0]._id, for: players[4]._id }] }) + .send({ + source: "all", action: "elect-sheriff", votes: [ + { from: players[2]._id, for: players[14]._id }, + { from: players[3]._id, for: players[14]._id }, + ], + }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.players[4].isAlive).to.be.false; + done(); + }); + }); + it("๐Ÿ‘ช All vote for the vile father of wolves (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "all", action: "vote", votes: [{ from: players[0]._id, for: players[5]._id }] }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + expect(game.players[5].isAlive).to.be.false; done(); }); }); it("๐ŸŒ™ Night falls", done => { - expect(game.phase).to.equals("night"); - expect(game.turn).to.equals(2); + expect(game.phase).to.equal("night"); + expect(game.turn).to.equal(2); done(); }); - it("๐ŸŽฒ Game is waiting for 'raven' to 'mark' because brothers and sisters are never waking up again according to game options", done => { - expect(game.waiting[0]).to.deep.equals({ for: "raven", to: "mark" }); - done(); + it("๐ŸฆŠ Fox skips (POST /games/:id/play)", done => { + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "fox", action: "sniff" }) + .end((err, res) => { + game = res.body; + expect(res).to.have.status(200); + expect(game.history[0].play.targets).to.not.exist; + done(); + }); + }); + it("๐Ÿชถ Raven skips (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "raven", action: "mark" }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + done(); + }); + }); + it("๐Ÿ›ก Guard protects the little girl (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "guard", action: "protect", targets: [{ player: players[9]._id }] }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + expect(game.players[9].attributes).to.deep.include({ name: "protected", source: "guard", remainingPhases: 1 }); + expect(game.history[0].play.targets).to.exist; + expect(game.history[0].play.targets[0].player._id).to.equal(players[9]._id); + done(); + }); + }); + it("๐Ÿบ Werewolf eats the sister (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "werewolves", action: "eat", targets: [{ player: players[0]._id }] }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + done(); + }); + }); + it("๐Ÿบ White werewolf skips because he is called every night thanks to options (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "white-werewolf", action: "eat", targets: [] }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + done(); + }); + }); + it("๐Ÿบ Big bad wolf eats the second sister even if one werewolf is dead thanks to options (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "big-bad-wolf", action: "eat", targets: [{ player: players[1]._id }] }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + expect(game.history[0].play.targets).to.exist; + expect(game.history[0].play.targets[0].player._id).to.equal(players[1]._id); + done(); + }); }); it("๐ŸŽฒ Cancels game (PATCH /games/:id)", done => { - chai.request(app) + chai.request(server) .patch(`/games/${game._id}`) .set({ Authorization: `Bearer ${token}` }) .send({ status: "canceled" }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.status).to.equals("canceled"); + expect(game.status).to.equal("canceled"); done(); }); }); it("๐ŸŽฒ Creates game with disabled sheriff option, brothers and sisters waking up only the first night, raven penalty to 1 and little girl is protected by guard with JWT auth (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ - players: [ - ...originalPlayers, - { name: "Little girl", role: "little-girl" }, - { name: "Guard", role: "guard" }, - ], + players: originalPlayers, options: { roles: { sheriff: { isEnabled: false }, littleGirl: { isProtectedByGuard: true }, twoSisters: { wakingUpInterval: 0 }, threeBrothers: { wakingUpInterval: 0 }, + thief: { mustChooseBetweenWerewolves: false }, raven: { markPenalty: 1 }, }, }, + additionalCards: [{ role: "werewolf", for: "thief" }, { role: "werewolf", for: "thief" }], }) .end((err, res) => { expect(res).to.have.status(200); @@ -422,8 +751,46 @@ describe("K - Game options", () => { done(); }); }); - it("๐Ÿ‘ญ The two sisters meet each other already because there is no sheriff to elect (POST /games/:id/play)", done => { - chai.request(app) + it("๐Ÿฆน๏ธ Thief can skip his turn thanks to options (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "thief", action: "choose-card" }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + expect(game.players[13].role.current).to.equal("thief"); + expect(game.players[13].side.current).to.equal("villagers"); + expect(game.history[0].play.card).to.not.exist; + done(); + }); + }); + it("๐ŸฆŠ Fox skips (POST /games/:id/play)", done => { + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "fox", action: "sniff" }) + .end((err, res) => { + game = res.body; + expect(res).to.have.status(200); + expect(game.history[0].play.targets).to.not.exist; + done(); + }); + }); + it("โš–๏ธ Stuttering judge chooses sign (POST /games/:id/play)", done => { + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "stuttering-judge", action: "choose-sign" }) + .end((err, res) => { + game = res.body; + expect(res).to.have.status(200); + done(); + }); + }); + it("๐Ÿ‘ญ The two sisters meet each other (POST /games/:id/play)", done => { + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "two-sisters", action: "meet-each-other" }) @@ -434,7 +801,7 @@ describe("K - Game options", () => { }); }); it("๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ฆ The three brothers meet each other (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "three-brothers", action: "meet-each-other" }) @@ -444,9 +811,9 @@ describe("K - Game options", () => { done(); }); }); - it("๐Ÿชถ Raven mark a brother (POST /games/:id/play)", done => { + it("๐Ÿชถ Raven marks a brother (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "raven", action: "mark", targets: [{ player: players[5]._id }] }) @@ -458,25 +825,25 @@ describe("K - Game options", () => { }); it("๐Ÿ›ก Guard protects the little girl (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) - .send({ source: "guard", action: "protect", targets: [{ player: players[8]._id }] }) + .send({ source: "guard", action: "protect", targets: [{ player: players[9]._id }] }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.players[8].attributes).to.deep.include({ name: "protected", source: "guard", remainingPhases: 1 }); + expect(game.players[9].attributes).to.deep.include({ name: "protected", source: "guard", remainingPhases: 1 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[8]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[9]._id); done(); }); }); it("๐Ÿบ Werewolf eats the little girl (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) - .send({ source: "werewolves", action: "eat", targets: [{ player: players[8]._id }] }) + .send({ source: "werewolves", action: "eat", targets: [{ player: players[9]._id }] }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; @@ -484,13 +851,13 @@ describe("K - Game options", () => { }); }); it("โ˜€๏ธ Sun is rising and little girl is alive because option for protecting her is set to true", done => { - expect(game.players[8].isAlive).to.be.true; - expect(game.phase).to.equals("day"); + expect(game.players[9].isAlive).to.be.true; + expect(game.phase).to.equal("day"); done(); }); it("๐Ÿ‘ช Tie in votes between the werewolf and one of the two sisters (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -502,7 +869,7 @@ describe("K - Game options", () => { .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.history[0].play.votesResult).to.equals("need-settlement"); + expect(game.history[0].play.votesResult).to.equal("need-settlement"); expect(game.players[0].isAlive).to.be.true; expect(game.players[1].isAlive).to.be.true; expect(game.players[5].isAlive).to.be.true; @@ -514,7 +881,7 @@ describe("K - Game options", () => { done(); }); it("๐Ÿ‘ช All can't vote if one vote target is not one of the players in the previous tie in votes (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -525,13 +892,13 @@ describe("K - Game options", () => { }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("CANT_BE_VOTE_TARGET"); + expect(res.body.type).to.equal("CANT_BE_VOTE_TARGET"); done(); }); }); it("๐Ÿ‘ช Another tie in votes between the werewolf, one of the two sisters and one of the three brothers, then nobody dies (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -550,19 +917,31 @@ describe("K - Game options", () => { expect(game.players[0].isAlive).to.be.true; expect(game.players[1].isAlive).to.be.true; expect(game.players[5].isAlive).to.be.true; - expect(game.history[0].play.votesResult).to.equals("no-death"); + expect(game.history[0].play.votesResult).to.equal("no-death"); expect(game.history[0].deadPlayers).to.not.exist; done(); }); }); it("๐ŸŒ™ Night falls", done => { - expect(game.phase).to.equals("night"); - expect(game.turn).to.equals(2); + expect(game.phase).to.equal("night"); + expect(game.turn).to.equal(2); done(); }); + it("๐ŸฆŠ Fox skips (POST /games/:id/play)", done => { + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "fox", action: "sniff" }) + .end((err, res) => { + game = res.body; + expect(res).to.have.status(200); + expect(game.history[0].play.targets).to.not.exist; + done(); + }); + }); it("๐Ÿชถ Raven skips (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "raven", action: "mark" }) @@ -574,25 +953,25 @@ describe("K - Game options", () => { }); it("๐Ÿ›ก Guard protects himself (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) - .send({ source: "guard", action: "protect", targets: [{ player: players[9]._id }] }) + .send({ source: "guard", action: "protect", targets: [{ player: players[8]._id }] }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.players[9].attributes).to.deep.include({ name: "protected", source: "guard", remainingPhases: 1 }); + expect(game.players[8].attributes).to.deep.include({ name: "protected", source: "guard", remainingPhases: 1 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[9]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[8]._id); done(); }); }); it("๐Ÿบ Werewolf eats one of the three brothers (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) - .send({ source: "werewolves", action: "eat", targets: [{ player: players[5]._id }] }) + .send({ source: "werewolves", action: "eat", targets: [{ player: players[4]._id }] }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; @@ -600,12 +979,12 @@ describe("K - Game options", () => { }); }); it("โ˜€๏ธ Sun is rising", done => { - expect(game.phase).to.equals("day"); + expect(game.phase).to.equal("day"); done(); }); it("๐Ÿ‘ช Tie in votes between the two sisters (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -617,7 +996,7 @@ describe("K - Game options", () => { .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.history[0].play.votesResult).to.equals("need-settlement"); + expect(game.history[0].play.votesResult).to.equal("need-settlement"); expect(game.players[1].isAlive).to.be.true; expect(game.players[2].isAlive).to.be.true; done(); @@ -625,7 +1004,7 @@ describe("K - Game options", () => { }); it("๐Ÿ‘ช All vote for the second sister (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -639,7 +1018,7 @@ describe("K - Game options", () => { game = res.body; expect(game.players[1].isAlive).to.be.true; expect(game.players[2].isAlive).to.be.false; - expect(game.history[0].play.votesResult).to.equals("death"); + expect(game.history[0].play.votesResult).to.equal("death"); expect(game.history[0].deadPlayers).to.be.an("array").lengthOf(1); expect(game.history[0].deadPlayers[0]._id).to.be.equals(game.players[2]._id); done(); diff --git a/tests/e2e/game/game-repartition.test.js b/tests/e2e/game/game-repartition.test.js index d08bbf3..d2c0474 100644 --- a/tests/e2e/game/game-repartition.test.js +++ b/tests/e2e/game/game-repartition.test.js @@ -58,13 +58,16 @@ const aLotOfPlayers = [ { name: "39" }, { name: "40" }, ]; -let token; +let server, token; describe("E - Game repartition with multiple teams", () => { before(done => resetDatabase(done)); + before(done => { + server = app.listen(3000, done); + }); after(done => resetDatabase(done)); it("๐Ÿ‘ค Creates new user (POST /users)", done => { - chai.request(app) + chai.request(server) .post("/users") .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -74,7 +77,7 @@ describe("E - Game repartition with multiple teams", () => { }); }); it("๐Ÿ”‘ Logs in successfully (POST /users/login)", done => { - chai.request(app) + chai.request(server) .post(`/users/login`) .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -85,7 +88,7 @@ describe("E - Game repartition with multiple teams", () => { }); }); it("๐Ÿ‘ช Gets game repartition for 4 players with basic auth (GET /games/repartition)", done => { - chai.request(app) + chai.request(server) .get(`/games/repartition?${stringify({ players })}`) .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .end((err, res) => { @@ -94,7 +97,7 @@ describe("E - Game repartition with multiple teams", () => { }); }); it("๐Ÿ‘ช Gets game repartition for 4 players with JWT auth (GET /games/repartition)", done => { - chai.request(app) + chai.request(server) .get(`/games/repartition?${stringify({ players })}`) .set({ Authorization: `Bearer ${token}` }) .end((err, res) => { @@ -103,7 +106,7 @@ describe("E - Game repartition with multiple teams", () => { }); }); it("๐Ÿ‘ช Gets game repartition for 40 players with JWT auth (GET /games/repartition)", done => { - chai.request(app) + chai.request(server) .get(`/games/repartition?${stringify({ players: aLotOfPlayers })}`) .set({ Authorization: `Bearer ${token}` }) .end((err, res) => { @@ -112,7 +115,7 @@ describe("E - Game repartition with multiple teams", () => { }); }); it("๐Ÿ” Can't get game repartition without auth (GET /games/repartition)", done => { - chai.request(app) + chai.request(server) .get(`/games/repartition?${stringify({ players })}`) .end((err, res) => { expect(res).to.have.status(401); @@ -120,22 +123,22 @@ describe("E - Game repartition with multiple teams", () => { }); }); it("๐Ÿคผ Can't get game repartition with less than 4 players (GET /games/repartition)", done => { - chai.request(app) + chai.request(server) .get(`/games/repartition?${stringify({ players: [{ name: "1" }] })}`) .set({ Authorization: `Bearer ${token}` }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_REQUEST"); + expect(res.body.type).to.equal("BAD_REQUEST"); done(); }); }); it("๐Ÿคผ Can't get game repartition with more than 40 players (GET /games/repartition)", done => { - chai.request(app) + chai.request(server) .get(`/games/repartition?${stringify({ players: [...players, ...aLotOfPlayers] })}`) .set({ Authorization: `Bearer ${token}` }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_REQUEST"); + expect(res.body.type).to.equal("BAD_REQUEST"); done(); }); }); diff --git a/tests/e2e/game/game-with-empty-days.test.js b/tests/e2e/game/game-with-empty-days.test.js index b741aa0..89c86ee 100644 --- a/tests/e2e/game/game-with-empty-days.test.js +++ b/tests/e2e/game/game-with-empty-days.test.js @@ -17,13 +17,16 @@ let players = [ { name: "Dyg", role: "villager" }, { name: "Dฯ€g", role: "villager" }, ]; -let token, game; +let server, token, game; describe("M - Game with empty days because vote is impossible", () => { before(done => resetDatabase(done)); + before(done => { + server = app.listen(3000, done); + }); after(done => resetDatabase(done)); it("๐Ÿ‘ค Creates new user (POST /users)", done => { - chai.request(app) + chai.request(server) .post("/users") .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -33,7 +36,7 @@ describe("M - Game with empty days because vote is impossible", () => { }); }); it("๐Ÿ”‘ Logs in successfully (POST /users/login)", done => { - chai.request(app) + chai.request(server) .post(`/users/login`) .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -44,7 +47,7 @@ describe("M - Game with empty days because vote is impossible", () => { }); }); it("๐ŸŽฒ Creates game with JWT auth (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players }) @@ -56,7 +59,7 @@ describe("M - Game with empty days because vote is impossible", () => { }); it("๐Ÿ‘ช All elect the werewolf as the sheriff (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "elect-sheriff", votes: [{ from: players[1]._id, for: players[0]._id }] }) @@ -66,10 +69,10 @@ describe("M - Game with empty days because vote is impossible", () => { expect(game.players[0].attributes).to.deep.include({ name: "sheriff", source: "all" }); expect(game.history).to.be.an("array").to.have.lengthOf(1); expect(game.history[0].play.votes).to.exist; - expect(game.history[0].play.votes[0].from._id).to.equals(game.players[1]._id); - expect(game.history[0].play.votes[0].for._id).to.equals(game.players[0]._id); + expect(game.history[0].play.votes[0].from._id).to.equal(game.players[1]._id); + expect(game.history[0].play.votes[0].for._id).to.equal(game.players[0]._id); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(game.players[0]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(game.players[0]._id); expect(game.history[0].play.source.name).to.equal("all"); expect(game.history[0].play.source.players).to.be.an("array").to.have.lengthOf(players.length); expect(game.history[0].deadPlayers).to.not.exist; @@ -78,7 +81,7 @@ describe("M - Game with empty days because vote is impossible", () => { }); it("๐Ÿบ Werewolf eats one villager (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[2]._id }] }) @@ -86,13 +89,13 @@ describe("M - Game with empty days because vote is impossible", () => { expect(res).to.have.status(200); game = res.body; expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[2]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[2]._id); done(); }); }); it("๐Ÿ‘ช Tie in votes between scapegoat and a villager (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -110,7 +113,7 @@ describe("M - Game with empty days because vote is impossible", () => { }); }); it("๐Ÿ Scapegoat bans voting all alive players except one (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -137,7 +140,7 @@ describe("M - Game with empty days because vote is impossible", () => { }); it("๐Ÿบ Werewolf eats the only villager who can vote (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[5]._id }] }) @@ -145,13 +148,13 @@ describe("M - Game with empty days because vote is impossible", () => { expect(res).to.have.status(200); game = res.body; expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[5]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[5]._id); done(); }); }); it("๐ŸŒ™ Night falls without day rising because no one can vote", done => { - expect(game.phase).to.equals("night"); - expect(game.turn).to.equals(3); + expect(game.phase).to.equal("night"); + expect(game.turn).to.equal(3); expect(game.waiting[0]).to.deep.equals({ for: "werewolves", to: "eat" }); done(); }); diff --git a/tests/e2e/game/game-won-by-angel.test.js b/tests/e2e/game/game-won-by-angel.test.js index 67f0c0c..3694655 100644 --- a/tests/e2e/game/game-won-by-angel.test.js +++ b/tests/e2e/game/game-won-by-angel.test.js @@ -14,14 +14,18 @@ const originalPlayers = [ { name: "Dig", role: "werewolf" }, { name: "Deg", role: "villager" }, { name: "Dog", role: "villager" }, + { name: "D|g", role: "witch" }, ]; -let token, game, players; +let server, token, game, players; -describe("T - Tiny game of 4 players in which the angel wins of vote, werewolves or sheriff, and wins", () => { +describe("T - Tiny game of 5 players in which the angel wins of vote, werewolves or sheriff, and wins", () => { before(done => resetDatabase(done)); + before(done => { + server = app.listen(3000, done); + }); after(done => resetDatabase(done)); it("๐Ÿ‘ค Creates new user (POST /users)", done => { - chai.request(app) + chai.request(server) .post("/users") .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -31,7 +35,7 @@ describe("T - Tiny game of 4 players in which the angel wins of vote, werewolves }); }); it("๐Ÿ”‘ Logs in successfully (POST /users/login)", done => { - chai.request(app) + chai.request(server) .post(`/users/login`) .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -42,7 +46,7 @@ describe("T - Tiny game of 4 players in which the angel wins of vote, werewolves }); }); it("๐ŸŽฒ Creates game with JWT auth (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players: [...originalPlayers, { name: "Dรฆg", role: "stuttering-judge" }], options: { roles: { sheriff: { isEnabled: false } } } }) @@ -54,19 +58,19 @@ describe("T - Tiny game of 4 players in which the angel wins of vote, werewolves }); it("โš–๏ธ Stuttering judge requests another vote but didn't choose his sign yet (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[1]._id, for: players[0]._id }], doesJudgeRequestAnotherVote: true }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("STUTTERING_JUDGE_DIDNT_CHOOSE_SIGN_YET"); + expect(res.body.type).to.equal("STUTTERING_JUDGE_DIDNT_CHOOSE_SIGN_YET"); done(); }); }); it("๐Ÿ‘ช All vote for the angel (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[1]._id, for: players[0]._id }] }) @@ -79,14 +83,14 @@ describe("T - Tiny game of 4 players in which the angel wins of vote, werewolves }); }); it("๐ŸŽฒ Game is WON by the angel !", done => { - expect(game.status).to.equals("done"); - expect(game.won.by).to.equals("angel"); + expect(game.status).to.equal("done"); + expect(game.won.by).to.equal("angel"); expect(game.won.players).to.be.an("array").lengthOf(1); - expect(game.won.players[0]._id).to.equals(game.players[0]._id); + expect(game.won.players[0]._id).to.equal(game.players[0]._id); done(); }); it("๐ŸŽฒ Creates game with JWT auth (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players: originalPlayers, options: { roles: { sheriff: { isEnabled: false } } } }) @@ -96,23 +100,23 @@ describe("T - Tiny game of 4 players in which the angel wins of vote, werewolves done(); }); }); - it("๐Ÿ‘ช All vote for the villager (POST /games/:id/play)", done => { + it("๐Ÿ‘ช All vote for the witch (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) - .send({ source: "all", action: "vote", votes: [{ from: players[1]._id, for: players[2]._id }] }) + .send({ source: "all", action: "vote", votes: [{ from: players[1]._id, for: players[4]._id }] }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.players[2].isAlive).to.be.false; - expect(game.players[2].murdered).to.deep.equals({ by: "all", of: "vote" }); + expect(game.players[4].isAlive).to.be.false; + expect(game.players[4].murdered).to.deep.equals({ by: "all", of: "vote" }); done(); }); }); - it("๐Ÿบ Werewolf eats one villager (POST /games/:id/play)", done => { + it("๐Ÿบ Werewolf eats the angel (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[0]._id }] }) @@ -120,19 +124,19 @@ describe("T - Tiny game of 4 players in which the angel wins of vote, werewolves expect(res).to.have.status(200); game = res.body; expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[0]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[0]._id); done(); }); }); - it("๐ŸŽฒ Game is WON by the angel !", done => { - expect(game.status).to.equals("done"); - expect(game.won.by).to.equals("angel"); + it("๐ŸŽฒ Game is WON by the angel ! (Witch is not called during the night because she is dead)", done => { + expect(game.status).to.equal("done"); + expect(game.won.by).to.equal("angel"); expect(game.won.players).to.be.an("array").lengthOf(1); - expect(game.won.players[0]._id).to.equals(game.players[0]._id); + expect(game.won.players[0]._id).to.equal(game.players[0]._id); done(); }); it("๐ŸŽฒ Creates game with JWT auth (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players: originalPlayers }) @@ -144,7 +148,7 @@ describe("T - Tiny game of 4 players in which the angel wins of vote, werewolves }); it("๐Ÿ‘ช All elect the angel as the sheriff (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "elect-sheriff", votes: [{ from: players[1]._id, for: players[0]._id }] }) @@ -156,7 +160,7 @@ describe("T - Tiny game of 4 players in which the angel wins of vote, werewolves }); it("๐Ÿ‘ช Tie in votes between werewolf and angel (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -175,7 +179,7 @@ describe("T - Tiny game of 4 players in which the angel wins of vote, werewolves }); it("๐ŸŽ– Sheriff settles votes by choosing himself (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "sheriff", action: "settle-votes", targets: [{ player: players[0]._id }] }) @@ -188,10 +192,10 @@ describe("T - Tiny game of 4 players in which the angel wins of vote, werewolves }); }); it("๐ŸŽฒ Game is WON by the angel !", done => { - expect(game.status).to.equals("done"); - expect(game.won.by).to.equals("angel"); + expect(game.status).to.equal("done"); + expect(game.won.by).to.equal("angel"); expect(game.won.players).to.be.an("array").lengthOf(1); - expect(game.won.players[0]._id).to.equals(game.players[0]._id); + expect(game.won.players[0]._id).to.equal(game.players[0]._id); done(); }); }); \ No newline at end of file diff --git a/tests/e2e/game/game-won-by-lovers.test.js b/tests/e2e/game/game-won-by-lovers.test.js index a523556..f731d43 100644 --- a/tests/e2e/game/game-won-by-lovers.test.js +++ b/tests/e2e/game/game-won-by-lovers.test.js @@ -15,13 +15,16 @@ let players = [ { name: "Deg", role: "villager" }, { name: "Dog", role: "little-girl" }, ]; -let token, game; +let server, token, game; describe("J - Tiny game of 4 players in which lovers win despite they're not on the same side", () => { before(done => resetDatabase(done)); + before(done => { + server = app.listen(3000, done); + }); after(done => resetDatabase(done)); it("๐Ÿ‘ค Creates new user (POST /users)", done => { - chai.request(app) + chai.request(server) .post("/users") .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -31,7 +34,7 @@ describe("J - Tiny game of 4 players in which lovers win despite they're not on }); }); it("๐Ÿ”‘ Logs in successfully (POST /users/login)", done => { - chai.request(app) + chai.request(server) .post(`/users/login`) .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -42,7 +45,7 @@ describe("J - Tiny game of 4 players in which lovers win despite they're not on }); }); it("๐ŸŽฒ Creates game with JWT auth (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players }) @@ -54,7 +57,7 @@ describe("J - Tiny game of 4 players in which lovers win despite they're not on }); it("๐Ÿ‘ช All elect the werewolf as the sheriff (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -71,7 +74,7 @@ describe("J - Tiny game of 4 players in which lovers win despite they're not on }); it("๐Ÿน Cupid charms the little girl and the werewolf (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -85,13 +88,13 @@ describe("J - Tiny game of 4 players in which lovers win despite they're not on game = res.body; expect(game.players[0].attributes).to.deep.include({ name: "in-love", source: "cupid" }); expect(game.players[3].attributes).to.deep.include({ name: "in-love", source: "cupid" }); - expect(game.history[0].play.targets[0].player._id).to.equals(players[0]._id); - expect(game.history[0].play.targets[1].player._id).to.equals(players[3]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[0]._id); + expect(game.history[0].play.targets[1].player._id).to.equal(players[3]._id); done(); }); }); it("๐Ÿ’• Lovers meet each other (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "lovers", action: "meet-each-other" }) @@ -103,19 +106,19 @@ describe("J - Tiny game of 4 players in which lovers win despite they're not on }); it("๐Ÿบ Vile father of wolves can't infect if he is not in the game (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[2]._id, isInfected: true }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("ABSENT_VILE_FATHER_OF_WOLVES"); + expect(res.body.type).to.equal("ABSENT_VILE_FATHER_OF_WOLVES"); done(); }); }); it("๐Ÿบ Werewolf eats the villager (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[2]._id }] }) @@ -126,13 +129,13 @@ describe("J - Tiny game of 4 players in which lovers win despite they're not on }); }); it("โ˜€๏ธ Sun is rising and villager is dead", done => { - expect(game.phase).to.equals("day"); + expect(game.phase).to.equal("day"); expect(game.players[2].isAlive).to.be.false; done(); }); it("๐Ÿ‘ช All vote for cupid (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[0]._id, for: players[1]._id }] }) @@ -145,10 +148,10 @@ describe("J - Tiny game of 4 players in which lovers win despite they're not on }); }); it("๐ŸŽฒ Game is WON by lovers!", done => { - expect(game.status).to.equals("done"); - expect(game.won.by).to.equals("lovers"); + expect(game.status).to.equal("done"); + expect(game.won.by).to.equal("lovers"); expect(game.won.players).to.be.an("array"); - expect(game.won.players.length).to.equals(2); + expect(game.won.players.length).to.equal(2); done(); }); }); diff --git a/tests/e2e/game/game-won-by-pied-piper.test.js b/tests/e2e/game/game-won-by-pied-piper.test.js index 6876f92..34d8bce 100644 --- a/tests/e2e/game/game-won-by-pied-piper.test.js +++ b/tests/e2e/game/game-won-by-pied-piper.test.js @@ -17,13 +17,16 @@ let players = [ { name: "Dฯ€g", role: "villager" }, { name: "Dรฆg", role: "werewolf" }, ]; -let token, game; +let server, token, game; describe("N - Tiny game of 6 players in which the pied piper charmed everybody, and so wins", () => { before(done => resetDatabase(done)); + before(done => { + server = app.listen(3000, done); + }); after(done => resetDatabase(done)); it("๐Ÿ‘ค Creates new user (POST /users)", done => { - chai.request(app) + chai.request(server) .post("/users") .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -33,7 +36,7 @@ describe("N - Tiny game of 6 players in which the pied piper charmed everybody, }); }); it("๐Ÿ”‘ Logs in successfully (POST /users/login)", done => { - chai.request(app) + chai.request(server) .post(`/users/login`) .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -44,7 +47,7 @@ describe("N - Tiny game of 6 players in which the pied piper charmed everybody, }); }); it("๐ŸŽฒ Creates game with JWT auth (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players }) @@ -56,7 +59,7 @@ describe("N - Tiny game of 6 players in which the pied piper charmed everybody, }); it("๐Ÿ‘ช All elect the pied piper as the sheriff (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "elect-sheriff", votes: [{ from: players[0]._id, for: players[1]._id }] }) @@ -68,7 +71,7 @@ describe("N - Tiny game of 6 players in which the pied piper charmed everybody, }); it("๐Ÿบ Werewolf eats the villager (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[2]._id }] }) @@ -79,7 +82,7 @@ describe("N - Tiny game of 6 players in which the pied piper charmed everybody, }); }); it("๐Ÿ“ฃ Pied piper charms two villagers (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -97,7 +100,7 @@ describe("N - Tiny game of 6 players in which the pied piper charmed everybody, }); }); it("๐Ÿ•บ๏ธ Charmed players meet each other (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "charmed", action: "meet-each-other" }) @@ -109,7 +112,7 @@ describe("N - Tiny game of 6 players in which the pied piper charmed everybody, }); it("๐Ÿ‘ช All vote for werewolf (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[1]._id, for: players[0]._id }] }) @@ -123,7 +126,7 @@ describe("N - Tiny game of 6 players in which the pied piper charmed everybody, }); it("๐Ÿบ Werewolf eats the pied piper (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[1]._id }] }) @@ -135,18 +138,18 @@ describe("N - Tiny game of 6 players in which the pied piper charmed everybody, }); it("๐Ÿน Pied piper can't charm just two targets if only one can be charmed (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "pied-piper", action: "charm", targets: [{ player: players[0]._id }, { player: players[5]._id }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_TARGETS_LENGTH"); + expect(res.body.type).to.equal("BAD_TARGETS_LENGTH"); done(); }); }); it("๐Ÿ“ฃ Pied piper charms the last werewolf (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "pied-piper", action: "charm", targets: [{ player: players[5]._id }] }) @@ -158,10 +161,10 @@ describe("N - Tiny game of 6 players in which the pied piper charmed everybody, }); }); it("๐ŸŽฒ Game is WON by the pied piper even if there is no more werewolves because all alive players are charmed !", done => { - expect(game.status).to.equals("done"); - expect(game.won.by).to.equals("pied-piper"); + expect(game.status).to.equal("done"); + expect(game.won.by).to.equal("pied-piper"); expect(game.won.players).to.be.an("array").lengthOf(1); - expect(game.won.players[0]._id).to.equals(game.players[1]._id); + expect(game.won.players[0]._id).to.equal(game.players[1]._id); done(); }); }); \ No newline at end of file diff --git a/tests/e2e/game/game-won-by-white-werewolf.test.js b/tests/e2e/game/game-won-by-white-werewolf.test.js index 9a1221e..8471e0d 100644 --- a/tests/e2e/game/game-won-by-white-werewolf.test.js +++ b/tests/e2e/game/game-won-by-white-werewolf.test.js @@ -15,13 +15,16 @@ let players = [ { name: "Deg", role: "witch" }, { name: "Dog", role: "villager" }, ]; -let token, game; +let server, token, game; describe("R - Tiny game of 4 players in which the white werewolf is the last survivor, and so wins alone", () => { before(done => resetDatabase(done)); + before(done => { + server = app.listen(3000, done); + }); after(done => resetDatabase(done)); it("๐Ÿ‘ค Creates new user (POST /users)", done => { - chai.request(app) + chai.request(server) .post("/users") .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -31,7 +34,7 @@ describe("R - Tiny game of 4 players in which the white werewolf is the last sur }); }); it("๐Ÿ”‘ Logs in successfully (POST /users/login)", done => { - chai.request(app) + chai.request(server) .post(`/users/login`) .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -42,7 +45,7 @@ describe("R - Tiny game of 4 players in which the white werewolf is the last sur }); }); it("๐ŸŽฒ Creates game with JWT auth (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players, options: { roles: { sheriff: { isEnabled: false } } } }) @@ -54,7 +57,7 @@ describe("R - Tiny game of 4 players in which the white werewolf is the last sur }); it("๐Ÿบ Werewolf eats the witch (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[2]._id }] }) @@ -66,7 +69,7 @@ describe("R - Tiny game of 4 players in which the white werewolf is the last sur }); it("๐Ÿบ White werewolf eats the werewolf (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "white-werewolf", action: "eat", targets: [{ player: players[1]._id }] }) @@ -75,25 +78,25 @@ describe("R - Tiny game of 4 players in which the white werewolf is the last sur game = res.body; expect(game.players[1].attributes).to.deep.include({ name: "eaten", source: "white-werewolf", remainingPhases: 1 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[1]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[1]._id); done(); }); }); it("๐Ÿช„ Witch can't use life potion on player eaten by white-werewolf (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "witch", action: "use-potion", targets: [{ player: players[1]._id, hasDrankLifePotion: true }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_LIFE_POTION_USE"); + expect(res.body.type).to.equal("BAD_LIFE_POTION_USE"); done(); }); }); it("๐Ÿช„ Witch use death potion on villager (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "witch", action: "use-potion", targets: [{ player: players[3]._id, hasDrankDeathPotion: true }] }) @@ -104,10 +107,10 @@ describe("R - Tiny game of 4 players in which the white werewolf is the last sur }); }); it("๐ŸŽฒ Game is WON by the white werewolf !", done => { - expect(game.status).to.equals("done"); - expect(game.won.by).to.equals("white-werewolf"); + expect(game.status).to.equal("done"); + expect(game.won.by).to.equal("white-werewolf"); expect(game.won.players).to.be.an("array").lengthOf(1); - expect(game.won.players[0]._id).to.equals(game.players[0]._id); + expect(game.won.players[0]._id).to.equal(game.players[0]._id); done(); }); }); \ No newline at end of file diff --git a/tests/e2e/game/hunter-kills-mayor-on-day-time.test.js b/tests/e2e/game/hunter-kills-mayor-on-day-time.test.js index fdf15b4..009a1f6 100644 --- a/tests/e2e/game/hunter-kills-mayor-on-day-time.test.js +++ b/tests/e2e/game/hunter-kills-mayor-on-day-time.test.js @@ -84,7 +84,7 @@ describe("H - Game where hunter kills mayor when day rises", () => { }); }); it("โ˜€๏ธ Sun is rising and hunter is dead", done => { - expect(game.phase).to.equals("day"); + expect(game.phase).to.equal("day"); expect(game.players[0].isAlive).to.be.false; done(); }); diff --git a/tests/e2e/game/index.js b/tests/e2e/game/index.js index c78c1b3..60e3ade 100644 --- a/tests/e2e/game/index.js +++ b/tests/e2e/game/index.js @@ -23,4 +23,6 @@ describe("E2E - ๐ŸŽฒ Game tests", () => { require("./game-won-by-angel.test"); require("./thief-steals.test"); require("./changes-during-night.test"); + require("./fox-lack-of-neighbors.test"); + require("./bear-tamer-infected.test"); }); \ No newline at end of file diff --git a/tests/e2e/game/kill-player-twice-game.test.js b/tests/e2e/game/kill-player-twice-game.test.js index d671018..e312daf 100644 --- a/tests/e2e/game/kill-player-twice-game.test.js +++ b/tests/e2e/game/kill-player-twice-game.test.js @@ -15,13 +15,16 @@ let players = [ { name: "Deg", role: "werewolf" }, { name: "Dog", role: "werewolf" }, ]; -let token, game; +let server, token, game; describe("G - Game where player is killed twice during the night", () => { before(done => resetDatabase(done)); + before(done => { + server = app.listen(3000, done); + }); after(done => resetDatabase(done)); it("๐Ÿ‘ค Creates new user (POST /users)", done => { - chai.request(app) + chai.request(server) .post("/users") .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -31,7 +34,7 @@ describe("G - Game where player is killed twice during the night", () => { }); }); it("๐Ÿ”‘ Logs in successfully (POST /users/login)", done => { - chai.request(app) + chai.request(server) .post(`/users/login`) .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -42,7 +45,7 @@ describe("G - Game where player is killed twice during the night", () => { }); }); it("๐ŸŽฒ Creates game with JWT auth (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players }) @@ -54,7 +57,7 @@ describe("G - Game where player is killed twice during the night", () => { }); it("๐Ÿ‘ช All elect the villager as the sheriff (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -68,16 +71,16 @@ describe("G - Game where player is killed twice during the night", () => { game = res.body; expect(game.players[1].attributes).to.deep.include({ name: "sheriff", source: "all" }); expect(game.history[0].play.votes).to.exist; - expect(game.history[0].play.votes[0].from._id).to.equals(game.players[0]._id); - expect(game.history[0].play.votes[0].for._id).to.equals(game.players[1]._id); + expect(game.history[0].play.votes[0].from._id).to.equal(game.players[0]._id); + expect(game.history[0].play.votes[0].for._id).to.equal(game.players[1]._id); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(game.players[1]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(game.players[1]._id); done(); }); }); it("๐Ÿบ Werewolves eat the villager (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[1]._id }] }) @@ -86,13 +89,13 @@ describe("G - Game where player is killed twice during the night", () => { game = res.body; expect(game.players[1].attributes).to.deep.include({ name: "eaten", source: "werewolves", remainingPhases: 1 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[1]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[1]._id); done(); }); }); it("๐Ÿช„ Witch uses death potion on villager (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "witch", action: "use-potion", targets: [{ player: players[1]._id, hasDrankDeathPotion: true }] }) @@ -100,13 +103,13 @@ describe("G - Game where player is killed twice during the night", () => { expect(res).to.have.status(200); game = res.body; expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[1]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[1]._id); expect(game.history[0].play.targets[0].hasDrankDeathPotion).to.be.true; done(); }); }); it("โ˜€๏ธ Sun is rising and villager is dead", done => { - expect(game.phase).to.equals("day"); + expect(game.phase).to.equal("day"); expect(game.players[1].isAlive).to.be.false; done(); }); @@ -116,7 +119,7 @@ describe("G - Game where player is killed twice during the night", () => { }); it("๐ŸŽ– Sheriff delegates to the hunter (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "sheriff", action: "delegate", targets: [{ player: players[0]._id }] }) @@ -126,7 +129,7 @@ describe("G - Game where player is killed twice during the night", () => { expect(game.players[1].attributes).to.not.deep.include({ name: "sheriff", source: "all" }); expect(game.players[0].attributes).to.deep.include({ name: "sheriff", source: "sheriff" }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(game.players[0]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(game.players[0]._id); done(); }); }); diff --git a/tests/e2e/game/no-winner-game.test.js b/tests/e2e/game/no-winner-game.test.js index d198f9c..c92c39e 100644 --- a/tests/e2e/game/no-winner-game.test.js +++ b/tests/e2e/game/no-winner-game.test.js @@ -16,13 +16,16 @@ let players = [ { name: "Deg", role: "witch" }, { name: "Dog", role: "villager" }, ]; -let token, game; +let server, token, game; describe("I - Tiny game of 4 players with no winner at the end", () => { before(done => resetDatabase(done)); + before(done => { + server = app.listen(3000, done); + }); after(done => resetDatabase(done)); it("๐Ÿ‘ค Creates new user (POST /users)", done => { - chai.request(app) + chai.request(server) .post("/users") .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -32,7 +35,7 @@ describe("I - Tiny game of 4 players with no winner at the end", () => { }); }); it("๐Ÿ”‘ Logs in successfully (POST /users/login)", done => { - chai.request(app) + chai.request(server) .post(`/users/login`) .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -43,7 +46,7 @@ describe("I - Tiny game of 4 players with no winner at the end", () => { }); }); it("๐ŸŽฒ Creates game with JWT auth (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players }) @@ -54,19 +57,19 @@ describe("I - Tiny game of 4 players with no winner at the end", () => { }); }); it("๐Ÿ” Can't make a play if game's doesn't belong to user (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${new mongoose.Types.ObjectId()}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "elect-sheriff" }) .end((err, res) => { expect(res).to.have.status(401); - expect(res.body.type).to.equals("GAME_DOESNT_BELONG_TO_USER"); + expect(res.body.type).to.equal("GAME_DOESNT_BELONG_TO_USER"); done(); }); }); it("๐Ÿ‘ช All elect the hunter as the sheriff (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -84,7 +87,7 @@ describe("I - Tiny game of 4 players with no winner at the end", () => { }); it("๐Ÿบ Werewolf eats the villager (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[3]._id }] }) @@ -96,7 +99,7 @@ describe("I - Tiny game of 4 players with no winner at the end", () => { }); it("๐Ÿช„ Witch uses life potion on villager (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "witch", action: "use-potion", targets: [{ player: players[3]._id, hasDrankLifePotion: true }] }) @@ -104,19 +107,19 @@ describe("I - Tiny game of 4 players with no winner at the end", () => { expect(res).to.have.status(200); game = res.body; expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[3]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[3]._id); expect(game.history[0].play.targets[0].hasDrankLifePotion).to.be.true; done(); }); }); it("โ˜€๏ธ Sun is rising and villager is dead", done => { - expect(game.phase).to.equals("day"); + expect(game.phase).to.equal("day"); expect(game.players[1].isAlive).to.be.true; done(); }); it("๐Ÿ‘ช All vote for villager (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[0]._id, for: players[3]._id }] }) @@ -129,13 +132,13 @@ describe("I - Tiny game of 4 players with no winner at the end", () => { }); }); it("๐ŸŒ™ Night falls", done => { - expect(game.phase).to.equals("night"); - expect(game.turn).to.equals(2); + expect(game.phase).to.equal("night"); + expect(game.turn).to.equal(2); done(); }); it("๐Ÿบ Werewolf eats the witch (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[2]._id }] }) @@ -147,7 +150,7 @@ describe("I - Tiny game of 4 players with no winner at the end", () => { }); it("๐Ÿช„ Witch uses death potion on hunter (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "witch", action: "use-potion", targets: [{ player: players[1]._id, hasDrankDeathPotion: true }] }) @@ -155,7 +158,7 @@ describe("I - Tiny game of 4 players with no winner at the end", () => { expect(res).to.have.status(200); game = res.body; expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[1]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[1]._id); expect(game.history[0].play.targets[0].hasDrankDeathPotion).to.be.true; done(); }); @@ -166,7 +169,7 @@ describe("I - Tiny game of 4 players with no winner at the end", () => { }); it("๐Ÿ”ซ Hunter shoots at the last werewolf (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "hunter", action: "shoot", targets: [{ player: players[0]._id }] }) @@ -178,8 +181,8 @@ describe("I - Tiny game of 4 players with no winner at the end", () => { }); }); it("๐ŸŽฒ Game is WON by... nobody ..!", done => { - expect(game.status).to.equals("done"); - expect(game.won.by).to.equals(null); + expect(game.status).to.equal("done"); + expect(game.won.by).to.equal(null); expect(game.won.players).to.not.exist; done(); }); diff --git a/tests/e2e/game/pied-piper-infected.test.js b/tests/e2e/game/pied-piper-infected.test.js index 42818b8..27aa896 100644 --- a/tests/e2e/game/pied-piper-infected.test.js +++ b/tests/e2e/game/pied-piper-infected.test.js @@ -16,13 +16,16 @@ const originalPlayers = [ { name: "Dog", role: "villager" }, { name: "Dรธg", role: "villager" }, ]; -let token, game, players; +let server, token, game, players; describe("O - Tiny game of 5 players in which the pied piper is infected and so, looses his powers and can't win alone", () => { before(done => resetDatabase(done)); + before(done => { + server = app.listen(3000, done); + }); after(done => resetDatabase(done)); it("๐Ÿ‘ค Creates new user (POST /users)", done => { - chai.request(app) + chai.request(server) .post("/users") .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -32,7 +35,7 @@ describe("O - Tiny game of 5 players in which the pied piper is infected and so, }); }); it("๐Ÿ”‘ Logs in successfully (POST /users/login)", done => { - chai.request(app) + chai.request(server) .post(`/users/login`) .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -43,7 +46,7 @@ describe("O - Tiny game of 5 players in which the pied piper is infected and so, }); }); it("๐ŸŽฒ Creates game with JWT auth (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players: originalPlayers }) @@ -55,7 +58,7 @@ describe("O - Tiny game of 5 players in which the pied piper is infected and so, }); it("๐Ÿ‘ช All elect the pied piper as the sheriff (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "elect-sheriff", votes: [{ from: players[0]._id, for: players[1]._id }] }) @@ -67,7 +70,7 @@ describe("O - Tiny game of 5 players in which the pied piper is infected and so, }); it("๐Ÿบ Vile father of wolf eats a villager (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[2]._id }] }) @@ -78,7 +81,7 @@ describe("O - Tiny game of 5 players in which the pied piper is infected and so, }); }); it("๐Ÿ“ฃ Pied piper charms two villagers (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -96,7 +99,7 @@ describe("O - Tiny game of 5 players in which the pied piper is infected and so, }); }); it("๐Ÿ•บ๏ธ Charmed players meet each other (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "charmed", action: "meet-each-other" }) @@ -108,7 +111,7 @@ describe("O - Tiny game of 5 players in which the pied piper is infected and so, }); it("๐Ÿ‘ช All vote for a villager (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[0]._id, for: players[3]._id }] }) @@ -122,7 +125,7 @@ describe("O - Tiny game of 5 players in which the pied piper is infected and so, }); it("๐Ÿบ Vile father of wolf infects the pied piper (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[1]._id, isInfected: true }] }) @@ -138,7 +141,7 @@ describe("O - Tiny game of 5 players in which the pied piper is infected and so, }); it("๐Ÿ‘ช All vote for the vile father of wolves (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[1]._id, for: players[0]._id }] }) @@ -152,25 +155,25 @@ describe("O - Tiny game of 5 players in which the pied piper is infected and so, }); it("๐ŸŽฒ Game is not won by pied piper even if all alive players are charmed because he is infected", done => { expect(game.waiting[0]).to.deep.equals({ for: "werewolves", to: "eat" }); - expect(game.status).to.equals("playing"); + expect(game.status).to.equal("playing"); expect(game.players.every(({ isAlive, role, attributes }) => !isAlive || role.current === "pied-piper" || attributes?.find(({ name }) => name === "charmed"))).to.be.true; done(); }); it("๐ŸŽฒ Cancels game (PATCH /games/:id)", done => { - chai.request(app) + chai.request(server) .patch(`/games/${game._id}`) .set({ Authorization: `Bearer ${token}` }) .send({ status: "canceled" }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.status).to.equals("canceled"); + expect(game.status).to.equal("canceled"); done(); }); }); it("๐ŸŽฒ Creates game with JWT auth (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players: originalPlayers, options: { roles: { sheriff: { isEnabled: false } } } }) @@ -182,7 +185,7 @@ describe("O - Tiny game of 5 players in which the pied piper is infected and so, }); it("๐Ÿบ Vile father of wolf infects the pied piper (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[1]._id, isInfected: true }] }) @@ -194,7 +197,7 @@ describe("O - Tiny game of 5 players in which the pied piper is infected and so, }); it("๐Ÿ‘ช All vote for the vile father of wolves (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[1]._id, for: players[0]._id }] }) @@ -208,7 +211,7 @@ describe("O - Tiny game of 5 players in which the pied piper is infected and so, }); it("๐Ÿบ Pied Piper eats a villager (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[2]._id }] }) @@ -222,4 +225,120 @@ describe("O - Tiny game of 5 players in which the pied piper is infected and so, expect(game.waiting[0]).to.deep.equals({ for: "all", to: "vote" }); done(); }); + it("๐ŸŽฒ Cancels game (PATCH /games/:id)", done => { + chai.request(server) + .patch(`/games/${game._id}`) + .set({ Authorization: `Bearer ${token}` }) + .send({ status: "canceled" }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + expect(game.status).to.equal("canceled"); + done(); + }); + }); + it("๐ŸŽฒ Creates game with JWT auth where Pied Piper charms 3 people per night and not powerless if infected (can win even infected) (POST /games)", done => { + chai.request(server) + .post("/games") + .set({ Authorization: `Bearer ${token}` }) + .send({ + players: [...originalPlayers, { name: "Dโˆg", role: "villager" }], options: { + roles: { + sheriff: { isEnabled: false }, + piedPiper: { charmedPeopleCountPerNight: 3, isPowerlessIfInfected: false }, + }, + }, + }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + done(); + }); + }); + it("๐Ÿบ Vile father of wolf infects the pied piper (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "werewolves", action: "eat", targets: [{ player: players[1]._id, isInfected: true }] }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + done(); + }); + }); + it("๐Ÿ“ฃ Pied piper charms three villagers (POST /games/:id/play)", done => { + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ + source: "pied-piper", action: "charm", targets: [ + { player: players[2]._id }, + { player: players[3]._id }, + { player: players[4]._id }, + ], + }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + expect(game.players[2].attributes).to.deep.include({ name: "charmed", source: "pied-piper" }); + expect(game.players[3].attributes).to.deep.include({ name: "charmed", source: "pied-piper" }); + expect(game.players[4].attributes).to.deep.include({ name: "charmed", source: "pied-piper" }); + done(); + }); + }); + it("๐Ÿ•บ๏ธ Charmed players meet each other (POST /games/:id/play)", done => { + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "charmed", action: "meet-each-other" }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + done(); + }); + }); + it("๐Ÿ‘ช All vote for the last villager (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "all", action: "vote", votes: [{ from: players[1]._id, for: players[5]._id }] }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + expect(game.players[5].isAlive).to.be.false; + expect(game.players[5].murdered).to.deep.equals({ by: "all", of: "vote" }); + done(); + }); + }); + it("๐Ÿบ Vile father eats one villager (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "werewolves", action: "eat", targets: [{ player: players[2]._id }] }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + done(); + }); + }); + it("๐Ÿ“ฃ Pied piper charms the vile father of wolves (POST /games/:id/play)", done => { + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "pied-piper", action: "charm", targets: [{ player: players[0]._id }] }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + expect(game.players[0].attributes).to.deep.include({ name: "charmed", source: "pied-piper" }); + done(); + }); + }); + it("๐ŸŽฒ Game is won by the pied piper even if he is infected thanks to game option", done => { + expect(game.status).to.equal("done"); + expect(game.won.by).to.equal("pied-piper"); + done(); + }); }); \ No newline at end of file diff --git a/tests/e2e/game/raven-marked-dead-player-game.test.js b/tests/e2e/game/raven-marked-dead-player-game.test.js index 24bd88d..f3e1176 100644 --- a/tests/e2e/game/raven-marked-dead-player-game.test.js +++ b/tests/e2e/game/raven-marked-dead-player-game.test.js @@ -16,13 +16,16 @@ let players = [ { name: "Dog", role: "werewolf" }, { name: "Dล“g", role: "angel" }, ]; -let token, game; +let server, token, game; describe("F - Game where raven marks a player who dies during the night", () => { before(done => resetDatabase(done)); + before(done => { + server = app.listen(3000, done); + }); after(done => resetDatabase(done)); it("๐Ÿ‘ค Creates new user (POST /users)", done => { - chai.request(app) + chai.request(server) .post("/users") .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -32,7 +35,7 @@ describe("F - Game where raven marks a player who dies during the night", () => }); }); it("๐Ÿ”‘ Logs in successfully (POST /users/login)", done => { - chai.request(app) + chai.request(server) .post(`/users/login`) .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -43,7 +46,7 @@ describe("F - Game where raven marks a player who dies during the night", () => }); }); it("๐ŸŽฒ Creates game with JWT auth (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players }) @@ -55,7 +58,7 @@ describe("F - Game where raven marks a player who dies during the night", () => }); it("๐Ÿ‘ช All elect the raven as the sheriff (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -70,16 +73,16 @@ describe("F - Game where raven marks a player who dies during the night", () => game = res.body; expect(game.players[0].attributes).to.deep.include({ name: "sheriff", source: "all" }); expect(game.history[0].play.votes).to.exist; - expect(game.history[0].play.votes[0].from._id).to.equals(game.players[0]._id); - expect(game.history[0].play.votes[0].for._id).to.equals(game.players[1]._id); + expect(game.history[0].play.votes[0].from._id).to.equal(game.players[0]._id); + expect(game.history[0].play.votes[0].for._id).to.equal(game.players[1]._id); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(game.players[0]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(game.players[0]._id); done(); }); }); it("๐Ÿ‘ช All vote for one werewolf (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[0]._id, for: players[2]._id }] }) @@ -87,7 +90,7 @@ describe("F - Game where raven marks a player who dies during the night", () => expect(res).to.have.status(200); game = res.body; expect(game.players[2].isAlive).to.be.false; - expect(game.history[0].play.votesResult).to.equals("death"); + expect(game.history[0].play.votesResult).to.equal("death"); expect(game.history[0].deadPlayers).to.be.an("array").lengthOf(1); expect(game.history[0].deadPlayers[0]._id).to.be.equals(game.players[2]._id); done(); @@ -95,7 +98,7 @@ describe("F - Game where raven marks a player who dies during the night", () => }); it("๐Ÿชถ Raven marks the villager (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "raven", action: "mark", targets: [{ player: players[1]._id }] }) @@ -104,13 +107,13 @@ describe("F - Game where raven marks a player who dies during the night", () => game = res.body; expect(game.players[1].attributes).to.deep.include({ name: "raven-marked", source: "raven", remainingPhases: 2 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[1]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[1]._id); done(); }); }); it("๐Ÿบ Werewolves eat the villager (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[1]._id }] }) @@ -118,19 +121,19 @@ describe("F - Game where raven marks a player who dies during the night", () => expect(res).to.have.status(200); game = res.body; expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[1]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[1]._id); done(); }); }); it("โ˜€๏ธ Sun is rising and villager is dead", done => { - expect(game.phase).to.equals("day"); + expect(game.phase).to.equal("day"); expect(game.players[1].isAlive).to.be.false; expect(game.players[1].attributes).to.not.deep.include({ name: "raven-marked", source: "raven", remainingPhases: 2 }); done(); }); it("๐Ÿ‘ช One vote only for angel, dead villager is not nominated despite the fact he has two votes (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[0]._id, for: players[4]._id }] }) @@ -144,7 +147,7 @@ describe("F - Game where raven marks a player who dies during the night", () => }); }); it("๐ŸŽฒ Game is still playing because angel died from the SECOND vote", done => { - expect(game.status).to.equals("playing"); + expect(game.status).to.equal("playing"); done(); }); }); \ No newline at end of file diff --git a/tests/e2e/game/reset-game.test.js b/tests/e2e/game/reset-game.test.js index 798a8f0..12abb23 100644 --- a/tests/e2e/game/reset-game.test.js +++ b/tests/e2e/game/reset-game.test.js @@ -16,13 +16,16 @@ let players = [ { name: "Deg", role: "werewolf" }, { name: "Dog", role: "villager" }, ]; -let token, game; +let server, token, game; describe("D - Game Reset", () => { before(done => resetDatabase(done)); + before(done => { + server = app.listen(3000, done); + }); after(done => resetDatabase(done)); it("๐Ÿ‘ค Creates new user (POST /users)", done => { - chai.request(app) + chai.request(server) .post("/users") .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -32,7 +35,7 @@ describe("D - Game Reset", () => { }); }); it("๐Ÿ”‘ Logs in successfully (POST /users/login)", done => { - chai.request(app) + chai.request(server) .post(`/users/login`) .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -43,7 +46,7 @@ describe("D - Game Reset", () => { }); }); it("๐ŸŽฒ Creates game with JWT auth (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players }) @@ -54,19 +57,19 @@ describe("D - Game Reset", () => { }); }); it("๐Ÿ” Can't make a play if game's doesn't belong to user (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${new mongoose.Types.ObjectId()}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "elect-sheriff" }) .end((err, res) => { expect(res).to.have.status(401); - expect(res.body.type).to.equals("GAME_DOESNT_BELONG_TO_USER"); + expect(res.body.type).to.equal("GAME_DOESNT_BELONG_TO_USER"); done(); }); }); it("๐Ÿ‘ช All elect the villager as the sheriff (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -83,33 +86,33 @@ describe("D - Game Reset", () => { }); }); it("โ™ป๏ธ Game is resetting (PATCH /games/:id/reset)", done => { - chai.request(app) + chai.request(server) .patch(`/games/${game._id}/reset`) .set({ Authorization: `Bearer ${token}` }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.status).to.equals("playing"); - expect(game.turn).to.equals(1); - expect(game.phase).to.equals("night"); - expect(game.tick).to.equals(1); + expect(game.status).to.equal("playing"); + expect(game.turn).to.equal(1); + expect(game.phase).to.equal("night"); + expect(game.tick).to.equal(1); expect(game.waiting[0]).to.deep.equals({ for: "all", to: "elect-sheriff" }); expect(game.history).to.deep.equals([]); expect(Array.isArray(game.players)).to.be.true; expect(game.players[0].role).to.deep.equals({ original: "werewolf", current: "werewolf", isRevealed: false }); - expect(game.players[0].side.current).to.equals("werewolves"); + expect(game.players[0].side.current).to.equal("werewolves"); expect(game.players[1].role).to.deep.equals({ original: "werewolf", current: "werewolf", isRevealed: false }); - expect(game.players[1].side.current).to.equals("werewolves"); + expect(game.players[1].side.current).to.equal("werewolves"); expect(game.players[2].role).to.deep.equals({ original: "werewolf", current: "werewolf", isRevealed: false }); - expect(game.players[2].side.current).to.equals("werewolves"); + expect(game.players[2].side.current).to.equal("werewolves"); expect(game.players[3].role).to.deep.equals({ original: "villager", current: "villager", isRevealed: false }); - expect(game.players[3].side.current).to.equals("villagers"); + expect(game.players[3].side.current).to.equal("villagers"); done(); }); }); it("๐Ÿ‘ช All elect the villager as the sheriff (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -127,7 +130,7 @@ describe("D - Game Reset", () => { }); it("๐Ÿบ Werewolves eat the villager (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[3]._id }] }) @@ -138,17 +141,17 @@ describe("D - Game Reset", () => { }); }); it("๐ŸŽฒ Game is WON by 'werewolves'!!", done => { - expect(game.status).to.equals("done"); - expect(game.won.by).to.equals("werewolves"); + expect(game.status).to.equal("done"); + expect(game.won.by).to.equal("werewolves"); done(); }); it("๐Ÿ” Game can't be reset if status is 'done' (PATCH /games/:id/reset)", done => { - chai.request(app) + chai.request(server) .patch(`/games/${game._id}/reset`) .set({ Authorization: `Bearer ${token}` }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("CANT_BE_RESET"); + expect(res.body.type).to.equal("CANT_BE_RESET"); done(); }); }); diff --git a/tests/e2e/game/thief-steals.test.js b/tests/e2e/game/thief-steals.test.js index 9c2dc1f..84d5fcf 100644 --- a/tests/e2e/game/thief-steals.test.js +++ b/tests/e2e/game/thief-steals.test.js @@ -16,13 +16,16 @@ const originalPlayers = [ { name: "Dog", role: "villager" }, ]; const options = { roles: { sheriff: { isEnabled: false } } }; -let token, game, additionalCards; +let server, token, game, additionalCards; describe("U - Tiny game of 4 players in which thief steals different roles", () => { before(done => resetDatabase(done)); + before(done => { + server = app.listen(3000, done); + }); after(done => resetDatabase(done)); it("๐Ÿ‘ค Creates new user (POST /users)", done => { - chai.request(app) + chai.request(server) .post("/users") .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -32,7 +35,7 @@ describe("U - Tiny game of 4 players in which thief steals different roles", () }); }); it("๐Ÿ”‘ Logs in successfully (POST /users/login)", done => { - chai.request(app) + chai.request(server) .post(`/users/login`) .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -43,7 +46,7 @@ describe("U - Tiny game of 4 players in which thief steals different roles", () }); }); it("๐ŸŽฒ Creates game with JWT auth (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ @@ -60,7 +63,7 @@ describe("U - Tiny game of 4 players in which thief steals different roles", () }); }); it("๐Ÿฆน Thief chooses the angel card (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "thief", action: "choose-card", card: additionalCards[0]._id }) @@ -68,8 +71,8 @@ describe("U - Tiny game of 4 players in which thief steals different roles", () expect(res).to.have.status(200); game = res.body; additionalCards = game.additionalCards; - expect(game.players[0].role.current).to.equals("angel"); - expect(game.players[0].side.current).to.equals("villagers"); + expect(game.players[0].role.current).to.equal("angel"); + expect(game.players[0].side.current).to.equal("villagers"); expect(game.history[0].play.card).to.deep.equals(additionalCards[0]); expect(game.waiting).to.be.an("array").lengthOf(2); expect(game.waiting[0]).to.deep.equals({ for: "all", to: "vote" }); @@ -78,19 +81,19 @@ describe("U - Tiny game of 4 players in which thief steals different roles", () }); }); it("๐ŸŽฒ Cancels game (PATCH /games/:id)", done => { - chai.request(app) + chai.request(server) .patch(`/games/${game._id}`) .set({ Authorization: `Bearer ${token}` }) .send({ status: "canceled" }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.status).to.equals("canceled"); + expect(game.status).to.equal("canceled"); done(); }); }); it("๐ŸŽฒ Creates game with JWT auth (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ @@ -107,7 +110,7 @@ describe("U - Tiny game of 4 players in which thief steals different roles", () }); }); it("๐Ÿฆน Thief chooses the witch card (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "thief", action: "choose-card", card: additionalCards[0]._id }) @@ -115,8 +118,8 @@ describe("U - Tiny game of 4 players in which thief steals different roles", () expect(res).to.have.status(200); game = res.body; additionalCards = game.additionalCards; - expect(game.players[0].role.current).to.equals("witch"); - expect(game.players[0].side.current).to.equals("villagers"); + expect(game.players[0].role.current).to.equal("witch"); + expect(game.players[0].side.current).to.equal("villagers"); expect(game.history[0].play.card).to.deep.equals(additionalCards[0]); expect(game.waiting).to.be.an("array").lengthOf(2); expect(game.waiting[0]).to.deep.equals({ for: "werewolves", to: "eat" }); diff --git a/tests/e2e/game/tiny-game.test.js b/tests/e2e/game/tiny-game.test.js index 03e45ff..113b221 100644 --- a/tests/e2e/game/tiny-game.test.js +++ b/tests/e2e/game/tiny-game.test.js @@ -20,13 +20,16 @@ const additionalCards = [ { for: "thief", role: "witch" }, { for: "thief", role: "seer" }, ]; -let token, game; +let server, token, game; describe("C - Tiny game of 4 players with only werewolves and one poor thief who skips", () => { before(done => resetDatabase(done)); + before(done => { + server = app.listen(3000, done); + }); after(done => resetDatabase(done)); it("๐Ÿ‘ค Creates new user (POST /users)", done => { - chai.request(app) + chai.request(server) .post("/users") .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -36,7 +39,7 @@ describe("C - Tiny game of 4 players with only werewolves and one poor thief who }); }); it("๐Ÿ”‘ Logs in successfully (POST /users/login)", done => { - chai.request(app) + chai.request(server) .post(`/users/login`) .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -47,7 +50,7 @@ describe("C - Tiny game of 4 players with only werewolves and one poor thief who }); }); it("๐ŸŽฒ Creates game with JWT auth (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) .send({ players, additionalCards }) @@ -58,19 +61,19 @@ describe("C - Tiny game of 4 players with only werewolves and one poor thief who }); }); it("๐Ÿ” Can't make a play if game's doesn't belong to user (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${new mongoose.Types.ObjectId()}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "elect-sheriff" }) .end((err, res) => { expect(res).to.have.status(401); - expect(res.body.type).to.equals("GAME_DOESNT_BELONG_TO_USER"); + expect(res.body.type).to.equal("GAME_DOESNT_BELONG_TO_USER"); done(); }); }); it("๐Ÿ‘ช All elect the villager as the sheriff (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ @@ -88,22 +91,22 @@ describe("C - Tiny game of 4 players with only werewolves and one poor thief who }); it("๐Ÿฆน๏ธ Thief skips his turn (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "thief", action: "choose-card" }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; - expect(game.players[3].role.current).to.equals("thief"); - expect(game.players[3].side.current).to.equals("villagers"); + expect(game.players[3].role.current).to.equal("thief"); + expect(game.players[3].side.current).to.equal("villagers"); expect(game.history[0].play.card).to.not.exist; done(); }); }); it("๐Ÿบ Werewolves eat the villager (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[3]._id }] }) @@ -114,18 +117,18 @@ describe("C - Tiny game of 4 players with only werewolves and one poor thief who }); }); it("๐Ÿบ Big bad wolf can't eat a target because there is no one left (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "big-bad-wolf", action: "eat", targets: [{ player: players[3]._id }] }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_TARGETS_LENGTH"); + expect(res.body.type).to.equal("BAD_TARGETS_LENGTH"); done(); }); }); it("๐Ÿบ Big bad wolf skips (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "big-bad-wolf", action: "eat" }) @@ -136,8 +139,8 @@ describe("C - Tiny game of 4 players with only werewolves and one poor thief who }); }); it("๐ŸŽฒ Game is WON by 'werewolves'!!", done => { - expect(game.status).to.equals("done"); - expect(game.won.by).to.equals("werewolves"); + expect(game.status).to.equal("done"); + expect(game.won.by).to.equal("werewolves"); done(); }); }); diff --git a/tests/e2e/game/unkillable-ancient.test.js b/tests/e2e/game/unkillable-ancient.test.js index 5debb0c..b8b5f83 100644 --- a/tests/e2e/game/unkillable-ancient.test.js +++ b/tests/e2e/game/unkillable-ancient.test.js @@ -9,7 +9,7 @@ chai.use(chaiHttp); const { expect } = chai; const credentials = { email: "test@test.fr", password: "secret" }; -let players = [ +const originalPlayers = [ { name: "Dig", role: "witch" }, { name: "Dag", role: "guard" }, { name: "Dog", role: "werewolf" }, @@ -17,13 +17,16 @@ let players = [ { name: "Dรดg", role: "scapegoat" }, { name: "Dโˆ‚g", role: "idiot" }, ]; -let token, game; +let server, token, game, players; describe("P - Game with an ancient who survives from 3 werewolves attacks", () => { before(done => resetDatabase(done)); + before(done => { + server = app.listen(3000, done); + }); after(done => resetDatabase(done)); it("๐Ÿ‘ค Creates new user (POST /users)", done => { - chai.request(app) + chai.request(server) .post("/users") .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -33,7 +36,7 @@ describe("P - Game with an ancient who survives from 3 werewolves attacks", () = }); }); it("๐Ÿ”‘ Logs in successfully (POST /users/login)", done => { - chai.request(app) + chai.request(server) .post(`/users/login`) .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .send(credentials) @@ -44,10 +47,10 @@ describe("P - Game with an ancient who survives from 3 werewolves attacks", () = }); }); it("๐ŸŽฒ Creates game with JWT auth (POST /games)", done => { - chai.request(app) + chai.request(server) .post("/games") .set({ Authorization: `Bearer ${token}` }) - .send({ players, options: { roles: { sheriff: { isEnabled: false } } } }) + .send({ players: originalPlayers, options: { roles: { sheriff: { isEnabled: false } } } }) .end((err, res) => { expect(res).to.have.status(200); game = res.body; @@ -56,12 +59,12 @@ describe("P - Game with an ancient who survives from 3 werewolves attacks", () = }); it("๐ŸŒ™ Night falls", done => { players = game.players; - expect(game.phase).to.equals("night"); + expect(game.phase).to.equal("night"); done(); }); it("๐Ÿ›ก Guard protects himself (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "guard", action: "protect", targets: [{ player: players[1]._id }] }) @@ -70,13 +73,13 @@ describe("P - Game with an ancient who survives from 3 werewolves attacks", () = game = res.body; expect(game.players[1].attributes).to.deep.include({ name: "protected", source: "guard", remainingPhases: 1 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[1]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[1]._id); done(); }); }); it("๐Ÿบ Werewolf eats the ancient (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[3]._id }] }) @@ -84,14 +87,14 @@ describe("P - Game with an ancient who survives from 3 werewolves attacks", () = expect(res).to.have.status(200); game = res.body; expect(game.players[3].attributes).to.deep.include({ name: "eaten", source: "werewolves", remainingPhases: 1 }); - expect(game.players[3].side.current).to.equals("villagers"); + expect(game.players[3].side.current).to.equal("villagers"); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[3]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[3]._id); done(); }); }); it("๐Ÿช„ Witch uses life potion on ancient (POST /games/:id/play)", done => { - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "witch", action: "use-potion", targets: [{ player: players[3]._id, hasDrankLifePotion: true }] }) @@ -102,26 +105,26 @@ describe("P - Game with an ancient who survives from 3 werewolves attacks", () = }); }); it("โ˜€๏ธ Sun is rising and ancient is alive because witch saved him", done => { - expect(game.phase).to.equals("day"); + expect(game.phase).to.equal("day"); expect(game.players[3].isAlive).to.be.true; expect(game.players[3].role.isRevealed).to.be.false; done(); }); it("โš–๏ธ Stuttering judge can't request another vote if he is absent from the game (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[1]._id, for: players[0]._id }], doesJudgeRequestAnotherVote: true }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("STUTTERING_JUDGE_ABSENT"); + expect(res.body.type).to.equal("STUTTERING_JUDGE_ABSENT"); done(); }); }); it("๐Ÿ‘ช All vote for the witch (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[1]._id, for: players[0]._id }] }) @@ -135,7 +138,7 @@ describe("P - Game with an ancient who survives from 3 werewolves attacks", () = }); it("๐Ÿ›ก Guard protects the ancient (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "guard", action: "protect", targets: [{ player: players[3]._id }] }) @@ -144,13 +147,13 @@ describe("P - Game with an ancient who survives from 3 werewolves attacks", () = game = res.body; expect(game.players[3].attributes).to.deep.include({ name: "protected", source: "guard", remainingPhases: 1 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[3]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[3]._id); done(); }); }); it("๐Ÿบ Werewolf eats the ancient again (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[3]._id }] }) @@ -158,19 +161,19 @@ describe("P - Game with an ancient who survives from 3 werewolves attacks", () = expect(res).to.have.status(200); game = res.body; expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[3]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[3]._id); done(); }); }); it("โ˜€๏ธ Sun is rising and ancient is alive because guard saved him", done => { - expect(game.phase).to.equals("day"); + expect(game.phase).to.equal("day"); expect(game.players[3].isAlive).to.be.true; expect(game.players[3].role.isRevealed).to.be.false; done(); }); it("๐Ÿ‘ช All vote for the idiot, which is only revealed (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[2]._id, for: players[5]._id }] }) @@ -184,7 +187,7 @@ describe("P - Game with an ancient who survives from 3 werewolves attacks", () = }); it("๐Ÿ›ก Guard protects himself (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "guard", action: "protect", targets: [{ player: players[1]._id }] }) @@ -193,13 +196,13 @@ describe("P - Game with an ancient who survives from 3 werewolves attacks", () = game = res.body; expect(game.players[1].attributes).to.deep.include({ name: "protected", source: "guard", remainingPhases: 1 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[1]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[1]._id); done(); }); }); it("๐Ÿบ Werewolf eats the ancient again, again (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[3]._id }] }) @@ -207,19 +210,19 @@ describe("P - Game with an ancient who survives from 3 werewolves attacks", () = expect(res).to.have.status(200); game = res.body; expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[3]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[3]._id); done(); }); }); it("โ˜€๏ธ Sun is rising and ancient is alive because he has a second life", done => { - expect(game.phase).to.equals("day"); + expect(game.phase).to.equal("day"); expect(game.players[3].isAlive).to.be.true; expect(game.players[3].role.isRevealed).to.be.false; done(); }); it("๐Ÿ‘ช All vote for the scapegoat (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "all", action: "vote", votes: [{ from: players[2]._id, for: players[4]._id }] }) @@ -233,7 +236,7 @@ describe("P - Game with an ancient who survives from 3 werewolves attacks", () = }); it("๐Ÿ›ก Guard protects the idiot (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "guard", action: "protect", targets: [{ player: players[5]._id }] }) @@ -242,13 +245,13 @@ describe("P - Game with an ancient who survives from 3 werewolves attacks", () = game = res.body; expect(game.players[5].attributes).to.deep.include({ name: "protected", source: "guard", remainingPhases: 1 }); expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[5]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[5]._id); done(); }); }); it("๐Ÿบ Werewolf eats the ancient again, again and again ! (POST /games/:id/play)", done => { players = game.players; - chai.request(app) + chai.request(server) .post(`/games/${game._id}/play`) .set({ Authorization: `Bearer ${token}` }) .send({ source: "werewolves", action: "eat", targets: [{ player: players[3]._id }] }) @@ -256,16 +259,104 @@ describe("P - Game with an ancient who survives from 3 werewolves attacks", () = expect(res).to.have.status(200); game = res.body; expect(game.history[0].play.targets).to.exist; - expect(game.history[0].play.targets[0].player._id).to.equals(players[3]._id); + expect(game.history[0].play.targets[0].player._id).to.equal(players[3]._id); done(); }); }); it("โ˜€๏ธ Sun is rising and ancient is finally dead and idiot too because he was already revealed", done => { - expect(game.phase).to.equals("day"); + expect(game.phase).to.equal("day"); expect(game.players[3].isAlive).to.be.false; expect(game.players[3].role.isRevealed).to.be.true; expect(game.players[5].isAlive).to.be.false; expect(game.players[5].murdered).to.deep.equals({ by: "all", of: "reconsider" }); done(); }); + it("๐ŸŽฒ Cancels game (PATCH /games/:id)", done => { + chai.request(server) + .patch(`/games/${game._id}`) + .set({ Authorization: `Bearer ${token}` }) + .send({ status: "canceled" }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + expect(game.status).to.equal("canceled"); + done(); + }); + }); + it("๐ŸŽฒ Creates game with JWT auth with ancient with only one life against werewolves and doesn't take his revenge (POST /games)", done => { + chai.request(server) + .post("/games") + .set({ Authorization: `Bearer ${token}` }) + .send({ + players: originalPlayers, options: { + roles: { + sheriff: { isEnabled: false }, + ancient: { livesCountAgainstWerewolves: 1, doesTakeHisRevenge: false }, + }, + }, + }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + expect(game.options.roles.ancient.livesCountAgainstWerewolves).to.equal(1); + expect(game.options.roles.ancient.doesTakeHisRevenge).to.be.false; + done(); + }); + }); + it("๐ŸŒ™ Night falls", done => { + players = game.players; + expect(game.phase).to.equal("night"); + done(); + }); + it("๐Ÿ›ก Guard protects himself (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "guard", action: "protect", targets: [{ player: players[1]._id }] }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + expect(game.players[1].attributes).to.deep.include({ name: "protected", source: "guard", remainingPhases: 1 }); + expect(game.history[0].play.targets).to.exist; + expect(game.history[0].play.targets[0].player._id).to.equal(players[1]._id); + done(); + }); + }); + it("๐Ÿบ Werewolf eats the ancient (POST /games/:id/play)", done => { + players = game.players; + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "werewolves", action: "eat", targets: [{ player: players[3]._id }] }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + expect(game.players[3].attributes).to.deep.include({ name: "eaten", source: "werewolves", remainingPhases: 1 }); + expect(game.players[3].side.current).to.equal("villagers"); + expect(game.history[0].play.targets).to.exist; + expect(game.history[0].play.targets[0].player._id).to.equal(players[3]._id); + done(); + }); + }); + it("๐Ÿช„ Witch skips (POST /games/:id/play)", done => { + chai.request(server) + .post(`/games/${game._id}/play`) + .set({ Authorization: `Bearer ${token}` }) + .send({ source: "witch", action: "use-potion" }) + .end((err, res) => { + expect(res).to.have.status(200); + game = res.body; + done(); + }); + }); + it("โ˜€๏ธ Sun is rising and ancient is dead because he has only one life against werewolves and no villagers are powerless", done => { + expect(game.phase).to.equal("day"); + expect(game.players[3].isAlive).to.be.false; + expect(game.players[0].attributes).to.not.exist; + expect(game.players[1].attributes).to.be.an("array").lengthOf(0); + expect(game.players[4].attributes).to.not.exist; + expect(game.players[5].attributes).to.not.exist; + done(); + }); }); \ No newline at end of file diff --git a/tests/e2e/role/get-roles.test.js b/tests/e2e/role/get-roles.test.js index fd44b45..6d51953 100644 --- a/tests/e2e/role/get-roles.test.js +++ b/tests/e2e/role/get-roles.test.js @@ -6,12 +6,16 @@ const { resetDatabase } = require("../../../src/helpers/functions/Test"); chai.use(chaiHttp); const { expect } = chai; +let server; describe("A - Get roles", () => { before(done => resetDatabase(done)); + before(done => { + server = app.listen(3000, done); + }); after(done => resetDatabase(done)); it("๐ŸŽฒ Gets roles (GET /roles)", done => { - chai.request(app) + chai.request(server) .get("/roles") .end((err, res) => { expect(res).to.have.status(200); @@ -19,6 +23,7 @@ describe("A - Get roles", () => { expect(res.body[0].name).to.exist; expect(res.body[0].side).to.exist; expect(res.body[0].maxInGame).to.exist; + expect(res.body[0].type).to.exist; done(); }); }); diff --git a/tests/e2e/user/user-registration.test.js b/tests/e2e/user/user-registration.test.js index d6fc36a..465dbab 100644 --- a/tests/e2e/user/user-registration.test.js +++ b/tests/e2e/user/user-registration.test.js @@ -9,53 +9,56 @@ chai.use(chaiHttp); const { expect } = chai; const credentials = { email: "test@test.fr", password: "secret" }; -let user, token, user2; +let server, user, token, user2; describe("A - Sign up and log in", () => { before(done => resetDatabase(done)); + before(done => { + server = app.listen(3000, done); + }); after(done => resetDatabase(done)); it("๐Ÿ“ง Doesn't allow bad email (POST /users)", done => { - chai.request(app) + chai.request(server) .post("/users") .send({ email: "foobar", password: "secret" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_REQUEST"); + expect(res.body.type).to.equal("BAD_REQUEST"); done(); }); }); it("๐Ÿ“ง Doesn't allow email longer than 50 characters (POST /users)", done => { - chai.request(app) + chai.request(server) .post("/users") .send({ email: "foobar@foooooooooooooooooooooooooooooooooooobar.com", password: "secret" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_REQUEST"); + expect(res.body.type).to.equal("BAD_REQUEST"); done(); }); }); it("๐Ÿ” Doesn't allow password shorter than 5 characters (POST /users)", done => { - chai.request(app) + chai.request(server) .post("/users") .send({ email: "foobar@lol.com", password: "lol" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_REQUEST"); + expect(res.body.type).to.equal("BAD_REQUEST"); done(); }); }); it("๐Ÿ” Doesn't allow password longer than 50 characters (POST /users)", done => { - chai.request(app) + chai.request(server) .post("/users") .send({ email: "foobar@lol.com", password: "IamASuperLongPasswordHowAreYouDoing?ReviewingCodeIsFun!" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_REQUEST"); + expect(res.body.type).to.equal("BAD_REQUEST"); done(); }); }); it("๐Ÿ‘ค Creates new user (POST /users)", done => { - chai.request(app) + chai.request(server) .post("/users") .send(credentials) .end((err, res) => { @@ -65,67 +68,68 @@ describe("A - Sign up and log in", () => { }); }); it("๐Ÿ“ง Doesn't allow duplicate email (POST /users)", done => { - chai.request(app) + chai.request(server) .post("/users") .send(credentials) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("EMAIL_EXISTS"); + expect(res.body.type).to.equal("EMAIL_EXISTS"); done(); }); }); it("๐Ÿ‘ค Gets freshly created user with Basic auth (GET /users/:id)", done => { - chai.request(app) + chai.request(server) .get(`/users/${user._id}`) .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .end((err, res) => { expect(res).to.have.status(200); - expect(res.body.email).to.equals(credentials.email); + expect(res.body.email).to.equal(credentials.email); + expect(res.body.registration.method).to.equal("local"); done(); }); }); it("๐Ÿ“ง Can't log in with a too long email address (POST /users/login)", done => { - chai.request(app) + chai.request(server) .post(`/users/login`) .send({ email: "foobar@foooooooooooooooooooooooooooooooooooobar.com", password: "secret" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_REQUEST"); + expect(res.body.type).to.equal("BAD_REQUEST"); done(); }); }); it("๐Ÿ” Can't log in with a too small password (POST /users/login)", done => { - chai.request(app) + chai.request(server) .post(`/users/login`) .send({ email: "foobar@lol.com", password: "lol" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_REQUEST"); + expect(res.body.type).to.equal("BAD_REQUEST"); done(); }); }); it("๐Ÿ” Can't log in with a too long password (POST /users/login)", done => { - chai.request(app) + chai.request(server) .post(`/users/login`) .send({ email: "foobar@lol.com", password: "IamASuperLongPasswordHowAreYouDoing?ReviewingCodeIsFun!" }) .end((err, res) => { expect(res).to.have.status(400); - expect(res.body.type).to.equals("BAD_REQUEST"); + expect(res.body.type).to.equal("BAD_REQUEST"); done(); }); }); it("๐Ÿ” Doesn't allow bad credentials (POST /users/login)", done => { - chai.request(app) + chai.request(server) .post(`/users/login`) .send({ email: "foo@bar.com", password: "secret" }) .end((err, res) => { expect(res).to.have.status(401); - expect(res.body.type).to.equals("BAD_CREDENTIALS"); + expect(res.body.type).to.equal("BAD_CREDENTIALS"); done(); }); }); it("๐Ÿ”‘ Logs in successfully (POST /users/login)", done => { - chai.request(app) + chai.request(server) .post(`/users/login`) .send(credentials) .end((err, res) => { @@ -136,17 +140,17 @@ describe("A - Sign up and log in", () => { }); }); it("๐Ÿ‘ค Gets freshly created user with JWT auth (GET /users/:id)", done => { - chai.request(app) + chai.request(server) .get(`/users/${user._id}`) .set({ Authorization: `Bearer ${token}` }) .end((err, res) => { expect(res).to.have.status(200); - expect(res.body.email).to.equals(credentials.email); + expect(res.body.email).to.equal(credentials.email); done(); }); }); it("๐Ÿ” Doesn't allow to get user without auth (GET /users/:id)", done => { - chai.request(app) + chai.request(server) .get(`/users/${user._id}`) .end((err, res) => { expect(res).to.have.status(401); @@ -154,7 +158,7 @@ describe("A - Sign up and log in", () => { }); }); it("๐Ÿ‘ค Creates a second user (POST /users)", done => { - chai.request(app) + chai.request(server) .post("/users") .send({ email: `${credentials.email}bis`, password: credentials.password }) .end((err, res) => { @@ -164,17 +168,17 @@ describe("A - Sign up and log in", () => { }); }); it("๐Ÿ” Doesn't allow to get user without the right token (GET /users/:id)", done => { - chai.request(app) + chai.request(server) .get(`/users/${user2._id}`) .set({ Authorization: `Bearer ${token}` }) .end((err, res) => { expect(res).to.have.status(401); - expect(res.body.type).to.equals("UNAUTHORIZED"); + expect(res.body.type).to.equal("UNAUTHORIZED"); done(); }); }); it("๐Ÿ‘ค Get users with only _id and email in response (GET /users?fields=email,_id)", done => { - chai.request(app) + chai.request(server) .get(`/users?fields=email,_id`) .auth(Config.app.basicAuth.username, Config.app.basicAuth.password) .end((err, res) => { @@ -186,4 +190,24 @@ describe("A - Sign up and log in", () => { done(); }); }); + it("๐Ÿ” Can't login with Facebook with bad access token (POST /users/login/facebook)", done => { + chai.request(server) + .post(`/users/login/facebook`) + .send({ accessToken: "lol" }) + .end((err, res) => { + expect(res).to.have.status(400); + expect(res.body.type).to.equal("BAD_FACEBOOK_ACCESS_TOKEN"); + done(); + }); + }); + it("๐Ÿ” Can't login with Google with bad id token (POST /users/login/facebook)", done => { + chai.request(server) + .post(`/users/login/google`) + .send({ idToken: "lol" }) + .end((err, res) => { + expect(res).to.have.status(400); + expect(res.body.type).to.equal("BAD_GOOGLE_ID_TOKEN"); + done(); + }); + }); }); \ No newline at end of file diff --git a/tests/units/game/get-nearest-neighbor.test.js b/tests/units/game/get-nearest-neighbor.test.js new file mode 100644 index 0000000..fb11cde --- /dev/null +++ b/tests/units/game/get-nearest-neighbor.test.js @@ -0,0 +1,68 @@ +const mongoose = require("mongoose"); +const { describe, it } = require("mocha"); +const { expect } = require("chai"); +const { getNearestNeighbor } = require("../../../src/helpers/functions/Game"); +const players = [ + { _id: new mongoose.Types.ObjectId(), position: 0, isAlive: true, side: { current: "villagers" } }, + { _id: new mongoose.Types.ObjectId(), position: 1, isAlive: true, side: { current: "villagers" } }, + { _id: new mongoose.Types.ObjectId(), position: 2, isAlive: true, side: { current: "villagers" } }, + { _id: new mongoose.Types.ObjectId(), position: 3, isAlive: true, side: { current: "villagers" } }, + { _id: new mongoose.Types.ObjectId(), position: 4, isAlive: true, side: { current: "werewolves" } }, +]; + +describe("A - Get nearest neighbor", () => { + it("Get first alive third player's neighbor on the left", done => { + const foundNeighbor = getNearestNeighbor(players[2]._id, players, "left", { isAlive: true }); + expect(foundNeighbor._id.toString()).to.equal(players[3]._id.toString()); + expect(foundNeighbor.isAlive).to.be.true; + done(); + }); + it("Get first alive third player's neighbor on the right", done => { + const foundNeighbor = getNearestNeighbor(players[2]._id, players, "right", { isAlive: true }); + expect(foundNeighbor._id.toString()).to.equal(players[1]._id.toString()); + expect(foundNeighbor.isAlive).to.be.true; + players[1].isAlive = false; + players[3].isAlive = false; + done(); + }); + it("Get first alive third player's neighbor on the left, first left neighbor is dead", done => { + const foundNeighbor = getNearestNeighbor(players[2]._id, players, "left", { isAlive: true }); + expect(foundNeighbor._id.toString()).to.equal(players[4]._id.toString()); + expect(foundNeighbor.isAlive).to.be.true; + done(); + }); + it("Get first alive third player's neighbor on the right, first right neighbor is dead", done => { + const foundNeighbor = getNearestNeighbor(players[2]._id, players, "right", { isAlive: true }); + expect(foundNeighbor._id.toString()).to.equal(players[0]._id.toString()); + expect(foundNeighbor.isAlive).to.be.true; + players[4].isAlive = false; + done(); + }); + it("Get first alive third player's neighbor on the left, first and second left neighbors are dead", done => { + const foundNeighbor = getNearestNeighbor(players[2]._id, players, "left", { isAlive: true }); + expect(foundNeighbor._id.toString()).to.equal(players[0]._id.toString()); + expect(foundNeighbor.isAlive).to.be.true; + players[4].isAlive = true; + players[0].isAlive = false; + done(); + }); + it("Get first alive third player's neighbor on the right, first and second right neighbors are dead", done => { + const foundNeighbor = getNearestNeighbor(players[2]._id, players, "right", { isAlive: true }); + expect(foundNeighbor._id.toString()).to.equal(players[4]._id.toString()); + expect(foundNeighbor.isAlive).to.be.true; + players[4].isAlive = false; + done(); + }); + it("Get first alive third player's neighbor on the right, but none is alive", done => { + const foundNeighbor = getNearestNeighbor(players[2]._id, players, "right", { isAlive: true }); + expect(foundNeighbor).to.equal(null); + players[4].isAlive = true; + done(); + }); + it("Get first alive third player's werewolf neighbor on the right, first and second right neighbors are dead", done => { + const foundNeighbor = getNearestNeighbor(players[2]._id, players, "right", { isAlive: true, side: "werewolves" }); + expect(foundNeighbor._id.toString()).to.equal(players[4]._id.toString()); + expect(foundNeighbor.side.current).to.equal("werewolves"); + done(); + }); +}); \ No newline at end of file diff --git a/tests/units/game/index.js b/tests/units/game/index.js new file mode 100644 index 0000000..3f80476 --- /dev/null +++ b/tests/units/game/index.js @@ -0,0 +1,5 @@ +const { describe } = require("mocha"); + +describe("Unit - ๐ŸŽฒ Game tests", () => { + require("./get-nearest-neighbor.test"); +}); \ No newline at end of file diff --git a/tests/units/index.js b/tests/units/index.js index e69de29..79b96c7 100644 --- a/tests/units/index.js +++ b/tests/units/index.js @@ -0,0 +1 @@ +require("./game"); \ No newline at end of file