Initial commit
This commit is contained in:
13
examples/calculator/index.html
Normal file
13
examples/calculator/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Calculator</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
<script type="module">
|
||||
import { renderCalculator } from './src/calculator.js';
|
||||
renderCalculator(document.body);
|
||||
</script>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
32
examples/calculator/package.json
Normal file
32
examples/calculator/package.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "calculator-example",
|
||||
"version": "1.0.0",
|
||||
"description": "A calculator that calculates numbers, but with tests.",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "vite dev",
|
||||
"test": "vitest"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/stevekinney/testing-javascript.git"
|
||||
},
|
||||
"author": "Steve Kinney <hello@stevekinney.net>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/stevekinney/testing-javascript/issues"
|
||||
},
|
||||
"homepage": "https://github.com/stevekinney/testing-javascript#readme",
|
||||
"devDependencies": {
|
||||
"@tailwindcss/nesting": "^0.0.0-insiders.565cd3e",
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/user-event": "^14.5.2",
|
||||
"@vitest/ui": "^2.1.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"happy-dom": "^15.7.4",
|
||||
"tailwindcss": "^3.4.11",
|
||||
"vite": "^5.4.5",
|
||||
"vitest": "^2.1.1"
|
||||
}
|
||||
}
|
||||
7
examples/calculator/postcss.config.js
Normal file
7
examples/calculator/postcss.config.js
Normal file
@@ -0,0 +1,7 @@
|
||||
export default {
|
||||
plugins: {
|
||||
'@tailwindcss/nesting': {},
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
1
examples/calculator/setup-tests.js
Normal file
1
examples/calculator/setup-tests.js
Normal file
@@ -0,0 +1 @@
|
||||
import '@testing-library/jest-dom'; // Provides custom matchers
|
||||
19
examples/calculator/src/calculator.html
Normal file
19
examples/calculator/src/calculator.html
Normal file
@@ -0,0 +1,19 @@
|
||||
<section id="calculator" class="calculator">
|
||||
<input id="display" class="display" type="number" placeholder="0" disabled />
|
||||
<button id="digit-7" class="number" data-value="7">7</button>
|
||||
<button id="digit-8" class="number" data-value="8">8</button>
|
||||
<button id="digit-9" class="number" data-value="9">9</button>
|
||||
<button id="divide" class="operator">/</button>
|
||||
<button id="digit-4" class="number" data-value="4">4</button>
|
||||
<button id="digit-5" class="number" data-value="5">5</button>
|
||||
<button id="digit-6" class="number" data-value="6">6</button>
|
||||
<button id="multiply" class="operator">*</button>
|
||||
<button id="digit-1" class="number" data-value="1">1</button>
|
||||
<button id="digit-2" class="number" data-value="2">2</button>
|
||||
<button id="digit-3" class="number" data-value="3">3</button>
|
||||
<button id="subtract" class="operator">-</button>
|
||||
<button id="digit-0" class="number" data-value="0">0</button>
|
||||
<button id="add" class="operator">+</button>
|
||||
<button id="clear" data-value="C">C</button>
|
||||
<button id="equals" data-value="=">=</button>
|
||||
</section>
|
||||
49
examples/calculator/src/calculator.js
Normal file
49
examples/calculator/src/calculator.js
Normal file
@@ -0,0 +1,49 @@
|
||||
import calculator from './calculator.html?raw';
|
||||
|
||||
/**
|
||||
* Renders the calculator component into the target element.
|
||||
* @param {HTMLElement} target The target element to render the calculator into.
|
||||
*/
|
||||
export function renderCalculator(target) {
|
||||
target.innerHTML = calculator;
|
||||
|
||||
let buffer = 0;
|
||||
|
||||
/** @type {HTMLInputElement} */
|
||||
const display = target.querySelector('#display');
|
||||
|
||||
/** @type {NodeListOf<HTMLButtonElement>} */
|
||||
const numbers = target.querySelectorAll('.number');
|
||||
|
||||
/** @type {NodeListOf<HTMLButtonElement>} */
|
||||
const operators = target.querySelectorAll('.operator');
|
||||
|
||||
/** @type {HTMLButtonElement} */
|
||||
const clear = target.querySelector('#clear');
|
||||
|
||||
/** @type {HTMLButtonElement} */
|
||||
const equals = target.querySelector('#equals');
|
||||
|
||||
numbers.forEach((number) => {
|
||||
number.addEventListener('click', () => {
|
||||
display.value += number.dataset.value;
|
||||
});
|
||||
});
|
||||
|
||||
operators.forEach((operator) => {
|
||||
operator.addEventListener('click', () => {
|
||||
buffer = display.valueAsNumber;
|
||||
display.value = '';
|
||||
});
|
||||
});
|
||||
|
||||
clear.addEventListener('click', () => {
|
||||
buffer = 0;
|
||||
display.value = '';
|
||||
});
|
||||
|
||||
equals.addEventListener('click', () => {
|
||||
const result = buffer + display.valueAsNumber;
|
||||
display.value = String(result);
|
||||
});
|
||||
}
|
||||
63
examples/calculator/src/calculator.test.js
Normal file
63
examples/calculator/src/calculator.test.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
import { fireEvent } from '@testing-library/dom';
|
||||
import { renderCalculator } from './calculator.js';
|
||||
|
||||
it('is running our test in a browser-like environment', () => {
|
||||
expect(typeof window).not.toBe('undefined');
|
||||
});
|
||||
|
||||
describe('Calculator', () => {
|
||||
let display;
|
||||
|
||||
beforeEach(() => {
|
||||
renderCalculator(document.body);
|
||||
|
||||
display = document.getElementById('display');
|
||||
});
|
||||
|
||||
it('displays number when a number button is clicked', () => {
|
||||
/** @type {NodeListOf<HTMLButtonElement>} */
|
||||
const [button] = document.querySelectorAll('button');
|
||||
const value = button.dataset.value;
|
||||
|
||||
fireEvent.click(button);
|
||||
|
||||
expect(display.value).toBe(value);
|
||||
});
|
||||
|
||||
it('display the sum of multiple numbers when the equals button is clicked', () => {
|
||||
const one = document.getElementById('digit-1');
|
||||
const two = document.getElementById('digit-2');
|
||||
const three = document.getElementById('digit-3');
|
||||
|
||||
fireEvent.click(one);
|
||||
fireEvent.click(two);
|
||||
fireEvent.click(three);
|
||||
|
||||
expect(display.value).toBe('123');
|
||||
});
|
||||
|
||||
it('supports addings two numbers and displaying the result', () => {
|
||||
const one = document.getElementById('digit-1');
|
||||
const two = document.getElementById('digit-2');
|
||||
const add = document.getElementById('add');
|
||||
const equals = document.getElementById('equals');
|
||||
|
||||
fireEvent.click(one);
|
||||
fireEvent.click(add);
|
||||
fireEvent.click(two);
|
||||
fireEvent.click(equals);
|
||||
|
||||
expect(display.value).toBe('3');
|
||||
});
|
||||
|
||||
it('clears the display when the clear button is clicked', () => {
|
||||
const one = document.getElementById('digit-1');
|
||||
const clear = document.getElementById('clear');
|
||||
|
||||
fireEvent.click(one);
|
||||
fireEvent.click(clear);
|
||||
|
||||
expect(display.value).toBe('');
|
||||
});
|
||||
});
|
||||
42
examples/calculator/styles.css
Normal file
42
examples/calculator/styles.css
Normal file
@@ -0,0 +1,42 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
body {
|
||||
@apply flex justify-center items-center h-screen bg-purple-50 font-sans m-0;
|
||||
}
|
||||
|
||||
button {
|
||||
@apply bg-slate-600 text-white text-xl cursor-pointer flex items-center justify-center transition duration-100 ease-in-out p-4 hover:bg-slate-700 active:bg-slate-800 rounded-xl;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.calculator {
|
||||
@apply grid grid-cols-4 gap-2 bg-slate-950 rounded-xl shadow-lg p-4;
|
||||
}
|
||||
|
||||
.number {
|
||||
@apply bg-slate-600 text-white text-xl cursor-pointer flex items-center justify-center transition duration-100 ease-in-out p-4 hover:bg-slate-700 active:bg-slate-800 rounded-xl;
|
||||
&[data-value='0'] {
|
||||
@apply col-span-3;
|
||||
}
|
||||
}
|
||||
|
||||
.operator {
|
||||
@apply bg-orange-500 hover:bg-orange-600 active:bg-orange-700;
|
||||
}
|
||||
|
||||
#equals {
|
||||
@apply bg-green-500 hover:bg-green-600 active:bg-green-700 col-start-3 col-span-2;
|
||||
}
|
||||
|
||||
#clear {
|
||||
@apply bg-red-500 hover:bg-red-600 active:bg-red-700;
|
||||
}
|
||||
|
||||
.display {
|
||||
@apply col-span-4 bg-cyan-100 text-cyan-800 border-cyan-200 outline outline-2 outline-transparent text-2xl text-right p-4 block font-semibold rounded-md;
|
||||
}
|
||||
}
|
||||
13
examples/calculator/tailwind.config.js
Normal file
13
examples/calculator/tailwind.config.js
Normal file
@@ -0,0 +1,13 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: ['./**/*.{html,js}'],
|
||||
theme: {
|
||||
extend: {
|
||||
container: {
|
||||
center: true,
|
||||
padding: '1rem',
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
5
examples/calculator/vite.config.js
Normal file
5
examples/calculator/vite.config.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
assetsInclude: ['**/*.html'],
|
||||
});
|
||||
8
examples/calculator/vitest.config.js
Normal file
8
examples/calculator/vitest.config.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
assetsInclude: ['**/*.html'],
|
||||
test: {
|
||||
environment: 'happy-dom',
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user