Skip to content

Commit 4cf0ec3

Browse files
committed
Add example tests
1 parent 36395bf commit 4cf0ec3

File tree

13 files changed

+258
-80
lines changed

13 files changed

+258
-80
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
name: 📥 Checkout
2+
description: 'Pinned checkout wrapper'
3+
4+
inputs:
5+
fetch-depth:
6+
description: 'Depth for git fetch (default 0 for full history)'
7+
default: '0'
8+
9+
runs:
10+
using: composite
11+
steps:
12+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
13+
with:
14+
fetch-depth: ${{ inputs.fetch-depth }}

.github/actions/code-analysis/action.yml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
name: 👁️‍🗨️ Code Analysis
22
description: 'Run code analysis'
33

4-
inputs:
5-
github-token:
6-
description: 'GitHub token for API access'
7-
required: true
8-
94
runs:
105
using: 'composite'
116
steps:
Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
name: 💻 Node.js setup
22
description: 'Setup Node.js environment and install dependencies'
33

4+
inputs:
5+
install-dependencies:
6+
description: 'Set to false to skip pnpm install (for tooling-only jobs)'
7+
default: 'true'
8+
49
runs:
510
using: 'composite'
611
steps:
@@ -9,37 +14,22 @@ runs:
914
shell: bash
1015

1116
- name: 🎰 Setup Node
12-
uses: actions/setup-node@v4
17+
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
1318
with:
1419
node-version-file: 'package.json'
15-
16-
- name: 🎈 Install pNPM
17-
uses: pnpm/action-setup@v3
18-
with:
19-
run_install: false
20-
21-
- name: 📀 Get pnpm store directory
22-
run: |
23-
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
24-
shell: bash
25-
26-
- name: 💾 Setup pnpm cache
27-
uses: actions/cache@v4
28-
with:
29-
path: ${{ env.STORE_PATH }}
30-
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
31-
restore-keys: |
32-
${{ runner.os }}-pnpm-store-
20+
cache: 'pnpm'
21+
cache-dependency-path: 'pnpm-lock.yaml'
3322

3423
- name: 📥 Install dependencies
24+
if: inputs.install-dependencies == 'true'
3525
run: pnpm install --frozen-lockfile --prefer-offline --ignore-scripts
3626
shell: bash
3727

3828
- name: 💾 Cache turbo build setup
39-
uses: actions/cache@v4
29+
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830
4030
with:
4131
path: .turbo
42-
key: ${{ runner.os }}-turbo-${{ github.head_ref || github.ref_name }}-${{ github.sha }}
32+
key: ${{ runner.os }}-turbo-${{ github.head_ref || github.ref_name }}-${{ hashFiles('pnpm-lock.yaml') }}
4333
restore-keys: |
4434
${{ runner.os }}-turbo-${{ github.head_ref || github.ref_name }}-
4535
${{ runner.os }}-turbo-

.github/actions/test-coverage/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,6 @@ runs:
3838
env:
3939
GITHUB_TOKEN: ${{ inputs.github-token }}
4040
PR_NUMBER: ${{ github.event.pull_request.number }}
41-
BASE_COVERAGE_ROOT: base-coverage/coverage
41+
BASE_COVERAGE_ROOT: base-coverage
4242
run: node scripts/comment-coverage-diff.js
4343
shell: bash

.github/workflows/base-coverage.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@ on:
66
- main
77
workflow_dispatch:
88

9+
concurrency:
10+
group: base-coverage-${{ github.ref }}
11+
cancel-in-progress: true
12+
913
jobs:
1014
upload-coverage:
1115
runs-on: ubuntu-24.04
1216
steps:
1317
- name: 📥 Checkout Repository
14-
uses: actions/checkout@v4
18+
uses: ./.github/actions/checkout
1519

1620
- name: 💻 Node setup
1721
uses: ./.github/actions/node-setup
@@ -25,7 +29,7 @@ jobs:
2529
shell: bash
2630

2731
- name: 📤 Upload coverage artifact
28-
uses: actions/upload-artifact@v4
32+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
2933
with:
3034
name: base-coverage
3135
path: coverage

