From 1390639c464c357761967ac696a4182ce5a253e2 Mon Sep 17 00:00:00 2001 From: Jim Cramer Date: Fri, 20 Sep 2024 16:50:05 +0200 Subject: [PATCH] Major revision (#102) - Test runner rewritten in TypeScript. - Unit tests rewritten in TypeScript (except the unit tests that are part of an exercise to be coded by trainees, those remain in JavaScript). - Where applicable, exercises now use `import`/`export` instead of `require()`, but are otherwise unchanged. - The test runner keeps track of which exercises have been modified and which exercises have been tested after modification. This is done by computing and comparing hashes based on exercise file content. - Added a Git pre-commit hook to disallow changes to the `main` branch and to enforce the format of the week-branch name. Example: JohnDoe-w1-JavaScript. As a new branch should be created for each week the pre-commit hook also disallows committing changes that are for more than one week. - Added a Git pre-push hook that disallows pushing changed exercises that have not been tested or need retesting after modification. - Added a test summary report that appears as the first changed file in a PR, for easy reference by homework reviewers. --- .env-example | 6 +- .eslintrc.js | 11 +- .github-later/workflows/ci.yml | 24 + .github-later/workflows/test-report.yml | 20 + .gitignore | 1 + .hashes.json | 74 +- .husky/pre-commit | 3 + .husky/pre-push | 3 + .markdownlint.json | 3 +- .prettierignore | 2 + .tours/test-runner.tour | 147 - .tours/unit-test-browser.tour | 88 - .tours/unit-test-for-node-based-exercise.tour | 84 - .tours/unit-test-node.tour | 108 - .vscode/extensions.json | 1 - .vscode/settings.json | 2 +- .vscode/tasks.json | 16 + 1-JavaScript/Week2/README.md | 1 - .../Week2/assignment/ex1-giveCompliment.js | 6 +- 1-JavaScript/Week2/assignment/ex2-dogYears.js | 4 +- .../Week2/assignment/ex3-tellFortune.js | 4 +- .../Week2/assignment/ex4-shoppingCart.js | 1 - .../Week2/assignment/ex5-shoppingCartPure.js | 1 - .../Week2/assignment/ex6-totalCost.js | 1 - .../Week2/assignment/ex7-mindPrivacy.js | 1 - .../test-reports/ex1-giveCompliment.todo.txt | 1 - .../Week2/test-reports/ex2-dogYears.todo.txt | 1 - .../test-reports/ex3-tellFortune.todo.txt | 1 - .../test-reports/ex4-shoppingCart.todo.txt | 1 - .../ex5-shoppingCartPure.todo.txt | 1 - .../Week2/test-reports/ex6-totalCost.todo.txt | 1 - .../test-reports/ex7-mindPrivacy.todo.txt | 1 - ...ent.test.js => ex1-giveCompliment.test.ts} | 56 +- ...-dogYears.test.js => ex2-dogYears.test.ts} | 26 +- ...ortune.test.js => ex3-tellFortune.test.ts} | 77 +- 1-JavaScript/Week3/README.md | 102 +- .../assignment/ex1-doubleEvenNumbers.test.js | 10 +- .../Week3/assignment/ex2-mondaysWorth.test.js | 7 +- .../Week3/assignment/ex3-lemonAllergy.test.js | 9 +- .../ex4-observable/ex4-observable.js | 10 +- .../ex4-observable/ex4-observable.test.js | 10 +- .../Week3/assignment/ex4-observable/main.js | 10 +- .../Week3/assignment/ex5-wallet/index.html | 2 +- .../Week3/assignment/ex5-wallet/index.js | 2 - .../ex1-doubleEvenNumbers.test.todo.txt | 1 - .../ex2-mondaysWorth.test.todo.txt | 1 - .../ex3-lemonAllergy.test.todo.txt | 1 - .../test-reports/ex4-observable.todo.txt | 1 - .../Week3/test-reports/ex5-wallet.todo.txt | 1 - .../Week3/unit-tests/ex5-wallet.test.js | 30 +- 2-Browsers/Week1/README.md | 4 +- .../Week1/assignment/ex1-bookList/index.html | 4 +- .../Week1/assignment/ex2-aboutMe/index.html | 4 +- .../Week1/assignment/ex2-aboutMe/index.js | 1 - 2-Browsers/Week1/assignment/ex3-hijackLogo.js | 1 - .../assignment/ex4-whatsTheTime/index.html | 4 +- .../assignment/ex4-whatsTheTime/index.js | 1 - .../Week1/assignment/ex5-catWalk/index.html | 4 +- .../Week1/assignment/ex5-catWalk/index.js | 1 - .../assignment/ex6-gameOfLife/index.html | 4 +- .../Week1/assignment/ex6-gameOfLife/index.js | 87 +- .../Week1/test-reports/ex1-bookList.todo.txt | 1 - .../Week1/test-reports/ex2-aboutMe.todo.txt | 1 - .../test-reports/ex3-hijackLogo.todo.txt | 1 - .../test-reports/ex4-whatsTheTime.todo.txt | 1 - .../Week1/test-reports/ex5-catWalk.todo.txt | 1 - .../test-reports/ex6-gameOfLife.todo.txt | 1 - 2-Browsers/Week1/unit-tests/.verbose | 0 ...-bookList.test.js => ex1-bookList.test.ts} | 59 +- ...x2-aboutMe.test.js => ex2-aboutMe.test.ts} | 34 +- .../Week1/unit-tests/ex3-hijackLogo.test.js | 37 - .../Week1/unit-tests/ex3-hijackLogo.test.ts | 45 + ...eTime.test.js => ex4-whatsTheTime.test.ts} | 55 +- .../Week1/unit-tests/ex5-catWalk.test.js | 58 - .../Week1/unit-tests/ex5-catWalk.test.ts | 72 + ...eOfLife.test.js => ex6-gameOfLife.test.ts} | 114 +- 3-UsingAPIs/Week1/README.md | 8 +- 3-UsingAPIs/Week1/assignment/ex1-johnWho.js | 4 +- .../Week1/assignment/ex2-checkDoubleDigits.js | 4 +- 3-UsingAPIs/Week1/assignment/ex3-rollDie.js | 4 +- .../Week1/assignment/ex4-pokerDiceAll.js | 3 +- .../Week1/assignment/ex5-pokerDiceChain.js | 3 +- .../Week1/test-reports/ex1-johnWho.todo.txt | 1 - .../ex2-checkDoubleDigits.todo.txt | 1 - .../Week1/test-reports/ex3-rollDie.todo.txt | 1 - .../test-reports/ex4-pokerDiceAll.todo.txt | 1 - .../test-reports/ex5-pokerDiceChain.todo.txt | 1 - 3-UsingAPIs/Week1/unit-tests/.verbose | 0 ...x1-johnWho.test.js => ex1-johnWho.test.ts} | 70 +- ....test.js => ex2-checkDoubleDigits.test.ts} | 54 +- ...x3-rollDie.test.js => ex3-rollDie.test.ts} | 58 +- ...ceAll.test.js => ex4-pokerDiceAll.test.ts} | 57 +- ...ain.test.js => ex5-pokerDiceChain.test.ts} | 31 +- 3-UsingAPIs/Week2/README.md | 87 + .../assignment/ex1-programmerFun/index.html | 4 +- .../assignment/ex1-programmerFun/index.js | 1 - .../assignment/ex2-pokemonApp/index.html | 4 +- .../Week2/assignment/ex2-pokemonApp/index.js | 1 - .../Week2/assignment/ex2-pokemonApp/style.css | 2 +- 3-UsingAPIs/Week2/assignment/ex3-rollAnAce.js | 18 +- 3-UsingAPIs/Week2/assignment/ex4-diceRace.js | 10 +- 3-UsingAPIs/Week2/assignment/ex5-vscDebug.js | 3 - .../assignment/ex6-browserDebug/index.html | 4 +- .../assignment/ex6-browserDebug/index.js | 2 - .../test-reports/ex1-programmerFun.todo.txt | 1 - .../test-reports/ex2-pokemonApp.todo.txt | 1 - .../Week2/test-reports/ex3-rollAnAce.todo.txt | 1 - .../Week2/test-reports/ex4-diceRace.todo.txt | 1 - .../Week2/test-reports/ex5-vscDebug.todo.txt | 1 - .../test-reports/ex6-browserDebug.todo.txt | 1 - 3-UsingAPIs/Week2/typescript/ex3-rollAnAce.ts | 35 + .../Week2/typescript/pokerDiceRoller.ts | 116 + 3-UsingAPIs/Week2/unit-tests/.verbose | 0 ...rFun.test.js => ex1-programmerFun.test.ts} | 43 +- ...emonApp.test.js => ex2-pokemonApp.test.ts} | 43 +- ...ollAnAce.test.js => ex3-rollAnAce.test.ts} | 49 +- ...-diceRace.test.js => ex4-diceRace.test.ts} | 57 +- 3-UsingAPIs/helpers/pokerDiceRoller.js | 40 +- CHANGELOG.md | 16 + README.md | 138 +- assets/directory-structure.png | Bin 30853 -> 33291 bytes assets/hover-die-face.png | Bin 0 -> 29111 bytes assets/hover-function-any.png | Bin 0 -> 21786 bytes assets/hover-function-jsdoc.png | Bin 0 -> 46526 bytes assets/hover-type-error.png | Bin 0 -> 38680 bytes assets/open-in-terminal.png | Bin 29749 -> 33282 bytes assets/problem-pane.png | Bin 0 -> 14394 bytes assets/terminal-window.png | Bin 18378 -> 13635 bytes assets/unsaved-changes.png | Bin 9589 -> 20349 bytes babel.config.cjs | 12 + cspell.json | 2 +- jest.config.js | 12 +- module-week.sh | 40 + package-lock.json | 15452 ++++++++++------ package.json | 94 +- prettier.config.js => prettier.config.cjs | 2 +- test-action.sh | 30 + test-encrypted/ex1-bookList.json | 16 - .../ex1-doubleEvenNumbers.test.json | 7 - test-encrypted/ex1-giveCompliment.json | 12 - test-encrypted/ex1-johnWho.json | 12 - test-encrypted/ex1-programmerFun.json | 16 - test-encrypted/ex2-aboutMe.json | 16 - test-encrypted/ex2-checkDoubleDigits.json | 12 - test-encrypted/ex2-dogYears.json | 12 - test-encrypted/ex2-mondaysWorth.test.json | 7 - test-encrypted/ex2-pokemonApp.json | 16 - test-encrypted/ex3-hijackLogo.json | 12 - test-encrypted/ex3-lemonAllergy.test.json | 7 - test-encrypted/ex3-rollAnAce.json | 12 - test-encrypted/ex3-rollDie.json | 12 - test-encrypted/ex3-tellFortune.json | 12 - test-encrypted/ex4-diceRace.json | 12 - test-encrypted/ex4-observable.json | 12 - test-encrypted/ex4-pokerDiceAll.json | 12 - test-encrypted/ex4-shoppingCart.json | 12 - test-encrypted/ex4-whatsTheTime.json | 16 - test-encrypted/ex5-catWalk.json | 20 - test-encrypted/ex5-pokerDiceChain.json | 12 - test-encrypted/ex5-shoppingCartPure.json | 12 - test-encrypted/ex5-vscDebug.json | 12 - test-encrypted/ex5-wallet.json | 16 - test-encrypted/ex6-browserDebug.json | 16 - test-encrypted/ex6-gameOfLife.json | 16 - test-encrypted/ex6-totalCost.json | 12 - test-encrypted/ex7-mindPrivacy.json | 12 - test-runner/.eslintrc.js | 17 - test-runner/CustomReporter.js | 52 - test-runner/ExerciseMenu.ts | 209 + test-runner/README.md | 233 +- test-runner/__tests__/ExerciseMenu.test.ts | 32 + test-runner/action-summary.ts | 63 + test-runner/assets/ast-explorer.png | Bin 106186 -> 61460 bytes test-runner/clean.ts | 38 + test-runner/cleanup.js | 20 - test-runner/compliance.ts | 307 + test-runner/decrypt.js | 71 - test-runner/encrypt.js | 84 - test-runner/eslint.config.js | 25 + test-runner/exercise-runner.ts | 109 + test-runner/fake-action-core.ts | 37 + test-runner/index.js | 309 - test-runner/jsdom-helpers.js | 52 - test-runner/jsdom-helpers.ts | 60 + test-runner/logger.js | 21 - test-runner/module-week.ts | 7 + test-runner/pre-commit.ts | 25 + test-runner/pre-push.ts | 27 + test-runner/reset-reports.js | 58 - test-runner/run-it.js | 125 - test-runner/sysinfo.js | 80 - test-runner/test-changed.ts | 22 + test-runner/test-cli.ts | 87 + test-runner/test-runner-helpers.js | 161 - test-runner/test-runner.ts | 298 + test-runner/unit-test-helpers.js | 149 - test-runner/unit-test-helpers.ts | 195 + tsconfig.json | 109 + 198 files changed, 13175 insertions(+), 8474 deletions(-) create mode 100644 .github-later/workflows/ci.yml create mode 100644 .github-later/workflows/test-report.yml create mode 100644 .husky/pre-commit create mode 100644 .husky/pre-push create mode 100644 .prettierignore delete mode 100644 .tours/test-runner.tour delete mode 100644 .tours/unit-test-browser.tour delete mode 100644 .tours/unit-test-for-node-based-exercise.tour delete mode 100644 .tours/unit-test-node.tour create mode 100644 .vscode/tasks.json delete mode 100644 1-JavaScript/Week2/test-reports/ex1-giveCompliment.todo.txt delete mode 100644 1-JavaScript/Week2/test-reports/ex2-dogYears.todo.txt delete mode 100644 1-JavaScript/Week2/test-reports/ex3-tellFortune.todo.txt delete mode 100644 1-JavaScript/Week2/test-reports/ex4-shoppingCart.todo.txt delete mode 100644 1-JavaScript/Week2/test-reports/ex5-shoppingCartPure.todo.txt delete mode 100644 1-JavaScript/Week2/test-reports/ex6-totalCost.todo.txt delete mode 100644 1-JavaScript/Week2/test-reports/ex7-mindPrivacy.todo.txt rename 1-JavaScript/Week2/unit-tests/{ex1-giveCompliment.test.js => ex1-giveCompliment.test.ts} (51%) rename 1-JavaScript/Week2/unit-tests/{ex2-dogYears.test.js => ex2-dogYears.test.ts} (68%) rename 1-JavaScript/Week2/unit-tests/{ex3-tellFortune.test.js => ex3-tellFortune.test.ts} (60%) delete mode 100644 1-JavaScript/Week3/test-reports/ex1-doubleEvenNumbers.test.todo.txt delete mode 100644 1-JavaScript/Week3/test-reports/ex2-mondaysWorth.test.todo.txt delete mode 100644 1-JavaScript/Week3/test-reports/ex3-lemonAllergy.test.todo.txt delete mode 100644 1-JavaScript/Week3/test-reports/ex4-observable.todo.txt delete mode 100644 1-JavaScript/Week3/test-reports/ex5-wallet.todo.txt delete mode 100644 2-Browsers/Week1/test-reports/ex1-bookList.todo.txt delete mode 100644 2-Browsers/Week1/test-reports/ex2-aboutMe.todo.txt delete mode 100644 2-Browsers/Week1/test-reports/ex3-hijackLogo.todo.txt delete mode 100644 2-Browsers/Week1/test-reports/ex4-whatsTheTime.todo.txt delete mode 100644 2-Browsers/Week1/test-reports/ex5-catWalk.todo.txt delete mode 100644 2-Browsers/Week1/test-reports/ex6-gameOfLife.todo.txt delete mode 100644 2-Browsers/Week1/unit-tests/.verbose rename 2-Browsers/Week1/unit-tests/{ex1-bookList.test.js => ex1-bookList.test.ts} (55%) rename 2-Browsers/Week1/unit-tests/{ex2-aboutMe.test.js => ex2-aboutMe.test.ts} (51%) delete mode 100644 2-Browsers/Week1/unit-tests/ex3-hijackLogo.test.js create mode 100644 2-Browsers/Week1/unit-tests/ex3-hijackLogo.test.ts rename 2-Browsers/Week1/unit-tests/{ex4-whatsTheTime.test.js => ex4-whatsTheTime.test.ts} (50%) delete mode 100644 2-Browsers/Week1/unit-tests/ex5-catWalk.test.js create mode 100644 2-Browsers/Week1/unit-tests/ex5-catWalk.test.ts rename 2-Browsers/Week1/unit-tests/{ex6-gameOfLife.test.js => ex6-gameOfLife.test.ts} (75%) delete mode 100644 3-UsingAPIs/Week1/test-reports/ex1-johnWho.todo.txt delete mode 100644 3-UsingAPIs/Week1/test-reports/ex2-checkDoubleDigits.todo.txt delete mode 100644 3-UsingAPIs/Week1/test-reports/ex3-rollDie.todo.txt delete mode 100644 3-UsingAPIs/Week1/test-reports/ex4-pokerDiceAll.todo.txt delete mode 100644 3-UsingAPIs/Week1/test-reports/ex5-pokerDiceChain.todo.txt delete mode 100644 3-UsingAPIs/Week1/unit-tests/.verbose rename 3-UsingAPIs/Week1/unit-tests/{ex1-johnWho.test.js => ex1-johnWho.test.ts} (59%) rename 3-UsingAPIs/Week1/unit-tests/{ex2-checkDoubleDigits.test.js => ex2-checkDoubleDigits.test.ts} (69%) rename 3-UsingAPIs/Week1/unit-tests/{ex3-rollDie.test.js => ex3-rollDie.test.ts} (66%) rename 3-UsingAPIs/Week1/unit-tests/{ex4-pokerDiceAll.test.js => ex4-pokerDiceAll.test.ts} (55%) rename 3-UsingAPIs/Week1/unit-tests/{ex5-pokerDiceChain.test.js => ex5-pokerDiceChain.test.ts} (65%) delete mode 100644 3-UsingAPIs/Week2/test-reports/ex1-programmerFun.todo.txt delete mode 100644 3-UsingAPIs/Week2/test-reports/ex2-pokemonApp.todo.txt delete mode 100644 3-UsingAPIs/Week2/test-reports/ex3-rollAnAce.todo.txt delete mode 100644 3-UsingAPIs/Week2/test-reports/ex4-diceRace.todo.txt delete mode 100644 3-UsingAPIs/Week2/test-reports/ex5-vscDebug.todo.txt delete mode 100644 3-UsingAPIs/Week2/test-reports/ex6-browserDebug.todo.txt create mode 100644 3-UsingAPIs/Week2/typescript/ex3-rollAnAce.ts create mode 100644 3-UsingAPIs/Week2/typescript/pokerDiceRoller.ts delete mode 100644 3-UsingAPIs/Week2/unit-tests/.verbose rename 3-UsingAPIs/Week2/unit-tests/{ex1-programmerFun.test.js => ex1-programmerFun.test.ts} (55%) rename 3-UsingAPIs/Week2/unit-tests/{ex2-pokemonApp.test.js => ex2-pokemonApp.test.ts} (51%) rename 3-UsingAPIs/Week2/unit-tests/{ex3-rollAnAce.test.js => ex3-rollAnAce.test.ts} (78%) rename 3-UsingAPIs/Week2/unit-tests/{ex4-diceRace.test.js => ex4-diceRace.test.ts} (54%) create mode 100644 CHANGELOG.md create mode 100644 assets/hover-die-face.png create mode 100644 assets/hover-function-any.png create mode 100644 assets/hover-function-jsdoc.png create mode 100644 assets/hover-type-error.png create mode 100644 assets/problem-pane.png create mode 100644 babel.config.cjs create mode 100755 module-week.sh rename prettier.config.js => prettier.config.cjs (80%) create mode 100644 test-action.sh delete mode 100644 test-encrypted/ex1-bookList.json delete mode 100644 test-encrypted/ex1-doubleEvenNumbers.test.json delete mode 100644 test-encrypted/ex1-giveCompliment.json delete mode 100644 test-encrypted/ex1-johnWho.json delete mode 100644 test-encrypted/ex1-programmerFun.json delete mode 100644 test-encrypted/ex2-aboutMe.json delete mode 100644 test-encrypted/ex2-checkDoubleDigits.json delete mode 100644 test-encrypted/ex2-dogYears.json delete mode 100644 test-encrypted/ex2-mondaysWorth.test.json delete mode 100644 test-encrypted/ex2-pokemonApp.json delete mode 100644 test-encrypted/ex3-hijackLogo.json delete mode 100644 test-encrypted/ex3-lemonAllergy.test.json delete mode 100644 test-encrypted/ex3-rollAnAce.json delete mode 100644 test-encrypted/ex3-rollDie.json delete mode 100644 test-encrypted/ex3-tellFortune.json delete mode 100644 test-encrypted/ex4-diceRace.json delete mode 100644 test-encrypted/ex4-observable.json delete mode 100644 test-encrypted/ex4-pokerDiceAll.json delete mode 100644 test-encrypted/ex4-shoppingCart.json delete mode 100644 test-encrypted/ex4-whatsTheTime.json delete mode 100644 test-encrypted/ex5-catWalk.json delete mode 100644 test-encrypted/ex5-pokerDiceChain.json delete mode 100644 test-encrypted/ex5-shoppingCartPure.json delete mode 100644 test-encrypted/ex5-vscDebug.json delete mode 100644 test-encrypted/ex5-wallet.json delete mode 100644 test-encrypted/ex6-browserDebug.json delete mode 100644 test-encrypted/ex6-gameOfLife.json delete mode 100644 test-encrypted/ex6-totalCost.json delete mode 100644 test-encrypted/ex7-mindPrivacy.json delete mode 100644 test-runner/.eslintrc.js delete mode 100644 test-runner/CustomReporter.js create mode 100644 test-runner/ExerciseMenu.ts create mode 100644 test-runner/__tests__/ExerciseMenu.test.ts create mode 100644 test-runner/action-summary.ts create mode 100644 test-runner/clean.ts delete mode 100644 test-runner/cleanup.js create mode 100644 test-runner/compliance.ts delete mode 100644 test-runner/decrypt.js delete mode 100644 test-runner/encrypt.js create mode 100644 test-runner/eslint.config.js create mode 100644 test-runner/exercise-runner.ts create mode 100644 test-runner/fake-action-core.ts delete mode 100644 test-runner/index.js delete mode 100644 test-runner/jsdom-helpers.js create mode 100644 test-runner/jsdom-helpers.ts delete mode 100644 test-runner/logger.js create mode 100644 test-runner/module-week.ts create mode 100644 test-runner/pre-commit.ts create mode 100644 test-runner/pre-push.ts delete mode 100644 test-runner/reset-reports.js delete mode 100644 test-runner/run-it.js delete mode 100644 test-runner/sysinfo.js create mode 100644 test-runner/test-changed.ts create mode 100644 test-runner/test-cli.ts delete mode 100644 test-runner/test-runner-helpers.js create mode 100644 test-runner/test-runner.ts delete mode 100644 test-runner/unit-test-helpers.js create mode 100644 test-runner/unit-test-helpers.ts create mode 100644 tsconfig.json diff --git a/.env-example b/.env-example index 3874f4c35..b5cc7846b 100644 --- a/.env-example +++ b/.env-example @@ -1 +1,5 @@ -HYF_SECRET= \ No newline at end of file +# Development +ASSIGNMENT_FOLDER=assignment +BRANCH_CHECKS=0 +ENABLE_CLEAN=1 +# HUSKY=0 \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js index 62e7dc28e..b475e0ef2 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -6,14 +6,11 @@ module.exports = { node: true, jest: true, }, - plugins: ['hyf', 'no-autofix'], + plugins: ['no-autofix'], extends: ['eslint:recommended'], parserOptions: { ecmaVersion: 2020, }, - globals: { - axios: 'readonly', - }, rules: { 'no-console': 'off', 'no-var': 'error', @@ -44,8 +41,8 @@ module.exports = { message: 'Avoid `for in` loops. Prefer `Object.keys()` instead.', }, ], - 'hyf/use-map-result': 'error', - 'hyf/camelcase': 'warn', - 'hyf/no-commented-out-code': 'warn', + // 'hyf/use-map-result': 'error', + // 'hyf/camelcase': 'warn', + // 'hyf/no-commented-out-code': 'warn', }, }; diff --git a/.github-later/workflows/ci.yml b/.github-later/workflows/ci.yml new file mode 100644 index 000000000..e01c6c5c4 --- /dev/null +++ b/.github-later/workflows/ci.yml @@ -0,0 +1,24 @@ +name: 'CI' +on: + pull_request: +jobs: + build-test: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [20.x] + + steps: + - uses: actions/checkout@v4 # checkout the repo + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + - run: npm ci # install packages + - run: ./module-week.sh # run tests (configured to use jest-junit reporter) + - uses: actions/upload-artifact@v4 # upload test results + if: success() || failure() # run this step even if previous step failed + with: + name: test-results + path: junit.xml diff --git a/.github-later/workflows/test-report.yml b/.github-later/workflows/test-report.yml new file mode 100644 index 000000000..45451b7da --- /dev/null +++ b/.github-later/workflows/test-report.yml @@ -0,0 +1,20 @@ +name: 'Test Report' +on: + workflow_run: + workflows: ['CI'] # runs after CI workflow + types: + - completed +permissions: + contents: read + actions: read + checks: write +jobs: + report: + runs-on: ubuntu-latest + steps: + - uses: dorny/test-reporter@v1 + with: + artifact: test-results # artifact name + name: JEST Tests # Name of the check run which will be created + path: '*.xml' # Path to test results (inside artifact .zip) + reporter: jest-junit # Format of test results diff --git a/.gitignore b/.gitignore index 4b72510b7..c62043e2d 100644 --- a/.gitignore +++ b/.gitignore @@ -122,3 +122,4 @@ dist .recent.json .disclaimer @assignment* +.dist/ diff --git a/.hashes.json b/.hashes.json index cbd6b59f0..c6dc8be95 100644 --- a/.hashes.json +++ b/.hashes.json @@ -1,31 +1,47 @@ { - "ex1-giveCompliment": "60df6f8686e387e8c2fb72789b35af2c66a27b224112dcf5b0c0eeb7ab694852", - "ex2-dogYears": "98e25ac9c737f580a1736eee1dcea2cb39a9739429c84aa182aaa4a13437e646", - "ex3-tellFortune": "684d840ab303b2bfbcc0aee3e4bcf94478b63f4dbe3a603ccba489bd922442a5", - "ex4-shoppingCart": "1ce7c5ff829999bccc0234f10d5304c80b8421ce4dc9dbae81cf0888c977b5fe", - "ex5-shoppingCartPure": "9fde65b469f4a86031503c6700845db4e832c76fd4322de4009be67a12c2dd6b", - "ex6-totalCost": "c52ceb8ee35009539c45ce3fb74b4cc00e2895a7e214419f313b7e61f6679376", - "ex7-mindPrivacy": "47ce19447d1c3e32a702dd708ed06091e9ea9996e01725b8f8b219f4524782af", - "ex1-doubleEvenNumbers.test": "fca4268f9c4ea8ba7a19d564a7150b3f7f1e2f3fb254d2a0902a0838d067a1dc", - "ex2-mondaysWorth.test": "3205b7ea8a12a08e200fca68febb9d373cfe8b8c005b32c990dbd91d0519c0e6", - "ex3-lemonAllergy.test": "58055218f88d00d396ab254800218a50fcfab10ed0548602aece6040a799c4a8", - "ex4-observable": "7c28d25384d6ef4e06a59ec651a053a2f5781fc555f9df4f2f2aeb7d07507354", - "ex5-wallet": "b2664b9411474a468f999c8c3bb7590046dd3dd3c883795d9ebffbee1140dea4", - "ex1-bookList": "889909bb59ecd158fe43a0bf36bcc4708bbb941e4566bcaf080481bedef7b0f0", - "ex2-aboutMe": "759d12d04b4c42592089e4ad77b0cc9fe440f543220f09eb2bbdccedacbc503f", - "ex3-hijackLogo": "86782e88e1dbb6571bb3576983e37e0293935531932621c13728924f24587aaf", - "ex4-whatsTheTime": "8106f06feb81e186a44a7c8bf57339187717c896f4c726008b85030fb868a391", - "ex5-catWalk": "748e45f93e283bf8972bc64a01f393fb28cb8d50efcfc6a664cf664b7c998508", - "ex6-gameOfLife": "0c29b5725dd8c3cba7e9bb6785ea1729f9229561642b65cff14ee379f9553efb", - "ex1-johnWho": "00f3567ed9d194394b7a5bfe66ff9442bdadf715dd0700be1a9f43667d2fae2a", - "ex2-checkDoubleDigits": "185367ae2f185e90ec03cc220029607981514d909f1b9ecc3a4f01607e14c88c", - "ex3-rollDie": "9ad20d60390ddf6029db0a87007100de4271f9c2056f9b4578bff6013338cb3c", - "ex4-pokerDiceAll": "f9eacfcbbd34dc82b5d1a66810b130929287aada2e0b84ea9bd95d6582f5150d", - "ex5-pokerDiceChain": "23651f5d5cbc70b78728c07290beb9700f7bae71edff6f7bd6bc478f168be1d6", - "ex1-programmerFun": "5f7ecc0ba804c290534a4ec32bb1d940e9783a105581f8c6fd06015a538b5c1c", - "ex2-pokemonApp": "47a8794931e8aaa2c0b24212334cb5a82b25ae09e857806f1debe5b83207f081", - "ex3-rollAnAce": "7d8d1a5bb999d4f3ff74675f57fb9cf2e02d9486c5c0308b2a3d545c59c5adf5", - "ex4-diceRace": "70eb2aa459da54f85959fd1a19d34e0287ea2c1f6f99bcb34686c16f175196c1", - "ex5-vscDebug": "b8f99814051d491d0aebe495dcab79d86ac4c98cecc063cf1604451e9a7a1090", - "ex6-browserDebug": "41899e54aeed71c8f8c2b4b02b264fea8952171b0074351964a28856b9b6e45b" + "1-JavaScript": { + "Week2": { + "ex1-giveCompliment": "c2c8b6253ad706989b9120f00d80e8453b5be70f34c35fcd7567dcf5550ba897", + "ex2-dogYears": "83cde87e41e27739a745da5038550cdb2470ce917b8b6f3b5b5d92d9576c6b8e", + "ex3-tellFortune": "8b2f61a4b1fc2603bac8d09a497fce6febcc94103f4a8eef05562f31410c04e0", + "ex4-shoppingCart": "faa80df26c206721a4126e3e11f2d8601e7a74b5f480b5c2b895cd21573389db", + "ex5-shoppingCartPure": "3476061983b7ffbd6550e5e57fd7499bd2808d31ee2d4bc61cb97404d9f38d44", + "ex6-totalCost": "5b1d0494344f16ac5b707ba868879c799b503b60595afa6684698ea4bf7aa351", + "ex7-mindPrivacy": "f809108686ee3c05c2b7dc39de4ec6259ae48b7940f4cf56f34293302e256380" + }, + "Week3": { + "ex1-doubleEvenNumbers.test": "99e7d34f1878d376c2c14367a5de445a3bf00abee43ff86996f1d2a1bb1bc683", + "ex2-mondaysWorth.test": "25e38c63390f21cac3f8cf83f683f79cb516b94c693c8679f6ddcba341859620", + "ex3-lemonAllergy.test": "a3438041779e7419eac19d7112feff3cf91aec0055810ad9b721683cbf83312e", + "ex4-observable": "4085411da456ca5f34e7bd7c8607e1e1ff6d82c7acec2f823791e4c422961d72", + "ex5-wallet": "2c8d09de71b9044b8107b2e86d7b66a3b82eebea17c17d7153bfc68fdf20da5e" + } + }, + "2-Browsers": { + "Week1": { + "ex1-bookList": "8ac2dee143392160c9d60b181eb3f43b7b1318ea2addfe490c9c1cff048d5d9b", + "ex2-aboutMe": "3fc6e8adc0af28648d048c388dd087a8bbc1d22d8b2aebe7b3329c8423d26810", + "ex3-hijackLogo": "9d5ad72401c231c92e03df70946d9ba40759c780b7c39756d8b17cd15945d241", + "ex4-whatsTheTime": "80ebbed95a4c838d067d9a4eab32bc002101ab60847a2e0bc4ff463e97c354fd", + "ex5-catWalk": "4d75417fd3a7792b084fa234753d4cdd652d40d3990bad13aa7eec11a5db648a", + "ex6-gameOfLife": "10ccd24d0b46559e7c3b778ef9d1659995064f36fb329b7aba2261b65e9f0355" + } + }, + "3-UsingAPIs": { + "Week1": { + "ex1-johnWho": "3a45143e7c62f304fd5bd5eb706cea077912373fa521c80934adac49ec9436ef", + "ex2-checkDoubleDigits": "05d6893c90f828e26c4471b69054252a7c094a8a1ea02e28fb72716230cfbbc8", + "ex3-rollDie": "a3001b96605ee20f187421d3323635c1556cc77c06346ff25d79302a2b798c55", + "ex4-pokerDiceAll": "c03cf0564e4702c69bc4224ceba797efbf69420ed87979cb823c920567866923", + "ex5-pokerDiceChain": "e0b3668e27909a717506dd028b67b7c3b53f8ba1e5b643c39dfb2a40c44d89f4" + }, + "Week2": { + "ex1-programmerFun": "e6cbf8715cf9b840b26fc8d8553dd235ed52b456fa6b37d639f6d54625e58797", + "ex2-pokemonApp": "b9ec9888524d269f507943086df9ccba3ae32c76b1d39ac6565f6f8371c04631", + "ex3-rollAnAce": "8c840efffc0734a9b226588289a44df893955da251120e21ef1a1afa10a5e181", + "ex4-diceRace": "d21ce44070d9ff596b941d138ca1a701fbfc1292afdd412cc8b8951ef512d413", + "ex5-vscDebug": "6242bc8861bdb0abea48d00fa6f2cd9bf3a6f93fdd9b708ad130c8b64c6c75eb", + "ex6-browserDebug": "1787583932555b23bcb625030cfcad2094bc222824dcba2d79db4bc01e6b9142" + } + } } \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 000000000..33d42f29c --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,3 @@ +#!/bin/bash +echo "Branch name: $(git rev-parse --abbrev-ref HEAD)" +npm run pre-commit \ No newline at end of file diff --git a/.husky/pre-push b/.husky/pre-push new file mode 100644 index 000000000..1860e5563 --- /dev/null +++ b/.husky/pre-push @@ -0,0 +1,3 @@ +#!/bin/bash +echo "Branch name: $(git rev-parse --abbrev-ref HEAD)" +npm run pre-push \ No newline at end of file diff --git a/.markdownlint.json b/.markdownlint.json index eef34aa53..e532ef2b8 100644 --- a/.markdownlint.json +++ b/.markdownlint.json @@ -2,5 +2,6 @@ "default": true, "line-length": false, "no-inline-html": false, - "no-duplicate-header": false + "no-duplicate-header": false, + "table-pipe-style": false } diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..f1239caa5 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +# Ignore JSON files +*.json \ No newline at end of file diff --git a/.tours/test-runner.tour b/.tours/test-runner.tour deleted file mode 100644 index 8036f7fb1..000000000 --- a/.tours/test-runner.tour +++ /dev/null @@ -1,147 +0,0 @@ -{ - "$schema": "https://aka.ms/codetour-schema", - "title": "1. test-runner", - "steps": [ - { - "file": "test-runner/index.js", - "description": "The test runner starts by calling function `main()`.", - "line": 231, - "title": "entry point" - }, - { - "file": "test-runner/index.js", - "description": "The function `compileMenuData()` is called to compile menu data by scanning the repo's directory tree, looking for exercise files and directories that match a prescribed naming convention. The returned menu data (a JavaScript object) is used to drive the menu dialog.", - "line": 175, - "title": "get menu data" - }, - { - "file": "test-runner/test-runner-helpers.js", - "description": "Exercise files and folders must reside in a folder named **assignment** and match a pattern that starts with the letters **ex** followed by a number and a dash. Each **assignment** folder is a subfolder of a **Week𝑛** folder, which itself is a subfolder of a module specific top-level folder. For instance:\n\n```console\n1-JavaScript/\n Week3/\n assignment/\n ex1-giveCompliment.js\n ...\n```\n\nNote that Windows paths contain back slashes that must be replaced with forward slashes as required by `fast-glob`.", - "line": 26, - "selection": { - "start": { - "line": 20, - "character": 3 - }, - "end": { - "line": 27, - "character": 1 - } - }, - "title": "compile menu data" - }, - { - "file": "test-runner/test-runner-helpers.js", - "description": "We use a regular expression to extract the **module**, **week** and *exercise* name from each file path. If the exercise is a (JavaScript) file, we strip off the `.js` file extension.", - "line": 43, - "selection": { - "start": { - "line": 28, - "character": 1 - }, - "end": { - "line": 44, - "character": 1 - } - }, - "title": "build menu data object" - }, - { - "file": "test-runner/index.js", - "description": "As a time-saver, a user is given the option to re-run the last test (if there is one). This information is kept in the file `.recent.json`.", - "line": 183, - "selection": { - "start": { - "line": 179, - "character": 1 - }, - "end": { - "line": 184, - "character": 1 - } - }, - "title": "recent selection" - }, - { - "file": "test-runner/index.js", - "description": "If the user wishes to select a different exercise to test (or if there was no previous to re-run) a series of menu prompts allows the user to select a **module**, **week** and **exercise** to test.", - "line": 190, - "title": "selection menu", - "selection": { - "start": { - "line": 185, - "character": 1 - }, - "end": { - "line": 191, - "character": 1 - } - } - }, - { - "file": "test-runner/index.js", - "description": "The students' exercises are in subfolders of the **assignment** folder. For testing the \"happy path\" of the unit tests completed exercises can be placed in an alternate folder, the name of whhich can be defined through the `ASSIGNMENT_FOLDER` environment variable. The recommended folder for this purpose is **@assignment** (git-ignored). The npm script **npm run testalt** sets the `ASSIGNMENT_FOLDER` environment variable to **@assignment** before running the test.", - "line": 198 - }, - { - "file": "test-runner/index.js", - "description": "A hash is computed over each exercise as part of the **posinstall** npm script. These hashes are stored in the file `.hashes.json` (git-ignored). When the use runs a test the hash is recomputed and compare with the stored hash. If the hash values are the same then obviously the exercise has not been touched.", - "line": 201, - "title": "recompute hash", - "selection": { - "start": { - "line": 203, - "character": 1 - }, - "end": { - "line": 207, - "character": 6 - } - } - }, - { - "file": "test-runner/index.js", - "description": "If the exercise is untouched, we log a message to the console and to log file.", - "line": 207, - "selection": { - "start": { - "line": 203, - "character": 1 - }, - "end": { - "line": 208, - "character": 1 - } - }, - "title": "compare hashes" - }, - { - "file": "test-runner/index.js", - "description": "Running a test comprises three steps:\n\n1. Unit test with Jest\n2. ESLint check\n3. Spelling check\n\nEach of these steps returns a (potentially multiline) string with error information or just an empty string if there were no errors. These strings are concatenated to form an error report.", - "line": 213, - "selection": { - "start": { - "line": 210, - "character": 1 - }, - "end": { - "line": 214, - "character": 1 - } - }, - "title": "run test steps" - }, - { - "file": "test-runner/index.js", - "description": "An error report is written to the `test-reports` folder, but only if the student did some work on the exercise.", - "line": 216, - "title": "write report" - }, - { - "file": "test-runner/index.js", - "description": "A textual disclaimer is shown at the end of the test that can be silenced for subsequent tests.", - "line": 220, - "title": "show disclaimer" - } - ] -} \ No newline at end of file diff --git a/.tours/unit-test-browser.tour b/.tours/unit-test-browser.tour deleted file mode 100644 index 7354e8694..000000000 --- a/.tours/unit-test-browser.tour +++ /dev/null @@ -1,88 +0,0 @@ -{ - "$schema": "https://aka.ms/codetour-schema", - "title": "4. unit test browser", - "steps": [ - { - "file": "2-Browsers/Week1/unit-tests/ex4-whatsTheTime.test.js", - "description": "For browser-based exercises we make use of `jsdom` through the helper function `prepare()`.", - "line": 11, - "title": "beforeAll prepare" - }, - { - "file": "test-runner/jsdom-helpers.js", - "description": "We use the JSDOM convenience function `JSDOM.fromFile()` to load `index.html` into jsdom. We add some sleep time to allow JavaScript file used in ` + diff --git a/2-Browsers/Week1/assignment/ex2-aboutMe/index.html b/2-Browsers/Week1/assignment/ex2-aboutMe/index.html index f5f404605..7b3c532ea 100644 --- a/2-Browsers/Week1/assignment/ex2-aboutMe/index.html +++ b/2-Browsers/Week1/assignment/ex2-aboutMe/index.html @@ -1,4 +1,4 @@ - + @@ -15,6 +15,6 @@

About Me

  • Hometown:
  • - + diff --git a/2-Browsers/Week1/assignment/ex2-aboutMe/index.js b/2-Browsers/Week1/assignment/ex2-aboutMe/index.js index d44ae0ddc..a03686b70 100644 --- a/2-Browsers/Week1/assignment/ex2-aboutMe/index.js +++ b/2-Browsers/Week1/assignment/ex2-aboutMe/index.js @@ -1,4 +1,3 @@ -'use strict'; /*------------------------------------------------------------------------------ Full description at: https://github.com/HackYourFuture/Assignments/tree/main/2-Browsers/Week1#exercise-2-about-me diff --git a/2-Browsers/Week1/assignment/ex3-hijackLogo.js b/2-Browsers/Week1/assignment/ex3-hijackLogo.js index c52f6d744..1b3d596e9 100644 --- a/2-Browsers/Week1/assignment/ex3-hijackLogo.js +++ b/2-Browsers/Week1/assignment/ex3-hijackLogo.js @@ -1,4 +1,3 @@ -'use strict'; /*------------------------------------------------------------------------------ Full description at: https://github.com/HackYourFuture/Assignments/tree/main/2-Browsers/Week1#exercise-3-the-logo-hijack diff --git a/2-Browsers/Week1/assignment/ex4-whatsTheTime/index.html b/2-Browsers/Week1/assignment/ex4-whatsTheTime/index.html index db4232201..5dac7dab6 100644 --- a/2-Browsers/Week1/assignment/ex4-whatsTheTime/index.html +++ b/2-Browsers/Week1/assignment/ex4-whatsTheTime/index.html @@ -1,4 +1,4 @@ - + @@ -6,6 +6,6 @@ What's The Time? - + diff --git a/2-Browsers/Week1/assignment/ex4-whatsTheTime/index.js b/2-Browsers/Week1/assignment/ex4-whatsTheTime/index.js index 0724f2d72..30dbdd61d 100644 --- a/2-Browsers/Week1/assignment/ex4-whatsTheTime/index.js +++ b/2-Browsers/Week1/assignment/ex4-whatsTheTime/index.js @@ -1,4 +1,3 @@ -'use strict'; /*------------------------------------------------------------------------------ Full description at: https://github.com/HackYourFuture/Assignments/tree/main/2-Browsers/Week1#exercise-4-whats-the-time diff --git a/2-Browsers/Week1/assignment/ex5-catWalk/index.html b/2-Browsers/Week1/assignment/ex5-catWalk/index.html index 58c619a27..7403bec69 100644 --- a/2-Browsers/Week1/assignment/ex5-catWalk/index.html +++ b/2-Browsers/Week1/assignment/ex5-catWalk/index.html @@ -1,4 +1,4 @@ - + @@ -15,6 +15,6 @@ src="http://www.anniemation.com/clip_art/images/cat-walk.gif" alt="Cat walking" /> - + diff --git a/2-Browsers/Week1/assignment/ex5-catWalk/index.js b/2-Browsers/Week1/assignment/ex5-catWalk/index.js index ad7c83fab..aedb02011 100644 --- a/2-Browsers/Week1/assignment/ex5-catWalk/index.js +++ b/2-Browsers/Week1/assignment/ex5-catWalk/index.js @@ -1,4 +1,3 @@ -'use strict'; /*------------------------------------------------------------------------------ Full description at: https://github.com/HackYourFuture/Assignments/tree/main/2-Browsers/Week1#exercise-5-the-cat-walk diff --git a/2-Browsers/Week1/assignment/ex6-gameOfLife/index.html b/2-Browsers/Week1/assignment/ex6-gameOfLife/index.html index 97b895737..2bb49e7a6 100644 --- a/2-Browsers/Week1/assignment/ex6-gameOfLife/index.html +++ b/2-Browsers/Week1/assignment/ex6-gameOfLife/index.html @@ -1,4 +1,4 @@ - + @@ -35,7 +35,7 @@

    >Wikipedia - Conway's Game of Life

    - + diff --git a/2-Browsers/Week1/assignment/ex6-gameOfLife/index.js b/2-Browsers/Week1/assignment/ex6-gameOfLife/index.js index b744fee03..a0955336a 100644 --- a/2-Browsers/Week1/assignment/ex6-gameOfLife/index.js +++ b/2-Browsers/Week1/assignment/ex6-gameOfLife/index.js @@ -1,4 +1,4 @@ -'use strict'; +// @ts-check /*------------------------------------------------------------------------------ Full description at: https://github.com/HackYourFuture/Assignments/tree/main/2-Browsers/Week1#exercise-6-conways-game-of-life @@ -11,8 +11,23 @@ const CELL_SIZE = 10; const NUM_COLUMNS = 75; const NUM_ROWS = 40; -// Create a cell with the given coordinates and randomly assign its begin state: -// life or death +/** + * @typedef {Object} GridCell + * @property {number} x + * @property {number} y + * @property {boolean} alive + * @property {boolean} [nextAlive] + */ + +/** @typedef {GridCell[]} GridRow */ + +/** + * Create a cell with the given coordinates and randomly assign its begin state: + * life or death + * @param {number} x + * @param {number} y + * @returns {GridCell} + */ function createCell(x, y) { const alive = Math.random() > 0.5; return { @@ -22,13 +37,21 @@ function createCell(x, y) { }; } -// Create the game "engine" with a closure -function createGame(context, numRows, numColumns) { +/** + * Create the game "engine" with a closure + * @param {CanvasRenderingContext2D} context + * @param {number} numRows + * @param {number} numColumns + * @returns + */ +export function createGame(context, numRows, numColumns) { + /** @type {GridRow[]} */ const grid = []; // Create the grid as a two-dimensional array (i.e. an array of arrays) function createGrid() { for (let y = 0; y < numRows; y++) { + /** @type {GridRow} */ const row = []; for (let x = 0; x < numColumns; x++) { const cell = createCell(x, y); @@ -38,14 +61,20 @@ function createGame(context, numRows, numColumns) { } } - // Execute a callback for each cell in the grid + /** + * Execute a callback for each cell in the grid + * @param {(cell: GridCell) => void} callback + */ function forEachCell(callback) { grid.forEach((row) => { row.forEach((cell) => callback(cell)); }); } - // Draw a cell onto the canvas + /** + * Draw a cell onto the canvas + * @param {GridCell} cell + */ function drawCell(cell) { // Draw cell background context.fillStyle = '#303030'; @@ -68,7 +97,12 @@ function createGame(context, numRows, numColumns) { } } - // Check the state of the cell at the given coordinates + /** + * Check the state of the cell at the given coordinates + * @param {number} x + * @param {number} y + * @returns {0 | 1} + */ function isAlive(x, y) { // Out-of-border cells are presumed dead if (x < 0 || x >= numColumns || y < 0 || y >= numRows) { @@ -78,7 +112,11 @@ function createGame(context, numRows, numColumns) { return grid[y][x].alive ? 1 : 0; } - // Count the number of living neighboring cells for a given cell + /** + * Count the number of living neighboring cells for a given cell + * @param {GridCell} cell + * @returns {number} + */ function countLivingNeighbors(cell) { const { x, y } = cell; return ( @@ -93,8 +131,10 @@ function createGame(context, numRows, numColumns) { ); } - // Update the state of the cells in the grid by applying the Game Of Life - // rules on each cell. + /** + * Update the state of the cells in the grid by applying the Game Of Life + * rules on each cell. + */ function updateGrid() { // Loop over all cells to determine their next state. forEachCell((cell) => { @@ -115,17 +155,23 @@ function createGame(context, numRows, numColumns) { // Apply the newly computed state to the cells forEachCell((cell) => { - cell.alive = cell.nextAlive; + cell.alive = cell.nextAlive ?? false; }); } - // Render a visual representation of the grid + // + + /** + * Render a visual representation of the grid + */ function renderGrid() { // Draw all cells in the grid forEachCell(drawCell); } - // Execute one game cycle + /** + * Execute one game cycle + */ function gameLoop() { // Update the state of cells in the grid updateGrid(); @@ -139,7 +185,9 @@ function createGame(context, numRows, numColumns) { }, 200); } - // Starts the game + /** + * Start the game + */ function start() { // Create initial grid createGrid(); @@ -158,11 +206,18 @@ function main() { // Resize the canvas to accommodate the desired number of cell rows and // columns const canvas = document.getElementById('canvas'); + if (!(canvas instanceof HTMLCanvasElement)) { + throw new Error('Canvas element not found'); + } + canvas.height = NUM_ROWS * CELL_SIZE; canvas.width = NUM_COLUMNS * CELL_SIZE; // Obtain a context that is needed to draw on the canvas const context = canvas.getContext('2d'); + if (!(context instanceof CanvasRenderingContext2D)) { + throw new Error('Context not found'); + } // Create the game "engine" const { start } = createGame(context, NUM_ROWS, NUM_COLUMNS); @@ -177,5 +232,3 @@ try { } catch { // ignore if running in node with jest } - -module.exports = createGame; diff --git a/2-Browsers/Week1/test-reports/ex1-bookList.todo.txt b/2-Browsers/Week1/test-reports/ex1-bookList.todo.txt deleted file mode 100644 index d8d868975..000000000 --- a/2-Browsers/Week1/test-reports/ex1-bookList.todo.txt +++ /dev/null @@ -1 +0,0 @@ -This test has not been run. \ No newline at end of file diff --git a/2-Browsers/Week1/test-reports/ex2-aboutMe.todo.txt b/2-Browsers/Week1/test-reports/ex2-aboutMe.todo.txt deleted file mode 100644 index d8d868975..000000000 --- a/2-Browsers/Week1/test-reports/ex2-aboutMe.todo.txt +++ /dev/null @@ -1 +0,0 @@ -This test has not been run. \ No newline at end of file diff --git a/2-Browsers/Week1/test-reports/ex3-hijackLogo.todo.txt b/2-Browsers/Week1/test-reports/ex3-hijackLogo.todo.txt deleted file mode 100644 index d8d868975..000000000 --- a/2-Browsers/Week1/test-reports/ex3-hijackLogo.todo.txt +++ /dev/null @@ -1 +0,0 @@ -This test has not been run. \ No newline at end of file diff --git a/2-Browsers/Week1/test-reports/ex4-whatsTheTime.todo.txt b/2-Browsers/Week1/test-reports/ex4-whatsTheTime.todo.txt deleted file mode 100644 index d8d868975..000000000 --- a/2-Browsers/Week1/test-reports/ex4-whatsTheTime.todo.txt +++ /dev/null @@ -1 +0,0 @@ -This test has not been run. \ No newline at end of file diff --git a/2-Browsers/Week1/test-reports/ex5-catWalk.todo.txt b/2-Browsers/Week1/test-reports/ex5-catWalk.todo.txt deleted file mode 100644 index d8d868975..000000000 --- a/2-Browsers/Week1/test-reports/ex5-catWalk.todo.txt +++ /dev/null @@ -1 +0,0 @@ -This test has not been run. \ No newline at end of file diff --git a/2-Browsers/Week1/test-reports/ex6-gameOfLife.todo.txt b/2-Browsers/Week1/test-reports/ex6-gameOfLife.todo.txt deleted file mode 100644 index d8d868975..000000000 --- a/2-Browsers/Week1/test-reports/ex6-gameOfLife.todo.txt +++ /dev/null @@ -1 +0,0 @@ -This test has not been run. \ No newline at end of file diff --git a/2-Browsers/Week1/unit-tests/.verbose b/2-Browsers/Week1/unit-tests/.verbose deleted file mode 100644 index e69de29bb..000000000 diff --git a/2-Browsers/Week1/unit-tests/ex1-bookList.test.js b/2-Browsers/Week1/unit-tests/ex1-bookList.test.ts similarity index 55% rename from 2-Browsers/Week1/unit-tests/ex1-bookList.test.js rename to 2-Browsers/Week1/unit-tests/ex1-bookList.test.ts index c6b8fd4fa..3be6b13e4 100644 --- a/2-Browsers/Week1/unit-tests/ex1-bookList.test.js +++ b/2-Browsers/Week1/unit-tests/ex1-bookList.test.ts @@ -1,47 +1,66 @@ -/* eslint-disable hyf/camelcase */ -const walk = require('acorn-walk'); -const { +import { simple } from 'acorn-walk'; + +import { DOMWindow } from 'jsdom'; +import { prepare, validateHTML } from '../../../test-runner/jsdom-helpers.js'; +import { beforeAllHelper, + ExerciseInfo, testTodosRemoved, -} = require('../../../test-runner/unit-test-helpers'); -const { prepare, validateHTML } = require('../../../test-runner/jsdom-helpers'); +} from '../../../test-runner/unit-test-helpers.js'; + +type State = { + outerHTML: string; + titlesAndAuthors: string[]; +}; + +describe('br-wk1-ex1-bookList', () => { + const state: State = { + outerHTML: '', + titlesAndAuthors: [], + }; -describe('Generated HTML', () => { - const state = {}; - let document, source, rootNode; + let exInfo: ExerciseInfo; + let document: DOMWindow['document']; beforeAll(async () => { ({ document } = await prepare()); state.outerHTML = document.documentElement.outerHTML; - ({ rootNode, source } = beforeAllHelper(__filename, { - noRequire: true, - parse: true, - })); + exInfo = await beforeAllHelper(__filename, { noImport: true }); - rootNode && - walk.simple(rootNode, { + exInfo.rootNode && + simple(exInfo.rootNode, { VariableDeclarator({ id, init }) { - if (id.name === 'myBooks' && init.type === 'ArrayExpression') { + if ( + id.type === 'Identifier' && + id.name === 'myBooks' && + init?.type === 'ArrayExpression' + ) { state.titlesAndAuthors = init.elements.reduce((acc, element) => { - if (element.type === 'ObjectExpression') { + if (element?.type === 'ObjectExpression') { element.properties.forEach((prop) => { - if (['title', 'author'].includes(prop.key.name)) { + if ( + prop.type === 'Property' && + prop.key.type === 'Identifier' && + prop.value.type === 'Literal' && + typeof prop.value.value === 'string' && + ['title', 'author'].includes(prop.key.name) + ) { acc.push(prop.value.value); } }); } return acc; - }, []); + }, [] as string[]); } }, }); }); test('HTML should be syntactically valid', () => - validateHTML(state.outerHTML)); + validateHTML(state.outerHTML!)); - testTodosRemoved(() => source); + testTodosRemoved(() => exInfo.source); test('should contain a