diff --git a/6-refactoring/exercises/state/list-items/data/constants.js b/6-refactoring/exercises/state/list-items/data/constants.js
index e69de29b..bd3f17b1 100644
--- a/6-refactoring/exercises/state/list-items/data/constants.js
+++ b/6-refactoring/exercises/state/list-items/data/constants.js
@@ -0,0 +1,3 @@
+
+
+export const bulletPoint = '*';
diff --git a/6-refactoring/exercises/state/list-items/src/handler.js b/6-refactoring/exercises/state/list-items/src/handler.js
index 911a1c5d..01ac8ddb 100644
--- a/6-refactoring/exercises/state/list-items/src/handler.js
+++ b/6-refactoring/exercises/state/list-items/src/handler.js
@@ -1,4 +1,18 @@
-import { list } from './utils.js';
-import { bulletPoint } from '../data/constants.js';
+import { generateList, displayResult, getUserInput } from './utils.js'; // Import utility functions
+import { bulletPoint } from './constants.js'; // Import constant bulletPoint
-export const listHandler = () => {};
+/*export function handleListButtonClick() {
+ // read & process user input
+ const allInputs = getUserInput();
+
+ // execute core logic
+ const stringList = generateList(allInputs, bulletPoint);
+
+ // communicate result to user
+ const message = `all items:${stringList}`;
+ displayResult(message);
+}*/
+const stringList = list(allInputs);
+
+// Add event listener
+document.getElementById('list-them').addEventListener('click', handleListButtonClick);
diff --git a/6-refactoring/exercises/state/list-items/src/init.js b/6-refactoring/exercises/state/list-items/src/init.js
index f7e55374..a95207ff 100644
--- a/6-refactoring/exercises/state/list-items/src/init.js
+++ b/6-refactoring/exercises/state/list-items/src/init.js
@@ -1,4 +1,15 @@
-const bulletPoint = '*'; // data to refactor out of this file
+import { setupEventListener } from './listeners.js';
+
+// This is where you would initialize your application
+// For example, setting up initial state or performing any setup tasks
+
+// Call the function to set up event listeners
+setupEventListener();
+
+
+
+
+/*const bulletPoint = '*'; // data to refactor out of this file
document.getElementById('list-them').addEventListener('click', () => {
// read & process user input
@@ -22,4 +33,4 @@ document.getElementById('list-them').addEventListener('click', () => {
// communicate result to user
const message = `all items:${stringList}`;
alert(message);
-});
+});*/
diff --git a/6-refactoring/exercises/state/list-items/src/listener.js b/6-refactoring/exercises/state/list-items/src/listener.js
index 10f33d91..39848ef9 100644
--- a/6-refactoring/exercises/state/list-items/src/listener.js
+++ b/6-refactoring/exercises/state/list-items/src/listener.js
@@ -1 +1,11 @@
import { listHandler } from './handler.js';
+import { bulletPoint } from './constants.js';
+
+const setupEventListener = () => {
+document.getElementById('list-them').addEventListener('click', () => {
+ const allInputs = getUserInput();
+ const stringList = generateList(allInputs, bulletPoint);
+ const message = `All items:${stringList}`;
+ displayResult(message);
+});
+}
diff --git a/6-refactoring/exercises/state/list-items/src/util.js b/6-refactoring/exercises/state/list-items/src/util.js
index d8b1127b..a765fb99 100644
--- a/6-refactoring/exercises/state/list-items/src/util.js
+++ b/6-refactoring/exercises/state/list-items/src/util.js
@@ -1,4 +1,29 @@
/**
*
*/
-export const list = () => {};
+export function generateList(allInputs, bulletPoint) {
+ let stringList = '';
+ for (const input of allInputs) {
+ stringList += `\n${bulletPoint} ${input}`;
+ }
+ return stringList;
+ }
+
+ export function displayResult(message) {
+ alert(message);
+ }
+
+ export function getUserInput() {
+ const allInputs = [];
+ let acceptingInput = true;
+ while (acceptingInput) {
+ const nextInput = prompt('Enter a list item');
+ if (nextInput !== null) {
+ allInputs.push(nextInput);
+ } else {
+ acceptingInput = false;
+ }
+ }
+ return allInputs;
+ }
+
diff --git a/6-refactoring/exercises/state/list-items/src/util.spec.js b/6-refactoring/exercises/state/list-items/src/util.spec.js
index 22adc8cb..74f91390 100644
--- a/6-refactoring/exercises/state/list-items/src/util.spec.js
+++ b/6-refactoring/exercises/state/list-items/src/util.spec.js
@@ -1,33 +1,46 @@
-import { list } from './util.js';
+import { generateList, displayResult, getUserInput } from './util.js'; // Import correct functions from util.js
describe('list: generates a list string from an array of strings', () => {
describe('list: correctly lists items', () => {
it('an empty array returns an empty string', () => {
const expected = '';
- const actual = list([]);
+ const actual = generateList([]); // Call generateList function
expect(actual).toEqual(expected);
});
+
it('can list a single item', () => {
const expected = '\n- hi';
- const actual = list(['hi'], '-');
+ const actual = generateList(['hi'], '-'); // Call generateList function
expect(actual).toEqual(expected);
});
+
it('can list many items', () => {
const expected = '\n+ a\n+ b\n+ c\n+ d\n+ e';
- const actual = list(['a', 'b', 'c', 'd', 'e'], '+');
+ const actual = generateList(['a', 'b', 'c', 'd', 'e'], '+'); // Call generateList function
expect(actual).toEqual(expected);
});
+
it('can use different bullet points', () => {
const expected = '\n* a\n* b\n* c\n* d\n* e';
- const actual = list(['a', 'b', 'c', 'd', 'e'], '*');
+ const actual = generateList(['a', 'b', 'c', 'd', 'e'], '*'); // Call generateList function
expect(actual).toEqual(expected);
});
});
+
+describe('list: generates a list string from an array of strings', () => {
describe('list: uses arguments correctly', () => {
it('does not modify the array argument', () => {
+ // Mock the prompt function
+ global.prompt = jest.fn(() => null);
+
const arg = ['a', 'b', 'c', 'd'];
- list(arg);
+ const result = getUserInput(arg);
expect(arg).toEqual(['a', 'b', 'c', 'd']);
+
+ // Remove the mock to avoid affecting other tests
+ global.prompt.mockRestore();
});
});
});
+
+ });
diff --git a/6-refactoring/exercises/state/no-copies/data/state.js b/6-refactoring/exercises/state/no-copies/data/state.js
index b6e03096..044af445 100644
--- a/6-refactoring/exercises/state/no-copies/data/state.js
+++ b/6-refactoring/exercises/state/no-copies/data/state.js
@@ -1 +1,9 @@
-export const state = _;
+
+const state = {
+ data: {
+ noCopies: [],
+ }
+ };
+
+export default state;
+
diff --git a/6-refactoring/exercises/state/no-copies/src/handler.js b/6-refactoring/exercises/state/no-copies/src/handler.js
index af72f6c0..688aa794 100644
--- a/6-refactoring/exercises/state/no-copies/src/handler.js
+++ b/6-refactoring/exercises/state/no-copies/src/handler.js
@@ -1,4 +1,14 @@
-import { saveNoCopies } from './utils.js';
+import { saveNoCopies } from './util.js';
import { state } from '../data/state.js';
-export const saveNoCopiesHandler = () => {};
+export const saveNoCopiesHandler = () => {
+
+
+ // communicate result to user
+ const message = saveNoCopies();
+ alert(message);
+
+ // log interaction
+ console.log(data);
+ };
+ export default saveNoCopiesHandler;
\ No newline at end of file
diff --git a/6-refactoring/exercises/state/no-copies/src/init.js b/6-refactoring/exercises/state/no-copies/src/init.js
index 4ef5574e..060302ae 100644
--- a/6-refactoring/exercises/state/no-copies/src/init.js
+++ b/6-refactoring/exercises/state/no-copies/src/init.js
@@ -1,25 +1,25 @@
// data to refactor out of this file
-const data = {
- noCopies: [],
-};
+import callEventListener from "./listener.js";
-document.getElementById('no-copies-button').addEventListener('click', () => {
- // read & process user input
- let userInput = null;
- while (userInput === null) {
- userInput = prompt('enter a string to save');
- }
+callEventListener();
- // execute core logic
- const alreadySaved = data.noCopies.includes(userInput);
- if (!alreadySaved) {
- data.noCopies.push(userInput);
- }
+// document.getElementById('no-copies-button').addEventListener('click', () => {
+// // read & process user input
+// let userInput = null;
+// while (userInput === null) {
+// userInput = prompt('enter a string to save');
+// }
- // communicate result to user
- const message = data.noCopies.join(', ');
- alert(message);
+// // execute core logic
+// const alreadySaved = data.noCopies.includes(userInput);
+// if (!alreadySaved) {
+// data.noCopies.push(userInput);
+// }
- // log interaction
- console.log(data);
-});
+// // communicate result to user
+// const message = data.noCopies.join(', ');
+// alert(message);
+
+// // log interaction
+// console.log(data);
+// });
diff --git a/6-refactoring/exercises/state/no-copies/src/listener.js b/6-refactoring/exercises/state/no-copies/src/listener.js
index f1dc26f7..b47a59a7 100644
--- a/6-refactoring/exercises/state/no-copies/src/listener.js
+++ b/6-refactoring/exercises/state/no-copies/src/listener.js
@@ -1 +1,12 @@
-import { saveNoCopiesHandler } from './handler.js';
+import saveNoCopiesHandler from './handler.js';
+
+
+const callEventListener = () => {
+ // document
+ // .getElementById(TEMPERATURE_INPUT)
+ // .addEventListener('change', handlers);
+ document.getElementById('no-copies-button').addEventListener('click', saveNoCopiesHandler);
+
+ };
+
+ export default callEventListener;
\ No newline at end of file
diff --git a/6-refactoring/exercises/state/no-copies/src/util.js b/6-refactoring/exercises/state/no-copies/src/util.js
index 44848c21..4d6d4d73 100644
--- a/6-refactoring/exercises/state/no-copies/src/util.js
+++ b/6-refactoring/exercises/state/no-copies/src/util.js
@@ -1,4 +1,20 @@
/**
*
*/
-export const saveNoCopies = () => {};
+import state from '../data/state.js';
+
+export const saveNoCopies = () => {
+ let userInput = null;
+ while (userInput === null) {
+ userInput = prompt('enter a string to save');
+ }
+
+ // execute core logic
+ const alreadySaved = state.data.noCopies.includes(userInput);
+ if (!alreadySaved) {
+ state.data.noCopies.push(userInput);
+ }
+
+ // communicate result to user
+ return state.data.noCopies.join(', ');
+};
diff --git a/6-refactoring/exercises/state/no-copies/src/util.spec.js b/6-refactoring/exercises/state/no-copies/src/util.spec.js
index fb53be18..ba934f23 100644
--- a/6-refactoring/exercises/state/no-copies/src/util.spec.js
+++ b/6-refactoring/exercises/state/no-copies/src/util.spec.js
@@ -1,4 +1,4 @@
-import { saveNoCopies } from './utils.js';
+import { saveNoCopies } from './util.js';
describe('saveNoCopies: ', () => {
describe('adds a new items that are not in the array', () => {
diff --git a/exercises/convertTemperature/index.html b/exercises/convertTemperature/index.html
new file mode 100644
index 00000000..6295a3bc
--- /dev/null
+++ b/exercises/convertTemperature/index.html
@@ -0,0 +1,18 @@
+
+
+
+
+ Convert Temperatures
+
+
+
+
+
+
+
+
+
diff --git a/exercises/convertTemperature/src/data/constants.js b/exercises/convertTemperature/src/data/constants.js
new file mode 100644
index 00000000..e14994b4
--- /dev/null
+++ b/exercises/convertTemperature/src/data/constants.js
@@ -0,0 +1,15 @@
+// Events
+export const ON_CHANGE = 'change';
+
+// Selectors
+export const TEMPERATURES_INPUT = 'temperatures';
+export const CONVERT_TEMPERATURE_CONTAINER = 'convertedTemperatures';
+
+// Regex
+export const SPACE_REGEX = /\s+/;
+export const NUMBERS_SPACES_REGEX = /^[0-9\s]*$/;
+export const PLACEHOLDER_REGEX = /{([0-9]+)}/g;
+
+// Messages
+export const MESSAGE_ERROR_NOT_INTEGER =
+ "The '{0}' contains values different of integer numbers";
diff --git a/exercises/convertTemperature/src/handlers/convert-temperatures-handler.js b/exercises/convertTemperature/src/handlers/convert-temperatures-handler.js
new file mode 100644
index 00000000..af79336a
--- /dev/null
+++ b/exercises/convertTemperature/src/handlers/convert-temperatures-handler.js
@@ -0,0 +1,65 @@
+import {
+ CONVERT_TEMPERATURE_CONTAINER,
+ MESSAGE_ERROR_NOT_INTEGER,
+} from '../data/constants.js';
+import { fahrenheitToCelcius } from '../logic/convert-temperatures.js';
+import { parseToNumberArray } from '../utils/parsers.js';
+import { replacePlaceholders } from '../utils/strings.js';
+import { containsOnlyNumbersAndSpaces } from '../utils/validators.js';
+
+const prepareOutputContainer = () => {
+ const convertedTemperaturesContainer = document.getElementById(
+ CONVERT_TEMPERATURE_CONTAINER,
+ );
+ convertedTemperaturesContainer.innerHTML = '';
+ return convertedTemperaturesContainer;
+};
+
+const renderOutputContainer = (outputContainer, celsiusList) => {
+ celsiusList.forEach((celsius) => {
+ const liString = `${celsius.toFixed(2)}`;
+ outputContainer.innerHTML = outputContainer.innerHTML + liString;
+ });
+};
+
+const validateTextInput = (text) => {
+ if (!text) {
+ throw new Error();
+ }
+
+ if (!containsOnlyNumbersAndSpaces(text)) {
+ throw new Error(replacePlaceholders(MESSAGE_ERROR_NOT_INTEGER, text));
+ }
+};
+
+const convertTemperatures = (fahrenheitTextList) => {
+ validateTextInput(fahrenheitTextList);
+ const fahrenheitList = parseToNumberArray(fahrenheitTextList);
+ const celsiusList = fahrenheitList.map((fahrenheit) => {
+ return fahrenheitToCelcius(fahrenheit);
+ });
+ return celsiusList;
+};
+
+/**
+ * Add an 'change' event listener to the element whose id is 'temperatures'.
+ *
+ * @param {Event} event - the 'change' event.
+ */
+export const convertTemperaturesHandler = (event) => {
+ try {
+ // Input
+ const convertedTemperaturesContainer = prepareOutputContainer();
+
+ // Logic
+ const fahrenheitTextList = event.target.value;
+ const celsiusList = convertTemperatures(fahrenheitTextList);
+
+ // Output
+ renderOutputContainer(convertedTemperaturesContainer, celsiusList);
+ } catch (error) {
+ if (error.message) {
+ window.alert(error.message);
+ }
+ }
+};
diff --git a/exercises/convertTemperature/src/handlers/convert-temperatures-handler.test.js b/exercises/convertTemperature/src/handlers/convert-temperatures-handler.test.js
new file mode 100644
index 00000000..2b0942e8
--- /dev/null
+++ b/exercises/convertTemperature/src/handlers/convert-temperatures-handler.test.js
@@ -0,0 +1,49 @@
+/**
+ * @jest-environment jsdom
+ */
+
+import {
+ CONVERT_TEMPERATURE_CONTAINER,
+ MESSAGE_ERROR_NOT_INTEGER,
+ TEMPERATURES_INPUT,
+} from '../data/constants';
+import { replacePlaceholders } from '../utils/strings';
+import { convertTemperaturesHandler } from './convert-temperatures-handler';
+
+describe('convertTemperaturesHandler', () => {
+ beforeEach(() => {
+ document.body.innerHTML = `
+
+ `;
+ });
+
+ it('should convert the input temperatures', () => {
+ // GIVEN
+ const event = { target: { value: '0 32 64' } };
+ // WHEN
+ convertTemperaturesHandler(event);
+ // THEN
+ const innerHTML = document.getElementById(
+ CONVERT_TEMPERATURE_CONTAINER,
+ ).innerHTML;
+ expect(innerHTML).toContain('>-17.78<');
+ expect(innerHTML).toContain('>0.00<');
+ expect(innerHTML).toContain('>17.78<');
+ });
+
+ it('should show an alert when invalid input', () => {
+ // GIVEN
+ const input = '0 invalid 64';
+ const event = { target: { value: input } };
+ window.alert = jest.fn();
+ // WHEN
+ convertTemperaturesHandler(event);
+ // THEN
+ expect(window.alert).toHaveBeenCalledWith(
+ replacePlaceholders(MESSAGE_ERROR_NOT_INTEGER, input),
+ );
+ });
+});
diff --git a/exercises/convertTemperature/src/init.js b/exercises/convertTemperature/src/init.js
new file mode 100644
index 00000000..9ff9e758
--- /dev/null
+++ b/exercises/convertTemperature/src/init.js
@@ -0,0 +1,4 @@
+import { convertTemperaturesHandler } from './handlers/convert-temperatures-handler.js';
+import { addTemperaturesChangeListener } from './listeners/temperatures-input-listener.js';
+
+addTemperaturesChangeListener(convertTemperaturesHandler);
diff --git a/exercises/convertTemperature/src/listeners/temperatures-input-listener.js b/exercises/convertTemperature/src/listeners/temperatures-input-listener.js
new file mode 100644
index 00000000..b9c3e71a
--- /dev/null
+++ b/exercises/convertTemperature/src/listeners/temperatures-input-listener.js
@@ -0,0 +1,12 @@
+import { ON_CHANGE, TEMPERATURES_INPUT } from '../data/constants.js';
+
+/**
+ * Add an 'change' event listener to the element whose id is 'temperatures'.
+ *
+ * @param {function} handler - Callback function to be called when the event occurs.
+ */
+export const addTemperaturesChangeListener = (handler) => {
+ document
+ .getElementById(TEMPERATURES_INPUT)
+ ?.addEventListener(ON_CHANGE, handler);
+};
diff --git a/exercises/convertTemperature/src/listeners/temperatures-input-listener.test.js b/exercises/convertTemperature/src/listeners/temperatures-input-listener.test.js
new file mode 100644
index 00000000..ddad61dc
--- /dev/null
+++ b/exercises/convertTemperature/src/listeners/temperatures-input-listener.test.js
@@ -0,0 +1,31 @@
+/**
+ * @jest-environment jsdom
+ */
+
+import { ON_CHANGE, TEMPERATURES_INPUT } from '../data/constants';
+import { addTemperaturesChangeListener } from './temperatures-input-listener';
+
+describe('addTemperatureChangeListener', () => {
+ beforeEach(() => {
+ document.body.innerHTML = `
+
+
+
+ `;
+ });
+
+ it('should add a handler to the temperature change', () => {
+ // GIVEN
+ const handler = jest.fn();
+ const dispatchChangeTemperatures = () => {
+ document
+ .getElementById(TEMPERATURES_INPUT)
+ .dispatchEvent(new window.Event(ON_CHANGE));
+ };
+ // WHEN
+ addTemperaturesChangeListener(handler);
+ dispatchChangeTemperatures();
+ // THEN
+ expect(handler).toHaveBeenCalledTimes(1);
+ });
+});
diff --git a/exercises/convertTemperature/src/logic/convert-temperatures.js b/exercises/convertTemperature/src/logic/convert-temperatures.js
new file mode 100644
index 00000000..5e0d5901
--- /dev/null
+++ b/exercises/convertTemperature/src/logic/convert-temperatures.js
@@ -0,0 +1,9 @@
+/**
+ * Convert a temperature in Fahrenheit degrees to Celcius degrees.
+ *
+ * @param {number} fahrenheit - Temperature in Fahrenheit degrees.
+ * @returns {number} - Temperature in Celcius degrees.
+ */
+export const fahrenheitToCelcius = (fahrenheit) => {
+ return ((fahrenheit - 32) * 5) / 9;
+};
diff --git a/exercises/convertTemperature/src/logic/convert-temperatures.test.js b/exercises/convertTemperature/src/logic/convert-temperatures.test.js
new file mode 100644
index 00000000..f65252ce
--- /dev/null
+++ b/exercises/convertTemperature/src/logic/convert-temperatures.test.js
@@ -0,0 +1,14 @@
+import { fahrenheitToCelcius } from './convert-temperatures';
+
+describe('fahrenheitToCelcius', () => {
+ describe.each`
+ fahrenheit | expected
+ ${0} | ${-17.77777777777778}
+ ${32} | ${0}
+ ${64} | ${17.77777777777778}
+ `('$fahrenheit fahrenheit', ({ fahrenheit, expected }) => {
+ it(`returns ${expected} celcius`, () => {
+ expect(fahrenheitToCelcius(fahrenheit)).toEqual(expected);
+ });
+ });
+});
diff --git a/exercises/convertTemperature/src/utils/parsers.js b/exercises/convertTemperature/src/utils/parsers.js
new file mode 100644
index 00000000..e1dadc23
--- /dev/null
+++ b/exercises/convertTemperature/src/utils/parsers.js
@@ -0,0 +1,18 @@
+import { SPACE_REGEX } from '../data/constants.js';
+
+/**
+ * Convert a text containing numbers to an array of numbers.
+ * If the input text contains non valid numbers, NaN is used as its substitute.
+ *
+ * @param {string} text - The text to be parsed.
+ * @returns {number[]} - Array of numbers.
+ */
+export const parseToNumberArray = (text) => {
+ if (text) {
+ return text
+ .trim()
+ .split(SPACE_REGEX)
+ .map((textNumber) => Number(textNumber));
+ }
+ return [];
+};
diff --git a/exercises/convertTemperature/src/utils/parsers.test.js b/exercises/convertTemperature/src/utils/parsers.test.js
new file mode 100644
index 00000000..e6b33ecc
--- /dev/null
+++ b/exercises/convertTemperature/src/utils/parsers.test.js
@@ -0,0 +1,18 @@
+import { parseToNumberArray } from './parsers';
+
+describe('parseToNumberArray', () => {
+ describe.each`
+ text | expected
+ ${null} | ${[]}
+ ${undefined} | ${[]}
+ ${''} | ${[]}
+ ${'1 2 3'} | ${[1, 2, 3]}
+ ${' \t 1 \n 2 3 '} | ${[1, 2, 3]}
+ ${'-1 -2 -3'} | ${[-1, -2, -3]}
+ ${'1 a @'} | ${[1, NaN, NaN]}
+ `('text $text', ({ text, expected }) => {
+ it(`returns ${expected}`, () => {
+ expect(parseToNumberArray(text)).toEqual(expected);
+ });
+ });
+});
diff --git a/exercises/convertTemperature/src/utils/strings.js b/exercises/convertTemperature/src/utils/strings.js
new file mode 100644
index 00000000..33e0f3d8
--- /dev/null
+++ b/exercises/convertTemperature/src/utils/strings.js
@@ -0,0 +1,18 @@
+import { PLACEHOLDER_REGEX } from '../data/constants.js';
+
+/**
+ * Returns a new string with all placeholders ({0}, {1}, {n}) replaced.
+ * Reference: https://medium.com/@onlinemsr/javascript-string-format-the-best-3-ways-to-do-it-c6a12b4b94ed
+ *
+ * @param {string} template - The string with placeholders.
+ * @param {string[]} args - The replacements.
+ * @returns {string} - The string with all placeholders ({0}, {1}, {n}) replaced.
+ */
+export const replacePlaceholders = (template, ...args) => {
+ if (template) {
+ return template.replace(PLACEHOLDER_REGEX, function (match, index) {
+ return typeof args[index] === 'undefined' ? match : args[index];
+ });
+ }
+ return template;
+};
diff --git a/exercises/convertTemperature/src/utils/strings.test.js b/exercises/convertTemperature/src/utils/strings.test.js
new file mode 100644
index 00000000..06231241
--- /dev/null
+++ b/exercises/convertTemperature/src/utils/strings.test.js
@@ -0,0 +1,24 @@
+import { replacePlaceholders } from './strings';
+
+describe('replacePlaceholders', () => {
+ describe.each`
+ text | replacements | expected
+ ${null} | ${[]} | ${null}
+ ${null} | ${['text']} | ${null}
+ ${''} | ${[]} | ${''}
+ ${''} | ${['text']} | ${''}
+ ${'text'} | ${['text']} | ${'text'}
+ ${'{0} text {0}'} | ${['zero']} | ${'zero text zero'}
+ ${'{0} text {1}'} | ${['zero', 'one']} | ${'zero text one'}
+ ${'{0} text {1}'} | ${['zero', 'one']} | ${'zero text one'}
+ ${'{0} text {1}'} | ${[]} | ${'{0} text {1}'}
+ ${'{0} text {1}'} | ${['zero']} | ${'zero text {1}'}
+ `(
+ 'text $text with replacements $replacements',
+ ({ text, replacements, expected }) => {
+ it(`returns ${expected}`, () => {
+ expect(replacePlaceholders(text, ...replacements)).toBe(expected);
+ });
+ },
+ );
+});
diff --git a/exercises/convertTemperature/src/utils/validators.js b/exercises/convertTemperature/src/utils/validators.js
new file mode 100644
index 00000000..6d991ea5
--- /dev/null
+++ b/exercises/convertTemperature/src/utils/validators.js
@@ -0,0 +1,11 @@
+import { NUMBERS_SPACES_REGEX } from '../data/constants.js';
+
+/**
+ * Check if the input text contains only number characters or spaces.
+ *
+ * @param {string} text - The text to be checked.
+ * @returns {boolean} - It returns true if the text match with /^[0-9\s]*$/ or false otherwise.
+ */
+export const containsOnlyNumbersAndSpaces = (text) => {
+ return NUMBERS_SPACES_REGEX.test(text);
+};
diff --git a/exercises/convertTemperature/src/utils/validators.test.js b/exercises/convertTemperature/src/utils/validators.test.js
new file mode 100644
index 00000000..3905329e
--- /dev/null
+++ b/exercises/convertTemperature/src/utils/validators.test.js
@@ -0,0 +1,18 @@
+import { containsOnlyNumbersAndSpaces } from './validators';
+
+describe('containsOnlyNumbersAndSpaces', () => {
+ describe.each`
+ text | expected
+ ${null} | ${false}
+ ${undefined} | ${false}
+ ${''} | ${true}
+ ${'1 2 3'} | ${true}
+ ${' \t 1 \n 2 3 '} | ${true}
+ ${'-1 -2 -3'} | ${false}
+ ${'1 a @'} | ${false}
+ `('text $text', ({ text, expected }) => {
+ it(`returns ${expected}`, () => {
+ expect(containsOnlyNumbersAndSpaces(text)).toEqual(expected);
+ });
+ });
+});
diff --git a/exercises/convertTemperature/styles.css b/exercises/convertTemperature/styles.css
new file mode 100644
index 00000000..a8ac6631
--- /dev/null
+++ b/exercises/convertTemperature/styles.css
@@ -0,0 +1,3 @@
+.number-item {
+ font-family: fantasy;
+}
diff --git a/package-lock.json b/package-lock.json
index 6d02f551..d8e1810d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,6 +10,7 @@
"license": "MIT",
"devDependencies": {
"@babel/plugin-transform-modules-commonjs": "^7.22.5",
+ "@types/jest": "^29.5.12",
"jest": "^29.6.1",
"jest-environment-jsdom": "^29.6.1",
"prettier": "^2.3.2"
@@ -1038,6 +1039,16 @@
"@types/istanbul-lib-report": "*"
}
},
+ "node_modules/@types/jest": {
+ "version": "29.5.12",
+ "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz",
+ "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==",
+ "dev": true,
+ "dependencies": {
+ "expect": "^29.0.0",
+ "pretty-format": "^29.0.0"
+ }
+ },
"node_modules/@types/jsdom": {
"version": "20.0.1",
"resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz",
@@ -4792,6 +4803,16 @@
"@types/istanbul-lib-report": "*"
}
},
+ "@types/jest": {
+ "version": "29.5.12",
+ "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz",
+ "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==",
+ "dev": true,
+ "requires": {
+ "expect": "^29.0.0",
+ "pretty-format": "^29.0.0"
+ }
+ },
"@types/jsdom": {
"version": "20.0.1",
"resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz",
diff --git a/package.json b/package.json
index 4ae947a5..1f575409 100644
--- a/package.json
+++ b/package.json
@@ -17,9 +17,10 @@
},
"homepage": "https://github.com/HackYourFutureBelgium/separation-of-concerns#readme",
"devDependencies": {
- "prettier": "^2.3.2",
"@babel/plugin-transform-modules-commonjs": "^7.22.5",
+ "@types/jest": "^29.5.12",
"jest": "^29.6.1",
- "jest-environment-jsdom": "^29.6.1"
+ "jest-environment-jsdom": "^29.6.1",
+ "prettier": "^2.3.2"
}
}