UNPKG

cm-chessboard

Version:

A JavaScript chessboard which is lightweight, ES6 module based, responsive, SVG rendered and without dependencies.

141 lines (129 loc) 6.03 kB
/** * Author and copyright: Stefan Haack (https://shaack.com) * Repository: https://github.com/shaack/cm-chessboard * License: MIT, see file 'LICENSE' */ import {describe, it, assert} from "../node_modules/teevi/src/teevi.js" import {PIECE, Chessboard} from "../src/Chessboard.js" import {FEN} from "../src/model/Position.js" describe("TestChessboard", () => { // https://github.com/shaack/cm-chessboard/issues/47 it("should create and immediately destroy a board without failure", () => { const chessboard = new Chessboard(document.getElementById("TestBoard"), { assetsUrl: "../assets/", position: FEN.start }) chessboard.destroy() }) it("should create and destroy a board", () => { const chessboard = new Chessboard(document.getElementById("TestBoard"), { assetsUrl: "../assets/", position: FEN.start }) assert.equal(chessboard.view.container.childNodes.length, 1) chessboard.destroy() assert.equal(chessboard.state, undefined) }) it("should create and destroy a chessboard", () => { const chessboard = new Chessboard(document.getElementById("TestPosition"), { assetsUrl: "../assets/", position: FEN.start }) assert.equal("" + chessboard.getPosition(), "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR") chessboard.destroy() }) it("should set and get the position", () => { const chessboard = new Chessboard(document.getElementById("TestPosition"), {assetsUrl: "../assets/"}) chessboard.setPosition("rn2k1r1/ppp1pp1p/3p2p1/5bn1/P7/2N2B2/1PPPPP2/2BNK1RR w Gkq - 4 11", false).then(() => { assert.equal("" + chessboard.getPosition(), "rn2k1r1/ppp1pp1p/3p2p1/5bn1/P7/2N2B2/1PPPPP2/2BNK1RR") chessboard.destroy() }) }) it("should get pieces on squares", () => { const chessboard = new Chessboard(document.getElementById("TestPosition"), { assetsUrl: "../assets/", position: FEN.start }) assert.equal(chessboard.getPiece("d1"), "wq") assert.equal(chessboard.getPiece("d8"), "bq") assert.equal(chessboard.getPiece("a2"), "wp") chessboard.destroy() }) it("should set pieces on squares", () => { const chessboard = new Chessboard(document.getElementById("TestPosition"), { assetsUrl: "../assets/", }) chessboard.setPiece("a1", PIECE.bk) assert.equal(chessboard.getPiece("a1"), "bk") chessboard.setPiece("e5", PIECE.wk) assert.equal(chessboard.getPiece("e5"), "wk") chessboard.destroy() }) // Regression for https://github.com/shaack/cm-chessboard/issues/154 // // A setPosition() call with a null diff (positionFrom === positionTo) // used to bypass the animation queue entirely and resolve on the next // microtask. That's wrong when an earlier animation — here the animated // movePiece — is still in the queue: the setPosition promise must wait // for that to drain before resolving, otherwise callers following the // "await setPosition(...); then continue" pattern run their // continuation before the board is visually settled. it("setPosition should wait for pending animations even on a null diff (#154)", async () => { const chessboard = new Chessboard(document.getElementById("TestPosition"), { assetsUrl: "../assets/", position: FEN.start, style: {animationDuration: 100} }) let movePieceResolved = false const movePromise = chessboard.movePiece("e2", "e4", true).then(() => { movePieceResolved = true }) // Same FEN as the position after movePiece → null diff inside // setPosition. The animated movePiece above is still queued. const targetFen = "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR" const setPromise = chessboard.setPosition(targetFen, true).then(() => { assert.equal(movePieceResolved, true, "setPosition resolved before the pending movePiece animation finished") }) await Promise.all([movePromise, setPromise]) chessboard.destroy() }) // Regression for a production crash: the ResizeObserver callback defers // handleResize() via setTimeout to avoid "ResizeObserver loop completed" // warnings. If destroy() is called between the observer firing and the // setTimeout running, handleResize -> redrawBoard hits // `this.chessboard.state.invokeExtensionPoints(...)` where `state` is // now undefined, throwing TypeError. // // We invalidate view.width by zeroing it so that the size-changed branch // of handleResize is guaranteed to enter updateMetrics + redrawBoard. // Otherwise a stable container size would short-circuit the branch and // the bug path wouldn't be exercised. it("should not crash when destroyed while a resize handler is pending", async () => { const chessboard = new Chessboard(document.getElementById("TestBoard"), { assetsUrl: "../assets/", position: FEN.start }) const view = chessboard.view // Force the size-changed branch to trigger on the next handleResize view.width = 0 view.height = 0 // Simulate what the ResizeObserver callback does: defer handleResize // via setTimeout. This path is deterministic. const pending = new Promise((resolve) => { setTimeout(() => { try { view.handleResize() resolve(null) } catch (e) { resolve(e) } }) }) // Force the race: destroy the board before the setTimeout fires. chessboard.destroy() const error = await pending assert.equal(error, null, error && error.message) }) })