| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391 | import * as anchor from "@coral-xyz/anchor";import { AnchorError, type Program } from "@coral-xyz/anchor";import { TicTacToe } from '../target/types/tic_tac_toe';import { expect } from 'chai';async function play(program: Program<TicTacToe>, game, player, tile, expectedTurn, expectedGameState, expectedBoard) {  await program.methods    .play(tile)    .accounts({      player: player.publicKey,      game    })    .signers(player instanceof (anchor.Wallet as any) ? [] : [player])    .rpc();  const gameState = await program.account.game.fetch(game);  expect(gameState.turn).to.equal(expectedTurn);  expect(gameState.state).to.eql(expectedGameState);  expect(gameState.board)    .to    .eql(expectedBoard);}describe('tic-tac-toe', () => {  // Configure the client to use the local cluster.  anchor.setProvider(anchor.AnchorProvider.env());  const program = anchor.workspace.TicTacToe as Program<TicTacToe>;  const programProvider = program.provider as anchor.AnchorProvider;  it('setup game!', async() => {    const gameKeypair = anchor.web3.Keypair.generate();    const playerOne = programProvider.wallet;    const playerTwo = anchor.web3.Keypair.generate();    await program.methods      .setupGame(playerTwo.publicKey)      .accounts({        game: gameKeypair.publicKey,        playerOne: playerOne.publicKey,      })      .signers([gameKeypair])      .rpc();    let gameState = await program.account.game.fetch(gameKeypair.publicKey);    expect(gameState.turn).to.equal(1);    expect(gameState.players)      .to      .eql([playerOne.publicKey, playerTwo.publicKey]);    expect(gameState.state).to.eql({ active: {} });    expect(gameState.board)      .to      .eql([[null,null,null],[null,null,null],[null,null,null]]);  });  it('player one wins!', async () => {    const gameKeypair = anchor.web3.Keypair.generate();    const playerOne = programProvider.wallet;    const playerTwo = anchor.web3.Keypair.generate();    await program.methods      .setupGame(playerTwo.publicKey)      .accounts({        game: gameKeypair.publicKey,        playerOne: playerOne.publicKey,      })      .signers([gameKeypair])      .rpc();    let gameState = await program.account.game.fetch(gameKeypair.publicKey);    expect(gameState.turn).to.equal(1);    expect(gameState.players)      .to      .eql([playerOne.publicKey, playerTwo.publicKey]);    expect(gameState.state).to.eql({ active: {} });    expect(gameState.board)      .to      .eql([[null,null,null],[null,null,null],[null,null,null]]);    await play(      program,      gameKeypair.publicKey,      playerOne,      {row: 0, column: 0},      2,      { active: {}, },      [        [{x:{}},null,null],        [null,null,null],        [null,null,null]      ]    );    try {      await play(        program,        gameKeypair.publicKey,        playerOne, // same player in subsequent turns        // change sth about the tx because        // duplicate tx that come in too fast        // after each other may get dropped        {row: 1, column: 0},        2,        { active: {}, },        [          [{x:{}},null,null],          [null,null,null],          [null,null,null]        ]      );      chai.assert(false, "should've failed but didn't ");    } catch (_err) {      expect(_err).to.be.instanceOf(AnchorError);      const err: AnchorError = _err;      expect(err.error.errorCode.code).to.equal("NotPlayersTurn");      expect(err.error.errorCode.number).to.equal(6003);      expect(err.program.equals(program.programId)).is.true;      expect(err.error.comparedValues).to.deep.equal([playerTwo.publicKey, playerOne.publicKey]);    }    await play(      program,      gameKeypair.publicKey,      playerTwo,      {row: 1, column: 0},      3,      { active: {}, },      [        [{x:{}},null,null],        [{o:{}},null,null],        [null,null,null]      ]    );    await play(      program,      gameKeypair.publicKey,      playerOne,      {row: 0, column: 1},      4,      { active: {}, },      [        [{x:{}},{x: {}},null],        [{o:{}},null,null],        [null,null,null]      ]    );    try {      await play(        program,        gameKeypair.publicKey,        playerTwo,        {row: 5, column: 1}, // out of bounds row        4,        { active: {}, },        [          [{x:{}},{x: {}},null],          [{o:{}},null,null],          [null,null,null]        ]      );      chai.assert(false, "should've failed but didn't ");    } catch (_err) {      expect(_err).to.be.instanceOf(AnchorError);      const err: AnchorError = _err;      expect(err.error.errorCode.number).to.equal(6000);      expect(err.error.errorCode.code).to.equal("TileOutOfBounds");    }    await play(      program,      gameKeypair.publicKey,      playerTwo,      {row: 1, column: 1},      5,      { active: {}, },      [        [{x:{}},{x: {}},null],        [{o:{}},{o:{}},null],        [null,null,null]      ]    );    try {      await play(        program,        gameKeypair.publicKey,        playerOne,        {row: 0, column: 0},        5,        { active: {}, },        [          [{x:{}},{x: {}},null],          [{o:{}},{o:{}},null],          [null,null,null]        ]      );      chai.assert(false, "should've failed but didn't ");    } catch (_err) {      expect(_err).to.be.instanceOf(AnchorError);      const err: AnchorError = _err;      expect(err.error.errorCode.number).to.equal(6001);    }    await play(      program,      gameKeypair.publicKey,      playerOne,      {row: 0, column: 2},      5,      { won: { winner: playerOne.publicKey }, },      [        [{x:{}},{x: {}},{x: {}}],        [{o:{}},{o:{}},null],        [null,null,null]      ]    );    try {      await play(        program,        gameKeypair.publicKey,        playerOne,        {row: 0, column: 2},        5,        { won: { winner: playerOne.publicKey }, },        [          [{x:{}},{x: {}},{x: {}}],          [{o:{}},{o:{}},null],          [null,null,null]        ]      );      chai.assert(false, "should've failed but didn't ");    } catch (_err) {      expect(_err).to.be.instanceOf(AnchorError);      const err: AnchorError = _err;      expect(err.error.errorCode.number).to.equal(6002);    }  })  it('tie', async () => {    const gameKeypair = anchor.web3.Keypair.generate();    const playerOne = programProvider.wallet;    const playerTwo = anchor.web3.Keypair.generate();    await program.methods      .setupGame(playerTwo.publicKey)      .accounts({        game: gameKeypair.publicKey,        playerOne: playerOne.publicKey,      })      .signers([gameKeypair])      .rpc();    let gameState = await program.account.game.fetch(gameKeypair.publicKey);    expect(gameState.turn).to.equal(1);    expect(gameState.players)      .to      .eql([playerOne.publicKey, playerTwo.publicKey]);    expect(gameState.state).to.eql({ active: {} });    expect(gameState.board)      .to      .eql([[null,null,null],[null,null,null],[null,null,null]]);    await play(      program,      gameKeypair.publicKey,      playerOne,      {row: 0, column: 0},      2,      { active: {}, },      [        [{x:{}},null,null],        [null,null,null],        [null,null,null]      ]    );    await play(      program,      gameKeypair.publicKey,      playerTwo,      {row: 1, column: 1},      3,      { active: {}, },      [        [{x:{}},null,null],        [null,{o:{}},null],        [null,null,null]      ]    );    await play(      program,      gameKeypair.publicKey,      playerOne,      {row: 2, column: 0},      4,      { active: {}, },      [        [{x:{}},null,null],        [null,{o:{}},null],        [{x:{}},null,null]      ]    );    await play(      program,      gameKeypair.publicKey,      playerTwo,      {row: 1, column: 0},      5,      { active: {}, },      [        [{x:{}},null,null],        [{o:{}},{o:{}},null],        [{x:{}},null,null]      ]    );    await play(      program,      gameKeypair.publicKey,      playerOne,      {row: 1, column: 2},      6,      { active: {}, },      [        [{x:{}},null,null],        [{o:{}},{o:{}},{x:{}}],        [{x:{}},null,null]      ]    );    await play(      program,      gameKeypair.publicKey,      playerTwo,      {row: 0, column: 1},      7,      { active: {}, },      [        [{x:{}},{o:{}},null],        [{o:{}},{o:{}},{x:{}}],        [{x:{}},null,null]      ]    );    await play(      program,      gameKeypair.publicKey,      playerOne,      {row: 2, column: 1},      8,      { active: {}, },      [        [{x:{}},{o:{}},null],        [{o:{}},{o:{}},{x:{}}],        [{x:{}},{x:{}},null]      ]    );    await play(      program,      gameKeypair.publicKey,      playerTwo,      {row: 2, column: 2},      9,      { active: {}, },      [        [{x:{}},{o:{}},null],        [{o:{}},{o:{}},{x:{}}],        [{x:{}},{x:{}},{o:{}}]      ]    );    await play(      program,      gameKeypair.publicKey,      playerOne,      {row: 0, column: 2},      9,      { tie: {}, },      [        [{x:{}},{o:{}},{x:{}}],        [{o:{}},{o:{}},{x:{}}],        [{x:{}},{x:{}},{o:{}}]      ]    );  })});
 |