This commit is contained in:
Philippe Torrel
2026-10-30 12:17:04 +01:00
parent e9211c0a92
commit e91c11346e
9 changed files with 360 additions and 1 deletions
@@ -20,6 +20,7 @@
"@types/react": "^19.3.0",
"@types/react-dom": "^19.3.0",
"@vitejs/plugin-react": "^6.1.2",
"@vitest/ui": "^5.0.5",
"jsdom": "^30.1.3",
"oxlint": "^1.90.0",
"sass-embedded": "^1.107.0",
@@ -973,6 +974,13 @@
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@polka/url": {
"version": "1.0.0-next.29",
"resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz",
"integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==",
"dev": true,
"license": "MIT"
},
"node_modules/@popperjs/core": {
"version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
@@ -1474,6 +1482,19 @@
}
}
},
"node_modules/@vitest/pretty-format": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-5.0.5.tgz",
"integrity": "sha512-24sSdIlMA9DVPXnYiB9siwU0bTd887Y9jX3+vabztCSsvHwo7ipvJ/a2L3Rljw8+2rK1f5fCj3h5U9I/Jsqgbw==",
"dev": true,
"license": "MIT",
"dependencies": {
"tinyrainbow": "^3.2.0"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/@vitest/spy": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-5.0.5.tgz",
@@ -1484,6 +1505,42 @@
"url": "https://opencollective.com/vitest"
}
},
"node_modules/@vitest/ui": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-5.0.5.tgz",
"integrity": "sha512-dCoGUOdgmCaSB2g+WQ/spPCBkytAWdNUkFSXY4z+eYp2d8JEOY8v0uDRaJTG9qf/kTlTVZ4CuYMYMz/f1HS/qQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/utils": "5.0.5",
"fflate": "^0.8.3",
"flatted": "^3.4.4",
"pathe": "^2.0.3",
"sirv": "^3.0.2",
"tinyrainbow": "^3.2.0"
},
"funding": {
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
"vitest": "5.0.5"
}
},
"node_modules/@vitest/utils": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-5.0.5.tgz",
"integrity": "sha512-/rBu7gzLDQI/SC79Tn98b5DwOgaHU/Gt4I9OVeP3YofzNaYtbImRCOJQmT1S6vOJg9gEIlWC+r3QxKkjuSa1nQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/pretty-format": "5.0.5",
"convert-source-map": "^2.0.0",
"tinyrainbow": "^3.2.0"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
@@ -1596,6 +1653,13 @@
"url": "https://opencollective.com/color"
}
},
"node_modules/convert-source-map": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
"dev": true,
"license": "MIT"
},
"node_modules/css-tree": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz",
@@ -1731,6 +1795,20 @@
}
}
},
"node_modules/fflate": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz",
"integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==",
"dev": true,
"license": "MIT"
},
"node_modules/flatted": {
"version": "3.4.4",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz",
"integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==",
"dev": true,
"license": "ISC"
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -2187,6 +2265,16 @@
"node": ">=4"
}
},
"node_modules/mrmime": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
"integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/nanoid": {
"version": "3.3.20",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz",
@@ -2290,6 +2378,13 @@
"url": "https://github.com/inikulin/parse5?sponsor=1"
}
},
"node_modules/pathe": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
"integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
"dev": true,
"license": "MIT"
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -2883,6 +2978,21 @@
"integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==",
"license": "MIT"
},
"node_modules/sirv": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz",
"integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@polka/url": "^1.0.0-next.24",
"mrmime": "^2.0.0",
"totalist": "^3.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/source-map-js": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz",
@@ -2989,6 +3099,16 @@
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/tinyrainbow": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.2.0.tgz",
"integrity": "sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/tldts": {
"version": "7.4.19",
"resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.19.tgz",
@@ -3009,6 +3129,16 @@
"dev": true,
"license": "MIT"
},
"node_modules/totalist": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
"integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/tough-cookie": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.2.tgz",
@@ -4,8 +4,9 @@
"version": "0.0.0",
"type": "module",
"scripts": {
"test": "vitest",
"test": "jest",
"test:watch": "vitest --watch",
"test:ui": "npx vitest --ui",
"dev": "vite",
"build": "vite build",
"lint": "oxlint",
@@ -24,6 +25,7 @@
"@types/react": "^19.3.0",
"@types/react-dom": "^19.3.0",
"@vitejs/plugin-react": "^6.1.2",
"@vitest/ui": "^5.0.5",
"jsdom": "^30.1.3",
"oxlint": "^1.90.0",
"sass-embedded": "^1.107.0",
@@ -1,4 +1,5 @@
import Button from './components/buttons/Button';
import FormContact from './components/forms/FormContact';
function App(props) {
// const { } = props;
@@ -8,6 +9,8 @@ function App(props) {
<h1>Testing in React</h1>
<hr />
<Button />
<hr />
<FormContact />
</div>
);
}
@@ -0,0 +1,82 @@
import { fireEvent, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { expect, test, it, describe, afterAll, vi } from 'vitest';
// Module
import FormContact from '../components/forms/FormContact';
const consoleSpy = vi.spyOn(console, 'log');
afterAll(() => {
consoleSpy.mockRestore();
});
// Test Suite
describe('FormContact', () => {
// Test Case
it('should submit data object correctly', async () => {
render(<FormContact />);
const user = userEvent.setup();
// 1. Find elements (Best Practice: getByLabelText for forms)
const firstnameInput = screen.getByLabelText(/Firstname/i);
const lastnameInput = screen.getByLabelText(/Lastname/i);
const emailInput = screen.getByLabelText(/Email/i);
const messageInput = screen.getByLabelText(/Message/i);
const submitButton = screen.getByRole('button', { name: /Send/i });
// 2. Simulate interactions (userEvent is asynchronous!)
// await user.type(firstnameInput, 'John');
// await user.type(lastnameInput, 'Doe');
// await user.type(emailInput, 'john.doe@example.com');
// await user.type(messageInput, 'Hello, this is a test message.');
// await user.click(firstnameInput);
// await user.paste('John');
// await user.click(lastnameInput);
// await user.paste('Doe');
// await user.click(emailInput);
// await user.paste('john.doe@example.com');
// await user.click(messageInput);
// await user.paste('Hello, this is a test message.');
fireEvent.change(firstnameInput, { target: { value: 'John' } });
fireEvent.change(lastnameInput, { target: { value: 'Doe' } });
fireEvent.change(emailInput, { target: { value: 'john.doe@example.com' } });
fireEvent.change(messageInput, { target: { value: 'Hello, this is a test message.' } });
// 3. Submit
await user.click(submitButton);
// 4. Assertion (The verification)
// We check if console.log was called with the correct object
expect(consoleSpy).toHaveBeenCalledWith('Form Data:', {
firstname: 'John',
lastname: 'Doe',
email: 'john.doe@example.com',
message: 'Hello, this is a test message.',
});
});
// it('should show error message when firstname is empty on submit', async () => {
// render(<FormContact />);
// const user = userEvent.setup();
// const lastNameInput = screen.getByLabelText('Lastname');
// const emailInput = screen.getByLabelText('E-Mail');
// const messageInput = screen.getByLabelText('Message');
// const submitButton = screen.getByRole('button', { name: 'Send Message' });
// await user.type(lastNameInput, 'Wick');
// await user.type(emailInput, 'john@example.com');
// await user.type(messageInput, 'This is a valid test message');
// await user.click(submitButton);
// expect(screen.getByText('Vorname ist erforderlich.')).toBeInTheDocument();
// });
});
@@ -11,5 +11,7 @@ export default defineConfig({
environment: 'jsdom', // use jsdom as the test environment
setupFiles: './src/tests/setup.js', // if you have a setup file
// more options here
testTimeout: 5000, // 5 Sekunden (default) pro Test Case
// hookTimeout: 10000, // Timeout für beforeAll, beforeEach etc. (Standard: 10.000 ms)
},
});
Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 630 KiB

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>continuous integration (CI) oder continuous delivery (CD)</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" />
</head>
<body>
<main>
<div class="container py-5">
<h1>continuous integration (CI) oder continuous delivery (CD)</h1>
<img src="assets/img/DevOps-Marketplace-Infinity-Loop.png" alt="" class="img-thumbnail" />
</div>
</main>
</body>
</html>