Newer
Older
Labyrinth / src / GameServer.spec.ts
// to get rid of "[ts] could not find module ..." in vs code realod the window: f1 -> type "reload window"
// official issue for this: https://github.com/Microsoft/TypeScript/issues/10346

import {expect} from "chai";
import {GameServer} from "./GameServer";
import { Direction, MazeMap, MazeTile } from "./MazeMap";
// import { describe, it } from "mocha";

function createTrivialMap(): MazeMap
{
    const maze = new MazeMap("lvl1");
    let tile = new MazeTile("1");
    tile.paths.push(Direction.right);
    tile.paths.push(Direction.bottom);
    maze.layout.push([tile]);
    tile = new MazeTile("2");
    tile.paths.push(Direction.left);
    tile.paths.push(Direction.bottom);
    maze.layout[0].push(tile);

    tile = new MazeTile("3");
    tile.paths.push(Direction.right);
    tile.paths.push(Direction.top);
    maze.layout.push([tile]);
    tile = new MazeTile("4");
    tile.paths.push(Direction.left);
    tile.paths.push(Direction.top);
    maze.layout[1].push(tile);

    maze.startPosition = [0,0];

    return maze;
}

function createGameServer(): GameServer
{
    const server = new GameServer();
    server.addMap(createTrivialMap());
    return server;
}

describe("Scenario: Accessing current position", function(){
    const userID = "UserID";
    const mapID = "lvl1";
    describe("When a game has been started for a player", function(){
        const games = createGameServer();
        games.startGame(userID, mapID);
        it("then the position must not be null", function(){
            const position = games.currentPosition(userID);
            expect(position).to.be.not.null;
        });
    });

    describe("When no game for player has been started", function(){
        const games = createGameServer();
        it("then null should be returned for that player", function(){
            const position = games.currentPosition(userID);
            expect(position).to.be.null;
        });
    });

    describe("When userID is null", function(){
        const games = createGameServer();
        it("then it should throw an exception", function(){
            function expectedToFail(){
                games.currentPosition(null);
            }
            expect(expectedToFail).to.throw(/.*userid.*/i);
        });
    });

    describe("When userID is undefined", function(){
        const games = createGameServer();
        it("then it should throw an exception", function(){
            function expectedToFail(){
                games.currentPosition(undefined);
            }
            expect(expectedToFail).to.throw(/.*userid.*/i);
        });
    });

    describe("When userID is an empty string", function(){
        const games = createGameServer();
        it("then it should throw an exception", function(){
            function expectedToFail(){
                games.currentPosition("");
            }
            expect(expectedToFail).to.throw(/.*userid.*empty.*/i);
        });
    });

    describe("When userID is a whitespace string", function(){
        const games = createGameServer();
        it("then it should throw an exception", function(){
            function expectedToFail(){
                games.currentPosition("  ");
            }
            expect(expectedToFail).to.throw(/.*userid.*empty.*/i);
        });
    });
});

describe("Scenario: Starting the game", function(){
    const userID = "UserID";
    const mapID = "lvl1";
    describe("When a user starts an existing valid level", function(){
        const games = createGameServer();
        games.startGame(userID, mapID);

        it("then the start position must be remembered for the user", function(){
            const current = games.currentPosition(userID);
            expect(current, "Position object is null.").to.be.not.null;
            expect(current.id, "Id of the tile is null.").to.be.not.null;
        });
    });

    describe("When the supplied userID is null", function(){
        const games = createGameServer();
        it("then it should throw an exception", function(){
            function expectedToFail(){
                games.startGame(null, mapID);
            }
            expect(expectedToFail).to.throw(/.*userid.*/i);
        });
    });

    describe("When the supplied userID is undefined", function(){
        const games = createGameServer();
        it("then it should throw an exception", function(){
            function expectedToFail(){
                games.startGame(undefined, mapID);
            }
            expect(expectedToFail).to.throw(/.*userid.*/i);
        });
    });

    describe("When the supplied userID is an empty string", function(){
        const games = createGameServer();
        it("then it should throw an exception", function(){
            function expectedToFail(){
                games.startGame("", mapID);
            }
            expect(expectedToFail).to.throw(/.*userid.*empty.*/i);
        });
    });

    describe("When the supplied userID has only white spaces", function(){
        const games = createGameServer();
        it("then it should throw an exception", function(){
            function expectedToFail(){
                games.startGame("  ", mapID);
            }
            expect(expectedToFail).to.throw(/.*userid.*empty.*/i);
        });
    });
});