.github/workflows/ci.yml

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
name: 🧪 CI
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened]
6+
workflow_dispatch:
7+
8+
permissions:
9+
contents: read # required so checkout/actions using GITHUB_TOKEN can read repo
10+
11+
concurrency:
12+
group: ci-${{ github.ref }}
13+
cancel-in-progress: true
14+
15+
jobs:
16+
lint:
17+
runs-on: ubuntu-24.04
18+
steps:
19+
- name: 📥 Checkout Repository
20+
uses: ./.github/actions/checkout
21+
22+
- name: 💻 Node setup
23+
uses: ./.github/actions/node-setup
24+
25+
- name: 👁️‍🗨️ Code Analysis
26+
uses: ./.github/actions/code-analysis
27+
28+
licenses:
29+
runs-on: ubuntu-24.04
30+
needs: lint
31+
steps:
32+
- name: 📥 Checkout Repository
33+
uses: ./.github/actions/checkout
34+
35+
- name: 💻 Node setup
36+
uses: ./.github/actions/node-setup
37+
38+
- name: 🗳️ Check Licenses
39+
uses: ./.github/actions/check-licenses
40+
41+
build:
42+
runs-on: ubuntu-24.04
43+
needs: lint
44+
steps:
45+
- name: 📥 Checkout Repository
46+
uses: ./.github/actions/checkout
47+
48+
- name: 💻 Node setup
49+
uses: ./.github/actions/node-setup
50+
51+
- name: 🏗️ Build packages
52+
run: pnpm build
53+
shell: bash
54+
55+
coverage:
56+
runs-on: ubuntu-24.04
57+
needs: lint
58+
permissions:
59+
actions: read # download base coverage artifact via workflow run APIs
60+
contents: read # checkout
61+
pull-requests: write # comment coverage diff on PRs
62+
steps:
63+
- name: 📥 Checkout Repository
64+
uses: ./.github/actions/checkout
65+
66+
- name: 💻 Node setup
67+
uses: ./.github/actions/node-setup
68+
69+
- name: 🧪 Test Coverage
70+
uses: ./.github/actions/test-coverage
71+
with:
72+
github-token: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/code-analysis.yml

Lines changed: 0 additions & 38 deletions
This file was deleted.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { render, screen } from '@testing-library/react';
2+
3+
import { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '../card';
4+
import { Button } from '../button';
5+
6+
describe('Card component suite', () => {
7+
it('renders all structural slots with custom classNames', () => {
8+
render(
9+
<Card className="test-card" data-testid="card-root">
10+
<CardHeader className="header-slot">
11+
<CardTitle>Plan</CardTitle>
12+
<CardDescription>Choose the perfect plan</CardDescription>
13+
<CardAction>
14+
<Button>Primary action</Button>
15+
</CardAction>
16+
</CardHeader>
17+
<CardContent className="content-slot">Card body content</CardContent>
18+
<CardFooter className="footer-slot">Footer CTA</CardFooter>
19+
</Card>
20+
);
21+
22+
expect(screen.getByTestId('card-root')).toHaveClass('test-card');
23+
expect(screen.getByText('Plan')).toHaveAttribute('data-slot', 'card-title');
24+
expect(screen.getByText('Choose the perfect plan')).toHaveAttribute('data-slot', 'card-description');
25+
expect(screen.getByRole('button', { name: /primary action/i })).toBeInTheDocument();
26+
expect(screen.getByText('Card body content')).toHaveClass('content-slot');
27+
expect(screen.getByText('Footer CTA')).toHaveClass('footer-slot');
28+
});
29+
30+
it('forwards arbitrary props down to the DOM nodes', () => {
31+
render(
32+
<Card id="pricing-card" aria-label="Pricing overview">
33+
<CardContent>Details</CardContent>
34+
</Card>
35+
);
36+
37+
const card = screen.getByLabelText('Pricing overview');
38+
expect(card).toHaveAttribute('id', 'pricing-card');
39+
});
40+
});
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { render, screen } from '@testing-library/react';
2+
import userEvent from '@testing-library/user-event';
3+
4+
import { Input } from '../input';
5+
6+
describe('Input component', () => {
7+
it('renders with placeholder and custom classes', () => {
8+
render(<Input placeholder="Email" className="custom-input" data-testid="input-element" />);
9+
10+
const input = screen.getByPlaceholderText('Email');
11+
expect(input).toHaveClass('custom-input');
12+
expect(input).toHaveAttribute('data-slot', 'input');
13+
});
14+
15+
it('accepts user input and forwards native props', async () => {
16+
const handleChange = jest.fn();
17+
const user = userEvent.setup();
18+
19+
render(<Input aria-invalid="true" onChange={handleChange} />);
20+
21+
const input = screen.getByRole('textbox');
22+
await user.type(input, 'Hello');
23+
24+
expect(handleChange).toHaveBeenCalled();
25+
expect(input).toHaveAttribute('aria-invalid', 'true');
26+
expect(input).toHaveValue('Hello');
27+
});
28+
});
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { render, screen } from '@testing-library/react';
2+
3+
import { Label } from '../label';
4+
5+
describe('Label component', () => {
6+
it('associates with form controls via htmlFor', () => {
7+
render(
8+
<div>
9+
<Label htmlFor="field">Field label</Label>
10+
<input id="field" />
11+
</div>
12+
);
13+
14+
const label = screen.getByText('Field label');
15+
expect(label).toHaveAttribute('for', 'field');
16+
});
17+
18+
it('respects custom class names and data attributes', () => {
19+
render(
20+
<Label className="text-accent" data-testid="label">
21+
Content
22+
</Label>
23+
);
24+
25+
const label = screen.getByTestId('label');
26+
expect(label).toHaveClass('text-accent');
27+
expect(label).toHaveAttribute('data-slot', 'label');
28+
});
29+
});

0 commit comments

Comments
 (0)