2025-11-03 21:46:15 +01:00
|
|
|
|
# Tutorial 1: Hello World!
|
|
|
|
|
|
|
|
|
|
|
|
Let’s walk through your first **HelloWorld** component built with **TestDrive-UI**.
|
|
|
|
|
|
|
|
|
|
|
|
This will demonstrate the full workflow:
|
|
|
|
|
|
👉 requirement → test → implementation → run → refinement.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 🧱 1. Project setup
|
|
|
|
|
|
|
|
|
|
|
|
If you haven’t already:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
unzip testdrive-ui.zip
|
|
|
|
|
|
cd testdrive-ui
|
|
|
|
|
|
npm install
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Now you’re ready to create your first component.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 🧩 2. Create a component folder
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
mkdir src/components/hello-world
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Inside it, you’ll have:
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
hello-world/
|
|
|
|
|
|
├── hello-world.js
|
|
|
|
|
|
├── hello-world.test.js
|
|
|
|
|
|
└── hello-world.stories.js
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## ✏️ 3. Step 1 — Write the requirement
|
|
|
|
|
|
|
|
|
|
|
|
> “When `<hello-world>` is rendered, it should display the text **Hello World!**
|
|
|
|
|
|
> and clicking the element should show an alert.”
|
|
|
|
|
|
|
|
|
|
|
|
That’s your behavioral spec — the *“why”* that drives the test.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 🧪 4. Step 2 — Write the test first
|
|
|
|
|
|
|
|
|
|
|
|
`src/components/hello-world/hello-world.test.js`
|
|
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
import "./hello-world.js";
|
|
|
|
|
|
|
|
|
|
|
|
describe("<hello-world>", () => {
|
|
|
|
|
|
it("renders the correct greeting", () => {
|
|
|
|
|
|
const el = document.createElement("hello-world");
|
|
|
|
|
|
document.body.appendChild(el);
|
|
|
|
|
|
|
|
|
|
|
|
const content = el.shadowRoot.textContent;
|
|
|
|
|
|
expect(content).to.include("Hello World!");
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it("triggers an alert on click", () => {
|
|
|
|
|
|
const el = document.createElement("hello-world");
|
|
|
|
|
|
document.body.appendChild(el);
|
|
|
|
|
|
|
|
|
|
|
|
let alerted = false;
|
|
|
|
|
|
window.alert = () => (alerted = true);
|
|
|
|
|
|
|
|
|
|
|
|
const div = el.shadowRoot.querySelector("div");
|
|
|
|
|
|
div.click();
|
|
|
|
|
|
|
|
|
|
|
|
expect(alerted).to.be.true;
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Run this test now:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
npm test
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Both tests will **fail** initially — perfect, that’s the TDD start.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 💡 5. Step 3 — Implement the component
|
|
|
|
|
|
|
|
|
|
|
|
`src/components/hello-world/hello-world.js`
|
|
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
import { LitElement, html, css } from "lit";
|
|
|
|
|
|
|
|
|
|
|
|
export class HelloWorld extends LitElement {
|
|
|
|
|
|
static styles = css`
|
|
|
|
|
|
div {
|
|
|
|
|
|
font-family: system-ui, sans-serif;
|
|
|
|
|
|
font-size: 1.5rem;
|
|
|
|
|
|
color: #007acc;
|
|
|
|
|
|
padding: 1rem;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
user-select: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
div:hover {
|
|
|
|
|
|
color: #005fa3;
|
|
|
|
|
|
}
|
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
render() {
|
|
|
|
|
|
return html`<div @click=${this._onClick}>Hello World!</div>`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_onClick() {
|
|
|
|
|
|
alert("Hello from TestDrive-UI!");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
customElements.define("hello-world", HelloWorld);
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Run the tests again:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
npm test
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
✅ Both should now pass.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## ⚡ 6. Step 4 — Preview it in the browser
|
|
|
|
|
|
|
|
|
|
|
|
Add it to your `index.html`:
|
|
|
|
|
|
|
|
|
|
|
|
```html
|
|
|
|
|
|
<script type="module" src="./components/hello-world/hello-world.js"></script>
|
|
|
|
|
|
<hello-world></hello-world>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Then run:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
npm run dev
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Open [http://localhost:5173](http://localhost:5173)
|
|
|
|
|
|
→ You’ll see your clickable **Hello World!** component rendered live.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 🧭 7. Step 5 — Visual story (optional)
|
|
|
|
|
|
|
|
|
|
|
|
`src/components/hello-world/hello-world.stories.js`
|
|
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
import "./hello-world.js";
|
|
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
|
title: "UI/Hello World",
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export const Default = () => `<hello-world></hello-world>`;
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Once you add Storybook to the scaffold later, this file will automatically generate a visual preview card.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## ✅ 8. What you achieved
|
|
|
|
|
|
|
|
|
|
|
|
* Created an **independent UI component** with Lit
|
|
|
|
|
|
* Wrote deterministic **tests with jsdom + Mocha**
|
|
|
|
|
|
* Verified behavior automatically
|
|
|
|
|
|
* Previewed visually via **Vite**
|
|
|
|
|
|
|
|
|
|
|
|
This loop is your core **TestDrive-UI workflow**:
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
spec → test → implement → run → refine
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
xxx
|