diff --git a/slides.md b/slides.md
index b73a20d..7445180 100644
--- a/slides.md
+++ b/slides.md
@@ -47,7 +47,7 @@ Test runners can be part of a larger **test framework** or standalone tools.
- Test runners are tools designed to execute your test suites and report the results. They are essential in automating the testing process.
- Python: `pytest` is widely appreciated for its powerful features and simple syntax, making it suitable for both simple and complex projects.
- Java: `JUnit` is the de facto standard for unit testing in Java development, known for its rich annotation-based configuration.
-- JavaScript: `Mocha` is a flexible test framework with a focus on asynchronous testing, offering rich features for running tests in Node.js and the browser.
+- JavaScript: `Jest` is a flexible test framework with a focus on asynchronous testing, offering rich features for running tests in Node.js and the browser.
- .NET: `NUnit` is a popular choice for .NET developers, similar to JUnit but with a focus on the .NET framework.
@@ -62,7 +62,7 @@ Choosing the right test runner involves considering the programming language, pr
- Test Runner: A tool that executes tests and reports the results. It is responsible for loading your test code, running it, and then providing feedback.
- Testing Framework: Provides the structure and guidelines for writing tests. It includes assertions, test cases, and test suites, but doesn't run tests by itself.
- The main difference lies in their roles; while a testing framework defines how to write tests, a test runner actually executes them.
-- Some tools, like `pytest` and `Mocha`, combine both functionalities, acting as both test runners and frameworks.
+- Some tools, like `pytest` and `Jest`, combine both functionalities, acting as both test runners and frameworks.
---
@@ -89,7 +89,7 @@ Choosing the right test runner involves considering the programming language, pr
# Workshop setup
-- This workshop will introduce to the Node.js test runner with 11 excercises
+- This workshop will introduce to the Node.js test runner with a series of exercises
- At each step you're asked to use a different test runner feature
- The 💡 icon indicates hints
@@ -539,17 +539,124 @@ You can reference the [`--import` official documentation](https://nodejs.org/api
---
-# Other useful resources
+# A11 Timers
+
+
+
+- Timers are crucial for testing **time-dependent functionality** in applications, such as debouncing, throttling, or any operation that relies on time delays.
+- Using real timers in tests can lead to unpredictable results and slow down the testing process, as tests have to wait for the actual time to pass.
+- The Node.js test runner offers a way to **mock timers**, enabling tests to simulate the passage of time instantly.
+- Developers can enable mocked versions of timers like `setTimeout` and `setInterval` that can be controlled programmatically.
+
+
---
-# Thanks For Having Us!
+# A11 The problem
-## 👏👏👏
+- In the `test` folder, there is a `index.test.js` file
+- The function to test, contains a `setTimeout`
+- During testing, this can lead to slow and unpredictable tests
+- Apply [timers mocking](https://nodejs.org/api/test.html#timers) in the test file
-````
+---
+
+# A11 Solution 💡
+
+```javascript
+test('delayedHello executes the callback after the specified delay', () => {
+ const fn = mock.fn()
+ mock.timers.enable({ apis: ['setTimeout'] })
+ delayedHello(fn, 5000)
+
+ // Initially, the callback has not been called
+ assert.strictEqual(fn.mock.calls.length, 0)
+ // Advance time by 5000 milliseconds
+ mock.timers.tick(5000)
+ // Now, the callback should have been called once
+ assert.strictEqual(fn.mock.calls.length, 1)
+ assert.strictEqual(fn.mock.calls[0][0], 'Hello, World!')
+
+ mock.timers.reset()
+})
```
+---
+
+# A12 Context
+
+
+
+- The `context` object is essential for managing test lifecycles, including setup and teardown processes.
+- It provides hooks (`before`, `beforeEach`, `after`, `afterEach`) for preparing and cleaning up before and after tests or a group of tests.
+- Enables control over test execution through methods like `skip` (to bypass tests), `todo` (to mark tests as pending), and `runOnly` (to execute only specified tests).
+- Offers a `diagnostic` method for logging debug information and a signal property for aborting tests programmatically.
+- Supports **hierarchical test structuring** with the test method, allowing for the creation of subtests that inherit the context of their parent test.
+- Facilitates grouping related tests by using `beforeEach` and `afterEach` hooks for shared setup and cleanup, ensuring a well-organized and maintainable test suite.
+
+
+
+---
+
+# A12 The problem
+
+- In this exercise about context, we will focus on child tests (also known as subtests)
+- In the file `index.test.js` you will find multiple tests for the `sum` and the `average` functions
+- Group together all the subtests related to the same function
+
+---
+
+# A12 Solution 💡
+
+```javascript
+// Grouping tests for `sum` function
+test('sum function tests', async t => {
+ await t.test('Sum works correctly with valid input', () => {
+ assert.deepStrictEqual(sum([1, 2, 3]), 6)
+ })
+
+ await t.test('Sum returns 0 in case of empty array', () => {
+ assert.deepStrictEqual(sum([]), 0)
+ })
+
+ await t.test('Sum throws in case of bad input', () => {
+ assert.throws(() => sum('abc'), {
+ message: 'Input must be an array of numbers'
+ })
+ })
+})
```
-````
+
+---
+
+# A12 Solution 💡 (2)
+
+```javascript
+// Grouping tests for `average` function
+test('average function tests', async t => {
+ await t.test('Average works correctly with valid input', () => {
+ assert.deepStrictEqual(average([1, 2, 3]), 2)
+ })
+
+ await t.test('Average returns 0 in case of empty array', () => {
+ assert.deepStrictEqual(average([]), 0)
+ })
+
+ await t.test('Average throws in case of bad input', () => {
+ assert.throws(() => average('abc'), {
+ message: 'Input must be an array of numbers'
+ })
+ })
+})
+```
+
+---
+
+# Other useful resources
+
+---
+
+# Thanks For Having Us!
+
+## 👏👏👏
diff --git a/src/a11-timers/README.md b/src/a11-timers/README.md
new file mode 100644
index 0000000..f6ef1ba
--- /dev/null
+++ b/src/a11-timers/README.md
@@ -0,0 +1,7 @@
+# a11-timers
+
+- Open the file `test/index.test.js`.
+
+- Use the [Timers API](https://nodejs.org/api/test.html#timers) to mock the `setTimeout`
+
+- Run in the terminal `node --test`.
diff --git a/src/a11-timers/package.json b/src/a11-timers/package.json
new file mode 100644
index 0000000..0ef2230
--- /dev/null
+++ b/src/a11-timers/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "a11-timers",
+ "version": "1.0.0",
+ "description": "",
+ "type": "module",
+ "main": "index.js",
+ "scripts": {
+ "test": "node --test",
+ "solution": "node --test ./test/solution.test.js"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC"
+}
diff --git a/src/a11-timers/src/index.js b/src/a11-timers/src/index.js
new file mode 100644
index 0000000..9799d9d
--- /dev/null
+++ b/src/a11-timers/src/index.js
@@ -0,0 +1,5 @@
+export function delayedHello(callback, delay) {
+ setTimeout(() => {
+ callback('Hello, World!')
+ }, delay)
+}
diff --git a/src/a11-timers/test/index.test.js b/src/a11-timers/test/index.test.js
new file mode 100644
index 0000000..6ce971c
--- /dev/null
+++ b/src/a11-timers/test/index.test.js
@@ -0,0 +1,24 @@
+/* eslint-disable no-unused-vars */
+import assert from 'node:assert'
+import { delayedHello } from '../src/index.js'
+import { test, mock } from 'node:test'
+
+test('delayedHello executes the callback after the specified delay', () => {
+ const fn = mock.fn()
+
+ // Enable mocking of setTimeout
+
+ // Call the function with a mock callback and a delay
+ //delayedHello(fn, 5000)
+
+ // Initially, the callback has not been called
+ //assert.strictEqual(fn.mock.calls.length, 0)
+
+ // Advance time by 5000 milliseconds
+
+ // Now, the callback should have been called once
+ //assert.strictEqual(fn.mock.calls.length, 1)
+ //assert.strictEqual(fn.mock.calls[0][0], 'Hello, World!')
+
+ // Reset mock timers after the test
+})
diff --git a/src/a11-timers/test/solution.test.js b/src/a11-timers/test/solution.test.js
new file mode 100644
index 0000000..4e42773
--- /dev/null
+++ b/src/a11-timers/test/solution.test.js
@@ -0,0 +1,20 @@
+import assert from 'node:assert'
+import { delayedHello } from '../src/index.js'
+import { test, mock } from 'node:test'
+
+test('delayedHello executes the callback after the specified delay', () => {
+ const fn = mock.fn()
+
+ mock.timers.enable({ apis: ['setTimeout'] })
+ delayedHello(fn, 5000)
+
+ // Initially, the callback has not been called
+ assert.strictEqual(fn.mock.calls.length, 0)
+ // Advance time by 5000 milliseconds
+ mock.timers.tick(5000)
+ // Now, the callback should have been called once
+ assert.strictEqual(fn.mock.calls.length, 1)
+ assert.strictEqual(fn.mock.calls[0].arguments[0], 'Hello, World!')
+
+ mock.timers.reset()
+})
diff --git a/src/a12-context/README.md b/src/a12-context/README.md
new file mode 100644
index 0000000..5f92736
--- /dev/null
+++ b/src/a12-context/README.md
@@ -0,0 +1,7 @@
+# a12-context
+
+- Open the file `test/index.test.js`.
+
+- Group all the subtests of a specific function with a context
+
+- Run in the terminal `node --test`.
diff --git a/src/a12-context/package.json b/src/a12-context/package.json
new file mode 100644
index 0000000..ca14aa7
--- /dev/null
+++ b/src/a12-context/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "a12-context",
+ "version": "1.0.0",
+ "description": "",
+ "type": "module",
+ "main": "index.js",
+ "scripts": {
+ "test": "node --test",
+ "solution": "node --test ./test/solution.test.js"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC"
+}
diff --git a/src/a12-context/src/index.js b/src/a12-context/src/index.js
new file mode 100644
index 0000000..ea97b09
--- /dev/null
+++ b/src/a12-context/src/index.js
@@ -0,0 +1,20 @@
+export function sum(numbers) {
+ if (!Array.isArray(numbers)) {
+ throw new Error('Input must be an array of numbers')
+ }
+
+ return numbers.reduce((acc, num) => acc + num, 0)
+}
+
+export function average(numbers) {
+ if (!Array.isArray(numbers)) {
+ throw new Error('Input must be an array of numbers')
+ }
+
+ if (numbers.length === 0) {
+ return 0
+ }
+
+ const sum = numbers.reduce((acc, num) => acc + num, 0)
+ return sum / numbers.length
+}
diff --git a/src/a12-context/test/index.test.js b/src/a12-context/test/index.test.js
new file mode 100644
index 0000000..e1744f1
--- /dev/null
+++ b/src/a12-context/test/index.test.js
@@ -0,0 +1,31 @@
+import { average, sum } from '../src/index.js'
+import { test } from 'node:test'
+import assert from 'node:assert'
+
+test('Sum works correctly with valid input', () => {
+ assert.deepStrictEqual(sum([1, 2, 3]), 6)
+})
+
+test('Sum returns 0 in case of empty array', () => {
+ assert.deepStrictEqual(sum([]), 0)
+})
+
+test('Sum throws in case of bad input', () => {
+ assert.throws(() => sum('abc'), {
+ message: 'Input must be an array of numbers'
+ })
+})
+
+test('Average works correctly with valid input', () => {
+ assert.deepStrictEqual(average([1, 2, 3]), 2)
+})
+
+test('Average returns 0 in case of empty array', () => {
+ assert.deepStrictEqual(average([]), 0)
+})
+
+test('Average throws in case of bad input', () => {
+ assert.throws(() => average('abc'), {
+ message: 'Input must be an array of numbers'
+ })
+})
diff --git a/src/a12-context/test/solution.test.js b/src/a12-context/test/solution.test.js
new file mode 100644
index 0000000..5c188cc
--- /dev/null
+++ b/src/a12-context/test/solution.test.js
@@ -0,0 +1,37 @@
+import { average, sum } from '../src/index.js'
+import { test } from 'node:test'
+import assert from 'node:assert'
+
+// Grouping tests for `sum` function
+test('sum function tests', async t => {
+ await t.test('Sum works correctly with valid input', () => {
+ assert.deepStrictEqual(sum([1, 2, 3]), 6)
+ })
+
+ await t.test('Sum returns 0 in case of empty array', () => {
+ assert.deepStrictEqual(sum([]), 0)
+ })
+
+ await t.test('Sum throws in case of bad input', () => {
+ assert.throws(() => sum('abc'), {
+ message: 'Input must be an array of numbers'
+ })
+ })
+})
+
+// Grouping tests for `average` function
+test('average function tests', async t => {
+ await t.test('Average works correctly with valid input', () => {
+ assert.deepStrictEqual(average([1, 2, 3]), 2)
+ })
+
+ await t.test('Average returns 0 in case of empty array', () => {
+ assert.deepStrictEqual(average([]), 0)
+ })
+
+ await t.test('Average throws in case of bad input', () => {
+ assert.throws(() => average('abc'), {
+ message: 'Input must be an array of numbers'
+ })
+ })
+})