diff --git a/curriculum/challenges/english/25-front-end-development/lab-tic-tac-toe/67e3a6b7f60b4085588189e6.md b/curriculum/challenges/english/25-front-end-development/lab-tic-tac-toe/67e3a6b7f60b4085588189e6.md index e2631551e65..d4f7767ce79 100644 --- a/curriculum/challenges/english/25-front-end-development/lab-tic-tac-toe/67e3a6b7f60b4085588189e6.md +++ b/curriculum/challenges/english/25-front-end-development/lab-tic-tac-toe/67e3a6b7f60b4085588189e6.md @@ -18,6 +18,31 @@ demoType: onClick 4. You should create a `button#reset` element that resets the game when clicked. 5. A message should be displayed indicating either `X` or `O` as the winner, or neither if the result is a draw. +# --before-all-- + +```js +let clock = __FakeTimers.install(); + +async function reset(assert) { + const reset = document.querySelector("#reset"); + assert.exists(reset, "button#reset should exist"); + reset.click(); + clock.tick(50); +} + +// Gets the text of the document excluding that of the matching selector. +function getInnerTextExcept(removingSelector) { + const body = document.body.cloneNode(true); + + const squareElements = body.querySelectorAll(`${removingSelector},script`); + squareElements.forEach(element => { + element.parentNode.removeChild(element) + }); + + return body.innerText; +} +``` + # --hints-- You should export a `Board` component. @@ -99,7 +124,7 @@ async () => { const el = document.querySelector("button.square"); el.click(); - await delay(50); + clock.tick(50); try { assert.include(el.textContent, "X"); @@ -117,7 +142,7 @@ async () => { // NOTE: This test is intentionally high-up, because the latter tests rely on the functionality. const el = document.querySelector("button.square"); el.click(); - await delay(50); + clock.tick(50); try { await reset(assert); @@ -136,10 +161,10 @@ async () => { await reset(assert); const els = document.querySelectorAll("button.square"); els[0].click(); - await delay(50); + clock.tick(50); els[1].click(); - await delay(50); + clock.tick(50); try { assert.include(els[1].textContent, "O"); @@ -160,7 +185,7 @@ async () => { for (let i = 3; i < els.length + 3; i++) { const wrappedI = i % els.length; els[wrappedI].click(); - await delay(50); + clock.tick(50); assert.include(els[wrappedI].textContent, (i - 3) % 2 === 0 ? "X" : "O"); } } catch(e) { @@ -181,9 +206,9 @@ async () => { const wrappedI = i % els.length; // Click button twice to ensure it does not change els[wrappedI].click(); - await delay(50); + clock.tick(50); els[wrappedI].click(); - await delay(50); + clock.tick(50); assert.include(els[wrappedI].textContent, (i - 3) % 2 === 0 ? "X" : "O"); } } catch(e) { @@ -204,12 +229,12 @@ async () => { for (let i = 0; i < 3; i++) { const x = els[i]; x.click(); - await delay(50); + clock.tick(50); assert.include(x.textContent, "X"); const o = els[i + 3]; o.click(); - await delay(50); + clock.tick(50); if (i === 2) { assert.notInclude(o.textContent, "O"); } else { @@ -237,16 +262,16 @@ async () => { for (let i = 0; i < 2; i++) { const x = els[i]; x.click(); - await delay(50); + clock.tick(50); const o = els[i + 3]; o.click(); - await delay(50); + clock.tick(50); } const preXWin = getInnerTextExcept("button.square"); // Click winning button for X els[2].click(); - await delay(50); + clock.tick(50); const postXWin = getInnerTextExcept("button.square"); assert.notEqual(preXWin, postXWin); @@ -256,18 +281,18 @@ async () => { for (let i = 0; i < 2; i++) { const x = els[i]; x.click(); - await delay(50); + clock.tick(50); const o = els[i + 3]; o.click(); - await delay(50); + clock.tick(50); } els[6].click(); - await delay(50); + clock.tick(50); const preOWin = getInnerTextExcept("button.square"); // Click winning button for O els[5].click(); - await delay(50); + clock.tick(50); const postOWin = getInnerTextExcept("button.square"); assert.notEqual(preOWin, postOWin); @@ -291,18 +316,28 @@ async () => { await reset(assert); const els = document.querySelectorAll("button.square"); try { - for (let i = 3; i < els.length + 2; i++) { - const wrappedI = i % els.length; - els[wrappedI].click(); - await delay(50); + //use already known draw states + const drawMoves = [0, 1, 2, 4, 3, 5, 7, 6, 8]; + + const snapshot1 = getInnerTextExcept("button.square"); + els[drawMoves[0]].click(); + clock.tick(50); + + const snapshot2 = getInnerTextExcept("button.square"); + for (let i = 1; i < drawMoves.length - 1; i++) { + els[drawMoves[i]].click(); + clock.tick(50); } - const pre = getInnerTextExcept("button.square"); - els[2].click(); - await delay(50); + const snapshot3 = getInnerTextExcept("button.square"); + els[drawMoves[drawMoves.length - 1]].click(); + clock.tick(50); + + const snapshot4 = getInnerTextExcept("button.square"); + assert.notEqual(snapshot4, snapshot1); + assert.notEqual(snapshot4, snapshot2); + assert.notEqual(snapshot4, snapshot3); - const post = getInnerTextExcept("button.square"); - assert.notEqual(pre, post); } catch(e) { console.error(e); throw e; @@ -310,30 +345,11 @@ async () => { } ``` -# --before-all-- +Uninstall the clock, ignore this test ```js -async function delay(time) { - return new Promise((resolve) => setTimeout(resolve, time)); -} - -async function reset(assert) { - const reset = document.querySelector("#reset"); - assert.exists(reset, "button#reset should exist"); - reset.click(); - await delay(50); -} - -// Gets the text of the document excluding that of the matching selector. -function getInnerTextExcept(removingSelector) { - const body = document.body.cloneNode(true); - - const squareElements = body.querySelectorAll(`${removingSelector},script`); - squareElements.forEach(element => { - element.parentNode.removeChild(element) - }); - - return body.innerText; +async () => { +clock.uninstall() } ```