describe("Scenario: Ending the game", function(){
    const userID = "UserID";
    const mapID = "lvl1";

    describe("Given a started game.", function(){
        const games = createGameServer();
        games.startGame(userID, mapID);

        describe("When the games is ended", function(){
            const notBreakingFunction = () => {
                games.endGame(userID);
            };
            it("then no exeption should be thrown.", function(){
                expect(notBreakingFunction).to.not.throw;
            });
        });
    });

    describe("Given a started and ended game.", function(){
        const games = createGameServer();
        games.startGame(userID, mapID);
        games.endGame(userID);

        describe("When the current position is queried", function(){
            const position = games.currentPosition(userID);

            it("then current position should be null.", function(){
                expect(position).to.be.null;
            });
        });
    });

    describe("Given no started game.", function(){
        const games = createGameServer();

        describe("When the game is ended", function(){
            const result = games.endGame(userID);

            it("then it should return true.", function(){
                expect(result).to.equal(true);
            });
        });
    });
});

describe("Scenario: Navigating in ongoing game", function(){
    const userID = "UserID";
    const mapID = "lvl1";

    describe("When player navigates in existing direction", function(){
        const games = createGameServer();
        games.startGame(userID, mapID);
        const startPosition = games.currentPosition(userID);
        const naviagtionDirection = startPosition.paths[0];
        const nextPosition = games.navigate(userID, naviagtionDirection);
        it("then the new position has to have a different id", function(){
            expect(nextPosition.id).to.not.equal(startPosition.id);
        });

        it("then the new position must be remembered for the user", function(){
            const current = games.currentPosition(userID);
            expect(current.id).to.equal(nextPosition.id);
        });
    });

    describe("When a player navigates outside the map boundaries", function(){
        const games = createGameServer();
        games.startGame(userID, mapID);
        it("then it should throw an exception", function(){
            function expectedToFail(){
                games.navigate(userID, Direction.left);
            }
            expect(expectedToFail).to.throw(/.*invalid direction.*/i);
        });
    });

    describe("When a player navigates inside the map in non existing direction", function(){
        const games = createGameServer();
        const maze2 = createTrivialMap();
        maze2.id = "lvl2";
        maze2.layout[0][0].paths = maze2.layout[0][0].paths.filter(p => p !== Direction.bottom);
        maze2.layout[1][0].paths = maze2.layout[1][0].paths.filter(p => p !== Direction.top);
        games.addMap(maze2);

        games.startGame(userID, maze2.id);

        it("then it should throw an exception", function(){
            function expectedToFail(){
                games.navigate(userID, Direction.bottom);
            }
            expect(expectedToFail).to.throw(/.*invalid direction.*/i);
        });
    });
});

describe("Scenario: List existing maps", function(){
    describe("When no map is inserted", function(){
       const games = new GameServer();
       it("then it should return an empty list", function(){
            const result = games.listMaps();
            expect(result).to.be.not.null;
            expect(result).to.be.empty;
       });
    });

    describe("When one map is inserted", function(){
        const games = createGameServer();
        it("then it should return a list with only one id", function(){
            const result = games.listMaps();
            expect(result).to.contain("lvl1");
        });
    });
});

describe("Scenario: Retrieving user id", function(){
    const games = createGameServer();
    describe("When a user id is returned", function(){
        const userID = games.createNewUserID();
        it("then it should not be null", function(){
            expect(userID).to.not.be.null;
        });
        it("then it should be a string", function(){
            expect(typeof userID).to.equal("string");
        });
        it("then it should not be empty", function(){
            expect(userID).to.not.equal("");
        });
    });

    describe("When two user ids are issued", function(){
        const id1 = games.createNewUserID();
        const id2 = games.createNewUserID();
        it("then they must not be the same", function(){
            expect(id1).to.not.equal(id2);
        });
    });
});