Initial commit
This commit is contained in:
30
.gitignore
vendored
Normal file
30
.gitignore
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# Node.js
|
||||
node_modules/
|
||||
|
||||
# Logs
|
||||
logs/
|
||||
*.log
|
||||
|
||||
# Dependency directories
|
||||
pids/
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Build output
|
||||
dist/
|
||||
build/
|
||||
out/
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
|
||||
# IDE files
|
||||
.vscode/
|
||||
.idea/
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
|
||||
# Miscellaneous
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
9
.prettierrc
Normal file
9
.prettierrc
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"printWidth": 80,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"bracketSpacing": true
|
||||
}
|
||||
25
examples/authentication/package.json
Normal file
25
examples/authentication/package.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "authentication",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"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": {
|
||||
"@vitest/ui": "^2.1.1",
|
||||
"vite": "^5.4.5",
|
||||
"vitest": "^2.1.1"
|
||||
}
|
||||
}
|
||||
5
examples/authentication/vitest.config.js
Normal file
5
examples/authentication/vitest.config.js
Normal file
@@ -0,0 +1,5 @@
|
||||
export default {
|
||||
test: {
|
||||
environment: 'node',
|
||||
},
|
||||
};
|
||||
1
examples/basic-math/README.md
Normal file
1
examples/basic-math/README.md
Normal file
@@ -0,0 +1 @@
|
||||
# Basic Math
|
||||
26
examples/basic-math/package.json
Normal file
26
examples/basic-math/package.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "basic-math",
|
||||
"version": "1.0.0",
|
||||
"description": "Let's talk about adding and subtracting numbers.",
|
||||
"main": "src/index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "vitest --ui",
|
||||
"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": {
|
||||
"@vitest/ui": "^2.1.1",
|
||||
"vite": "^5.4.5",
|
||||
"vitest": "^2.1.1"
|
||||
}
|
||||
}
|
||||
0
examples/basic-math/src/calculator.js
Normal file
0
examples/basic-math/src/calculator.js
Normal file
1
examples/basic-math/src/calculator.test.ts
Normal file
1
examples/basic-math/src/calculator.test.ts
Normal file
@@ -0,0 +1 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
5
examples/basic-math/vitest.config.js
Normal file
5
examples/basic-math/vitest.config.js
Normal file
@@ -0,0 +1,5 @@
|
||||
export default {
|
||||
test: {
|
||||
environment: 'node',
|
||||
},
|
||||
};
|
||||
393
examples/calculator-reducer/README.md
Normal file
393
examples/calculator-reducer/README.md
Normal file
@@ -0,0 +1,393 @@
|
||||
# Calculator Reducer
|
||||
|
||||
## Breakdown of the Code
|
||||
|
||||
## 1. **JSDoc Type Imports**
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* @typedef {import('./types.js').CalculatorState} CalculatorState
|
||||
* @typedef {import('./types.js').CalculatorAction} CalculatorAction
|
||||
*/
|
||||
```
|
||||
|
||||
- These lines use **JSDoc** to import types from `types.js`.
|
||||
- `CalculatorState` represents the shape of the calculator's state, and `CalculatorAction` represents the shape of an action dispatched to the reducer.
|
||||
- These types provide type checking and documentation within the IDE, even though the code is in plain JavaScript.
|
||||
|
||||
## 2. **Initial State**
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* The initial state for the calculator.
|
||||
* @type {CalculatorState}
|
||||
*/
|
||||
const initialState = {
|
||||
currentValue: '0', // Current input or result
|
||||
previousValue: null, // Previous value before an operation
|
||||
operator: null, // The operator (+, -, *, /)
|
||||
waitingForOperand: false, // Tracks if we're waiting for the next operand
|
||||
};
|
||||
```
|
||||
|
||||
- `initialState` defines the starting values for the calculator.
|
||||
- `currentValue`: Represents the value currently being input or the last calculated result. It starts as `'0'`.
|
||||
- `previousValue`: Holds the value before an operator is pressed. Initially `null`.
|
||||
- `operator`: Stores the operator (`+`, `-`, `*`, `/`) currently in use. It starts as `null`.
|
||||
- `waitingForOperand`: A boolean flag that indicates if the calculator is waiting for the next operand after an operator is pressed.
|
||||
|
||||
## 3. **Reducer Function**
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* A reducer function for the calculator state.
|
||||
* @param {CalculatorState} state
|
||||
* @param {CalculatorAction} action
|
||||
* @returns {CalculatorState}
|
||||
*/
|
||||
export function calculatorReducer(state = initialState, action) {
|
||||
```
|
||||
|
||||
- This is a **reducer function** that takes the current state and an action, and returns the new state based on the type of the action.
|
||||
- **Default value**: If no state is provided (e.g., at initialization), it uses `initialState`.
|
||||
|
||||
## 4. **Handling Action Types**
|
||||
|
||||
```javascript
|
||||
switch (action.type) {
|
||||
case 'DIGIT':
|
||||
```
|
||||
|
||||
- The `switch` statement handles different `action.type` values.
|
||||
|
||||
### 4.1 **Handling `'DIGIT'` Action**
|
||||
|
||||
```javascript
|
||||
case 'DIGIT':
|
||||
if (state.waitingForOperand) {
|
||||
return {
|
||||
...state,
|
||||
currentValue: action.payload,
|
||||
waitingForOperand: false,
|
||||
};
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
currentValue:
|
||||
state.currentValue === '0'
|
||||
? action.payload
|
||||
: state.currentValue + action.payload,
|
||||
};
|
||||
```
|
||||
|
||||
- **When a digit is pressed** (`'DIGIT'` action):
|
||||
- If the calculator is waiting for the next operand (`waitingForOperand` is `true`), it replaces the `currentValue` with the digit and resets the `waitingForOperand` flag.
|
||||
- Otherwise, it appends the digit to the `currentValue`. If `currentValue` is `'0'`, it replaces it with the new digit.
|
||||
|
||||
### 4.2 **Handling `'OPERATOR'` Action**
|
||||
|
||||
```javascript
|
||||
case 'OPERATOR':
|
||||
if (state.operator && state.previousValue !== null) {
|
||||
const result = evaluate(state);
|
||||
return {
|
||||
...state,
|
||||
previousValue: result,
|
||||
currentValue: '0',
|
||||
operator: action.payload,
|
||||
waitingForOperand: true,
|
||||
};
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
previousValue: state.currentValue,
|
||||
operator: action.payload,
|
||||
waitingForOperand: true,
|
||||
};
|
||||
```
|
||||
|
||||
- **When an operator is pressed** (`'OPERATOR'` action):
|
||||
- If there's already a `previousValue` and an operator is set, the function evaluates the current expression and stores the result as the `previousValue`.
|
||||
- Otherwise, it sets the `currentValue` as the `previousValue` and assigns the operator from `action.payload`.
|
||||
- It also sets `waitingForOperand` to `true` to signal that the calculator is waiting for the next number to be entered.
|
||||
|
||||
### 4.3 **Handling `'EQUALS'` Action**
|
||||
|
||||
```javascript
|
||||
case 'EQUALS':
|
||||
if (state.operator && state.previousValue !== null) {
|
||||
const result = evaluate(state);
|
||||
return {
|
||||
...state,
|
||||
currentValue: result,
|
||||
previousValue: null,
|
||||
operator: null,
|
||||
waitingForOperand: false,
|
||||
};
|
||||
}
|
||||
return state;
|
||||
```
|
||||
|
||||
- **When the equals button is pressed** (`'EQUALS'` action):
|
||||
- If an operator and a `previousValue` are set, it evaluates the current expression and updates `currentValue` with the result. It also clears the operator and `previousValue`.
|
||||
- If the necessary data for evaluation is not present, it simply returns the current state unchanged.
|
||||
|
||||
### 4.4 **Handling `'CLEAR'` Action**
|
||||
|
||||
```javascript
|
||||
case 'CLEAR':
|
||||
return initialState;
|
||||
```
|
||||
|
||||
- **When the clear button is pressed** (`'CLEAR'` action):
|
||||
- It resets the calculator state to the `initialState`.
|
||||
|
||||
### 4.5 **Default Case**
|
||||
|
||||
```javascript
|
||||
default:
|
||||
return state;
|
||||
```
|
||||
|
||||
- If an action type is not recognized, the reducer returns the current state without any changes.
|
||||
|
||||
## 5. **Evaluate Function**
|
||||
|
||||
```javascript
|
||||
function evaluate({ currentValue, previousValue, operator }) {
|
||||
const prev = parseFloat(previousValue);
|
||||
const current = parseFloat(currentValue);
|
||||
|
||||
switch (operator) {
|
||||
case '+':
|
||||
return (prev + current).toString();
|
||||
case '-':
|
||||
return (prev - current).toString();
|
||||
case '*':
|
||||
return (prev * current).toString();
|
||||
case '/':
|
||||
return current !== 0 ? (prev / current).toString() : 'Error';
|
||||
default:
|
||||
return currentValue;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- The `evaluate` function takes `currentValue`, `previousValue`, and `operator` to perform the calculation.
|
||||
- **Steps**:
|
||||
1. It converts the `previousValue` and `currentValue` from strings to numbers using `parseFloat`.
|
||||
2. Based on the operator, it performs the corresponding arithmetic operation (`+`, `-`, `*`, `/`).
|
||||
3. **Divide by zero handling**: If the operation is division (`/`) and `currentValue` is `0`, it returns `'Error'` to avoid division by zero.
|
||||
4. The result of the operation is returned as a string.
|
||||
|
||||
## Summary
|
||||
|
||||
- The `calculatorReducer` function handles the state changes of a calculator by interpreting different actions (`'DIGIT'`, `'OPERATOR'`, `'EQUALS'`, `'CLEAR'`).
|
||||
- The `evaluate` helper function performs the arithmetic operations when required.
|
||||
- The state is updated incrementally as the user presses digits, operators, and other controls, making it suitable for a basic calculator UI.
|
||||
|
||||
## Breakdown of the Tests
|
||||
|
||||
### 1. **Imports and Initial Setup**
|
||||
|
||||
```javascript
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { calculatorReducer } from './calculator.js'; // Assume this is the path to the reducer
|
||||
```
|
||||
|
||||
- **Vitest** is being imported for testing, using `describe` to group the test cases, `it` for individual test cases, and `expect` for assertions.
|
||||
- The `calculatorReducer` is imported from a file called `calculator.js` for testing.
|
||||
- The `CalculatorState` and `CalculatorAction` types are imported using **JSDoc** for static type checking.
|
||||
|
||||
### 2. **Initial State**
|
||||
|
||||
```javascript
|
||||
const initialState = {
|
||||
currentValue: '0',
|
||||
previousValue: null,
|
||||
operator: null,
|
||||
waitingForOperand: false,
|
||||
};
|
||||
```
|
||||
|
||||
- This defines the `initialState`, representing the calculator's default state when no input has been provided yet.
|
||||
|
||||
### 3. **Test: Handling Digit Input**
|
||||
|
||||
```javascript
|
||||
it('should handle a digit input', () => {
|
||||
const action = { type: 'DIGIT', payload: '5' };
|
||||
const newState = calculatorReducer(initialState, action);
|
||||
expect(newState.currentValue).toBe('5');
|
||||
});
|
||||
```
|
||||
|
||||
- **What it does**: Tests that when the `'DIGIT'` action is dispatched with the digit `'5'`, the `currentValue` is updated to `'5'`.
|
||||
- **Why**: To ensure the reducer correctly updates the state with the inputted digit.
|
||||
|
||||
### 4. **Test: Appending Digits to Current Input**
|
||||
|
||||
```javascript
|
||||
it('should append digits to the current input', () => {
|
||||
const firstAction = { type: 'DIGIT', payload: '5' };
|
||||
const secondAction = { type: 'DIGIT', payload: '3' };
|
||||
|
||||
let state = calculatorReducer(initialState, firstAction);
|
||||
state = calculatorReducer(state, secondAction);
|
||||
|
||||
expect(state.currentValue).toBe('53');
|
||||
});
|
||||
```
|
||||
|
||||
- **What it does**: Tests appending digits `'5'` and `'3'` to form `'53'`.
|
||||
- **Why**: To verify that digits are appended to `currentValue` correctly and are not overwritten when a new digit is added.
|
||||
|
||||
### 5. **Test: Handling Operator Input**
|
||||
|
||||
```javascript
|
||||
it('should handle operator input', () => {
|
||||
const stateWithDigit = { ...initialState, currentValue: '10' };
|
||||
const action = { type: 'OPERATOR', payload: '+' };
|
||||
|
||||
const newState = calculatorReducer(stateWithDigit, action);
|
||||
|
||||
expect(newState.previousValue).toBe('10');
|
||||
expect(newState.operator).toBe('+');
|
||||
expect(newState.waitingForOperand).toBe(true);
|
||||
});
|
||||
```
|
||||
|
||||
- **What it does**: Tests pressing an operator (`'+'`), ensuring the state updates with the current value as `previousValue`, sets the operator, and sets `waitingForOperand` to `true`.
|
||||
- **Why**: To confirm that when an operator is pressed, the reducer correctly prepares for the next operand.
|
||||
|
||||
### 6. **Test: Performing Addition**
|
||||
|
||||
```javascript
|
||||
it('should perform addition when equals is pressed', () => {
|
||||
const stateWithOperator = {
|
||||
currentValue: '5',
|
||||
previousValue: '10',
|
||||
operator: '+',
|
||||
waitingForOperand: false,
|
||||
};
|
||||
|
||||
const action = { type: 'EQUALS' };
|
||||
const newState = calculatorReducer(stateWithOperator, action);
|
||||
|
||||
expect(newState.currentValue).toBe('15');
|
||||
});
|
||||
```
|
||||
|
||||
- **What it does**: Tests the `'EQUALS'` action when the operator is `'+'`, and the values are `10` and `5`. The result should be `'15'`.
|
||||
- **Why**: To verify that the addition is correctly performed when the equals button is pressed.
|
||||
|
||||
### 7. **Test: Performing Subtraction**
|
||||
|
||||
```javascript
|
||||
it('should perform subtraction when equals is pressed', () => {
|
||||
const stateWithOperator = {
|
||||
currentValue: '3',
|
||||
previousValue: '8',
|
||||
operator: '-',
|
||||
waitingForOperand: false,
|
||||
};
|
||||
|
||||
const action = { type: 'EQUALS' };
|
||||
const newState = calculatorReducer(stateWithOperator, action);
|
||||
|
||||
expect(newState.currentValue).toBe('5');
|
||||
});
|
||||
```
|
||||
|
||||
- **What it does**: Tests subtraction (`'8 - 3'`) when the equals button is pressed, with the result being `'5'`.
|
||||
- **Why**: To confirm that subtraction works correctly with the equals action.
|
||||
|
||||
### 8. **Test: Performing Multiplication**
|
||||
|
||||
```javascript
|
||||
it('should perform multiplication when equals is pressed', () => {
|
||||
const stateWithOperator = {
|
||||
currentValue: '4',
|
||||
previousValue: '6',
|
||||
operator: '*',
|
||||
waitingForOperand: false,
|
||||
};
|
||||
|
||||
const action = { type: 'EQUALS' };
|
||||
const newState = calculatorReducer(stateWithOperator, action);
|
||||
|
||||
expect(newState.currentValue).toBe('24');
|
||||
});
|
||||
```
|
||||
|
||||
- **What it does**: Tests multiplication (`'6 * 4'`) when the equals button is pressed, expecting the result `'24'`.
|
||||
- **Why**: To ensure the multiplication operation is working correctly.
|
||||
|
||||
### 9. **Test: Performing Division**
|
||||
|
||||
```javascript
|
||||
it('should perform division when equals is pressed', () => {
|
||||
const stateWithOperator = {
|
||||
currentValue: '2',
|
||||
previousValue: '10',
|
||||
operator: '/',
|
||||
waitingForOperand: false,
|
||||
};
|
||||
|
||||
const action = { type: 'EQUALS' };
|
||||
const newState = calculatorReducer(stateWithOperator, action);
|
||||
|
||||
expect(newState.currentValue).toBe('5');
|
||||
});
|
||||
```
|
||||
|
||||
- **What it does**: Tests division (`'10 / 2'`) with the expected result of `'5'`.
|
||||
- **Why**: To confirm that division is handled properly.
|
||||
|
||||
### 10. **Test: Division by Zero**
|
||||
|
||||
```javascript
|
||||
it('should return "Error" when dividing by zero', () => {
|
||||
const stateWithOperator = {
|
||||
currentValue: '0',
|
||||
previousValue: '10',
|
||||
operator: '/',
|
||||
waitingForOperand: false,
|
||||
};
|
||||
|
||||
const action = { type: 'EQUALS' };
|
||||
const newState = calculatorReducer(stateWithOperator, action);
|
||||
|
||||
expect(newState.currentValue).toBe('Error');
|
||||
});
|
||||
```
|
||||
|
||||
- **What it does**: Tests dividing by zero (`'10 / 0'`) and expects the result to be `'Error'`.
|
||||
- **Why**: To ensure the reducer correctly handles the edge case of division by zero, avoiding invalid arithmetic results.
|
||||
|
||||
### 11. **Test: Clearing the Calculator State**
|
||||
|
||||
```javascript
|
||||
it('should reset the state when "CLEAR" action is dispatched', () => {
|
||||
const stateWithValue = {
|
||||
currentValue: '25',
|
||||
previousValue: '5',
|
||||
operator: '+',
|
||||
waitingForOperand: true,
|
||||
};
|
||||
|
||||
const action = { type: 'CLEAR' };
|
||||
const newState = calculatorReducer(stateWithValue, action);
|
||||
|
||||
expect(newState).toEqual(initialState);
|
||||
});
|
||||
```
|
||||
|
||||
- **What it does**: Tests that the `'CLEAR'` action resets the state back to its initial values (`initialState`).
|
||||
- **Why**: To verify that pressing the clear button resets the entire calculator back to its default state.
|
||||
|
||||
## Summary
|
||||
|
||||
- Each test case ensures that the **calculatorReducer** handles individual actions correctly.
|
||||
- The tests cover all possible scenarios, including handling digits, appending digits, performing operations, evaluating expressions, handling edge cases like division by zero, and resetting the state.
|
||||
- This ensures that the calculator works as expected in all cases and prevents regressions when changes are made to the code.
|
||||
25
examples/calculator-reducer/package.json
Normal file
25
examples/calculator-reducer/package.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "calculator-reducer",
|
||||
"version": "1.0.0",
|
||||
"description": "Yet another attempt at implementing a calculator, but this time with a reducer.",
|
||||
"main": "src/index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "vitest --ui",
|
||||
"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": {
|
||||
"@vitest/ui": "^2.1.1",
|
||||
"vitest": "^2.1.1"
|
||||
}
|
||||
}
|
||||
101
examples/calculator-reducer/src/calculator.js
Normal file
101
examples/calculator-reducer/src/calculator.js
Normal file
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* @typedef {import('./types.js').CalculatorState} CalculatorState
|
||||
* @typedef {import('./types.js').CalculatorAction} CalculatorAction
|
||||
*/
|
||||
|
||||
/**
|
||||
* The initial state for the calculator.
|
||||
* @type {CalculatorState}
|
||||
*/
|
||||
const initialState = {
|
||||
currentValue: '0', // Current input or result
|
||||
previousValue: null, // Previous value before an operation
|
||||
operator: null, // The operator (+, -, *, /)
|
||||
waitingForOperand: false, // Tracks if we're waiting for the next operand
|
||||
};
|
||||
|
||||
/**
|
||||
* A reducer function for the calculator state.
|
||||
* @param {CalculatorState} state
|
||||
* @param {CalculatorAction} action
|
||||
* @returns {CalculatorState}
|
||||
*/
|
||||
export function calculatorReducer(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case 'DIGIT':
|
||||
if (state.waitingForOperand) {
|
||||
return {
|
||||
...state,
|
||||
currentValue: action.payload, // Start a new value for next operand
|
||||
waitingForOperand: false,
|
||||
};
|
||||
}
|
||||
// Append the digit
|
||||
return {
|
||||
...state,
|
||||
currentValue:
|
||||
state.currentValue === '0'
|
||||
? action.payload
|
||||
: state.currentValue + action.payload,
|
||||
};
|
||||
|
||||
case 'OPERATOR':
|
||||
// When an operator is pressed, store the current value and operator
|
||||
if (state.operator && state.previousValue !== null) {
|
||||
// If there's an operator already, evaluate the expression
|
||||
const result = evaluate(state);
|
||||
return {
|
||||
...state,
|
||||
previousValue: result,
|
||||
currentValue: '0',
|
||||
operator: action.payload,
|
||||
waitingForOperand: true,
|
||||
};
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
previousValue: state.currentValue,
|
||||
operator: action.payload,
|
||||
waitingForOperand: true,
|
||||
};
|
||||
|
||||
case 'EQUALS':
|
||||
// Perform the operation when '=' is pressed
|
||||
if (state.operator && state.previousValue !== null) {
|
||||
const result = evaluate(state);
|
||||
return {
|
||||
...state,
|
||||
currentValue: result,
|
||||
previousValue: null,
|
||||
operator: null,
|
||||
waitingForOperand: false,
|
||||
};
|
||||
}
|
||||
return state;
|
||||
|
||||
case 'CLEAR':
|
||||
// Clear the calculator state
|
||||
return initialState;
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
function evaluate({ currentValue, previousValue, operator }) {
|
||||
const prev = parseFloat(previousValue);
|
||||
const current = parseFloat(currentValue);
|
||||
|
||||
switch (operator) {
|
||||
case '+':
|
||||
return (prev + current).toString();
|
||||
case '-':
|
||||
return (prev - current).toString();
|
||||
case '*':
|
||||
return (prev * current).toString();
|
||||
case '/':
|
||||
return current !== 0 ? (prev / current).toString() : 'Error';
|
||||
default:
|
||||
return currentValue;
|
||||
}
|
||||
}
|
||||
146
examples/calculator-reducer/src/calculator.test.js
Normal file
146
examples/calculator-reducer/src/calculator.test.js
Normal file
@@ -0,0 +1,146 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { calculatorReducer } from './calculator.js'; // Assume this is the path to the reducer
|
||||
|
||||
/**
|
||||
* @typedef {import('./types.js').CalculatorState} CalculatorState
|
||||
* @typedef {import('./types.js').CalculatorAction} CalculatorAction
|
||||
*/
|
||||
|
||||
/** @type {import('./types.js').CalculatorState} */
|
||||
const initialState = {
|
||||
currentValue: '0',
|
||||
previousValue: null,
|
||||
operator: null,
|
||||
waitingForOperand: false,
|
||||
};
|
||||
|
||||
describe('calculatorReducer', () => {
|
||||
it('should handle a digit input', () => {
|
||||
/** @type {CalculatorAction} */
|
||||
const action = { type: 'DIGIT', payload: '5' };
|
||||
|
||||
const newState = calculatorReducer(initialState, action);
|
||||
|
||||
expect(newState.currentValue).toBe('5');
|
||||
});
|
||||
|
||||
it('should append digits to the current input', () => {
|
||||
/** @type {CalculatorAction} */
|
||||
const firstAction = { type: 'DIGIT', payload: '5' };
|
||||
/** @type {CalculatorAction} */
|
||||
const secondAction = { type: 'DIGIT', payload: '3' };
|
||||
|
||||
let state = calculatorReducer(initialState, firstAction);
|
||||
state = calculatorReducer(state, secondAction);
|
||||
|
||||
expect(state.currentValue).toBe('53');
|
||||
});
|
||||
|
||||
it('should handle operator input', () => {
|
||||
const stateWithDigit = { ...initialState, currentValue: '10' };
|
||||
/** @type {CalculatorAction} */
|
||||
const action = { type: 'OPERATOR', payload: '+' };
|
||||
|
||||
const newState = calculatorReducer(stateWithDigit, action);
|
||||
|
||||
expect(newState.previousValue).toBe('10');
|
||||
expect(newState.operator).toBe('+');
|
||||
expect(newState.waitingForOperand).toBe(true);
|
||||
});
|
||||
|
||||
it('should perform addition when equals is pressed', () => {
|
||||
/** @type {CalculatorState} */
|
||||
const stateWithOperator = {
|
||||
currentValue: '5',
|
||||
previousValue: '10',
|
||||
operator: '+',
|
||||
waitingForOperand: false,
|
||||
};
|
||||
|
||||
/** @type {CalculatorAction} */
|
||||
const action = { type: 'EQUALS' };
|
||||
const newState = calculatorReducer(stateWithOperator, action);
|
||||
|
||||
expect(newState.currentValue).toBe('15');
|
||||
});
|
||||
|
||||
it('should perform subtraction when equals is pressed', () => {
|
||||
/** @type {CalculatorState} */
|
||||
const stateWithOperator = {
|
||||
currentValue: '3',
|
||||
previousValue: '8',
|
||||
operator: '-',
|
||||
waitingForOperand: false,
|
||||
};
|
||||
|
||||
/** @type {CalculatorAction} */
|
||||
const action = { type: 'EQUALS' };
|
||||
const newState = calculatorReducer(stateWithOperator, action);
|
||||
|
||||
expect(newState.currentValue).toBe('5');
|
||||
});
|
||||
|
||||
it('should perform multiplication when equals is pressed', () => {
|
||||
/** @type {CalculatorState} */
|
||||
const stateWithOperator = {
|
||||
currentValue: '4',
|
||||
previousValue: '6',
|
||||
operator: '*',
|
||||
waitingForOperand: false,
|
||||
};
|
||||
|
||||
/** @type {CalculatorAction} */
|
||||
const action = { type: 'EQUALS' };
|
||||
const newState = calculatorReducer(stateWithOperator, action);
|
||||
|
||||
expect(newState.currentValue).toBe('24');
|
||||
});
|
||||
|
||||
it('should perform division when equals is pressed', () => {
|
||||
/** @type {CalculatorState} */
|
||||
const stateWithOperator = {
|
||||
currentValue: '2',
|
||||
previousValue: '10',
|
||||
operator: '/',
|
||||
waitingForOperand: false,
|
||||
};
|
||||
|
||||
/** @type {CalculatorAction} */
|
||||
const action = { type: 'EQUALS' };
|
||||
const newState = calculatorReducer(stateWithOperator, action);
|
||||
|
||||
expect(newState.currentValue).toBe('5');
|
||||
});
|
||||
|
||||
it('should return "Error" when dividing by zero', () => {
|
||||
/** @type {CalculatorState} */
|
||||
const stateWithOperator = {
|
||||
currentValue: '0',
|
||||
previousValue: '10',
|
||||
operator: '/',
|
||||
waitingForOperand: false,
|
||||
};
|
||||
|
||||
/** @type {CalculatorAction} */
|
||||
const action = { type: 'EQUALS' };
|
||||
const newState = calculatorReducer(stateWithOperator, action);
|
||||
|
||||
expect(newState.currentValue).toBe('Error');
|
||||
});
|
||||
|
||||
it('should reset the state when "CLEAR" action is dispatched', () => {
|
||||
/** @type {CalculatorState} */
|
||||
const stateWithValue = {
|
||||
currentValue: '25',
|
||||
previousValue: '5',
|
||||
operator: '+',
|
||||
waitingForOperand: true,
|
||||
};
|
||||
|
||||
/** @type {CalculatorAction} */
|
||||
const action = { type: 'CLEAR' };
|
||||
const newState = calculatorReducer(stateWithValue, action);
|
||||
|
||||
expect(newState).toEqual(initialState);
|
||||
});
|
||||
});
|
||||
37
examples/calculator-reducer/src/types.ts
Normal file
37
examples/calculator-reducer/src/types.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
export type CalculatorState = {
|
||||
currentValue: string;
|
||||
previousValue: string | null;
|
||||
operator: Operator | null;
|
||||
waitingForOperand: boolean;
|
||||
};
|
||||
|
||||
export type CalculatorAction =
|
||||
| DigitAction
|
||||
| OperatorAction
|
||||
| EqualsAction
|
||||
| ClearAction;
|
||||
|
||||
export type Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';
|
||||
export type Operator = '+' | '-' | '*' | '/';
|
||||
|
||||
export type ActionType = 'DIGIT' | 'OPERATOR' | 'EQUALS' | 'CLEAR';
|
||||
|
||||
type DigitAction = {
|
||||
type: 'DIGIT';
|
||||
payload: Digit;
|
||||
};
|
||||
|
||||
type OperatorAction = {
|
||||
type: 'OPERATOR';
|
||||
payload: Operator;
|
||||
};
|
||||
|
||||
type EqualsAction = {
|
||||
type: 'EQUALS';
|
||||
payload?: never;
|
||||
};
|
||||
|
||||
type ClearAction = {
|
||||
type: 'CLEAR';
|
||||
payload?: never;
|
||||
};
|
||||
7
examples/calculator-reducer/vitest.config.js
Normal file
7
examples/calculator-reducer/vitest.config.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
environment: 'happy-dom', // Use jsdom environment for browser testing
|
||||
},
|
||||
});
|
||||
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',
|
||||
},
|
||||
});
|
||||
25
examples/guess-the-number/package.json
Normal file
25
examples/guess-the-number/package.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "guess-the-number",
|
||||
"version": "1.0.0",
|
||||
"description": "Guess the number and test that you're logic actually works.",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"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": {
|
||||
"@vitest/ui": "^2.1.1",
|
||||
"vite": "^5.4.5",
|
||||
"vitest": "^2.1.1"
|
||||
}
|
||||
}
|
||||
5
examples/guess-the-number/vitest.config.js
Normal file
5
examples/guess-the-number/vitest.config.js
Normal file
@@ -0,0 +1,5 @@
|
||||
export default {
|
||||
test: {
|
||||
environment: 'node',
|
||||
},
|
||||
};
|
||||
25
examples/task-list/package.json
Normal file
25
examples/task-list/package.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "task-list",
|
||||
"version": "1.0.0",
|
||||
"description": "Test that you won't forget to write tests.",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"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": {
|
||||
"@vitest/ui": "^2.1.1",
|
||||
"vite": "^5.4.5",
|
||||
"vitest": "^2.1.1"
|
||||
}
|
||||
}
|
||||
5
examples/task-list/vitest.config.js
Normal file
5
examples/task-list/vitest.config.js
Normal file
@@ -0,0 +1,5 @@
|
||||
export default {
|
||||
test: {
|
||||
environment: 'node',
|
||||
},
|
||||
};
|
||||
25
examples/tic-tac-toe/package.json
Normal file
25
examples/tic-tac-toe/package.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "tic-tac-toe",
|
||||
"version": "1.0.0",
|
||||
"description": "The classic game of Tic-Tac-Toe, but this time it's less fun because we're testing it.",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"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": {
|
||||
"@vitest/ui": "^2.1.1",
|
||||
"vite": "^5.4.5",
|
||||
"vitest": "^2.1.1"
|
||||
}
|
||||
}
|
||||
5
examples/tic-tac-toe/vitest.config.js
Normal file
5
examples/tic-tac-toe/vitest.config.js
Normal file
@@ -0,0 +1,5 @@
|
||||
export default {
|
||||
test: {
|
||||
environment: 'node',
|
||||
},
|
||||
};
|
||||
16
jsconfig.json
Normal file
16
jsconfig.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"paths": {
|
||||
"*": ["node_modules/*"],
|
||||
"./*": ["./*"],
|
||||
"./*.html": ["./*"],
|
||||
"./*.html?raw": ["./*"]
|
||||
}
|
||||
},
|
||||
"include": ["examples/**/*.js"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
4714
package-lock.json
generated
Normal file
4714
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
40
package.json
Normal file
40
package.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "testing-javascript",
|
||||
"version": "1.0.0",
|
||||
"description": "A set of examples and exercises for Steve Kinney's \"Introduction to Testing\" course for Frontend Masters.",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "vitest",
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/stevekinney/testing-javascript.git"
|
||||
},
|
||||
"keywords": [
|
||||
"testing",
|
||||
"vitest",
|
||||
"unit",
|
||||
"testing",
|
||||
"javascript",
|
||||
"tutorial"
|
||||
],
|
||||
"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",
|
||||
"workspaces": [
|
||||
"examples/calculator",
|
||||
"examples/tic-tac-toe",
|
||||
"examples/task-list",
|
||||
"examples/authentication",
|
||||
"examples/guess-the-number",
|
||||
"examples/basic-math",
|
||||
"examples/calculator-reducer"
|
||||
],
|
||||
"devDependencies": {
|
||||
"prettier": "^3.3.3"
|
||||
}
|
||||
}
|
||||
9
types.d.ts
vendored
Normal file
9
types.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
declare module '*.html' {
|
||||
const value: string;
|
||||
export default value;
|
||||
}
|
||||
|
||||
declare module '*.html?raw' {
|
||||
const value: string;
|
||||
export default value;
|
||||
}
|
||||
Reference in New Issue
Block a user