Skip to content

Commit 2650e30

Browse files
authored
Replace Happy DOM with Vitest Browser Mode (#250)
1 parent 1380eac commit 2650e30

File tree

11 files changed

+208
-62
lines changed

11 files changed

+208
-62
lines changed

.github/workflows/ci.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ jobs:
5252

5353
- name: Install dependencies
5454
run: yarn --immutable
55+
env:
56+
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: true
5557

5658
- name: Build package
5759
run: yarn build
@@ -91,6 +93,15 @@ jobs:
9193
restore-keys: |
9294
${{ runner.os }}-${{ env.cache-name }}
9395
96+
- name: Cache ~/.cache/ms-playwright
97+
id: playwright-cache
98+
uses: actions/cache@v4
99+
env:
100+
cache-name: playwright-cache
101+
with:
102+
path: ~/.cache/ms-playwright
103+
key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/yarn.lock') }}
104+
94105
- name: Use Node.js
95106
uses: actions/setup-node@v4
96107
with:
@@ -102,5 +113,9 @@ jobs:
102113
- name: Install dependencies
103114
run: yarn --immutable
104115

116+
- name: Install Playwright browsers
117+
if: steps.playwright-cache.outputs.cache-hit != 'true'
118+
run: yarn workspace react-clock playwright install chromium-headless-shell
119+
105120
- name: Run tests
106121
run: yarn unit

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
!**/.yarn/versions
1919

2020
# Project-generated directories and files
21+
__screenshots__
2122
coverage
2223
dist
2324
node_modules

.yarnrc.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
enableScripts: false
2+
13
logFilters:
24
- code: YN0076
35
level: discard

packages/react-clock/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,14 @@
5757
"@types/node": "*",
5858
"@types/react": "*",
5959
"@types/react-dom": "*",
60+
"@vitest/browser": "^3.2.3",
6061
"cpy-cli": "^5.0.0",
61-
"happy-dom": "^15.10.2",
62+
"playwright": "^1.51.1",
6263
"react": "^18.2.0",
6364
"react-dom": "^18.2.0",
6465
"typescript": "^5.5.2",
65-
"vitest": "^3.2.3"
66+
"vitest": "^3.2.3",
67+
"vitest-browser-react": "^1.0.1"
6668
},
6769
"peerDependencies": {
6870
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",

packages/react-clock/src/Clock.spec.tsx

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,19 +150,32 @@ describe('Clock', () => {
150150
const secondMillisecondAngle = secondAngle / 1000;
151151

152152
function getDeg(transform: string) {
153-
const match = transform.match(/rotate\(([0-9.]*)deg\)/);
153+
const match = transform.match(
154+
/matrix\((-?[\d.]+), (-?[\d.]+), (-?[\d.]+), (-?[\d.]+), (-?[\d.]+), (-?[\d.]+)\)/,
155+
);
154156

155157
if (!match) {
156158
throw new Error('Could not parse transform');
157159
}
158160

159-
const deg = match[1];
161+
const [_, ...rawNumbers] = match;
160162

161-
if (!deg) {
163+
const numbers = rawNumbers.map(Number.parseFloat);
164+
165+
const a = numbers[0];
166+
const b = numbers[1];
167+
168+
if (typeof a !== 'number' || typeof b !== 'number') {
162169
throw new Error('Could not parse transform');
163170
}
164171

165-
return Number.parseFloat(deg);
172+
const radians = Math.atan2(b, a);
173+
const degrees = radians * (180 / Math.PI);
174+
175+
// Normalize to [0, 360)
176+
const normalized = ((degrees % 360) + 360) % 360;
177+
178+
return normalized;
166179
}
167180

168181
function getAngle(hand: HTMLElement) {

packages/react-clock/src/Hand.spec.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@ describe('Hand', () => {
1919

2020
const hand = container.querySelector('.react-clock__hand');
2121

22-
expect(hand).toHaveStyle('transform: rotate(0deg)');
22+
expect(hand).toHaveStyle('transform: matrix(1, 0, 0, 1, 0, 0)'); // rotate(0deg)
2323
});
2424

2525
it('renders properly angled hand given angle prop', () => {
2626
const { container } = render(<Hand angle={15} name="minute" />);
2727

2828
const hand = container.querySelector('.react-clock__hand');
2929

30-
expect(hand).toHaveStyle('transform: rotate(15deg)');
30+
expect(hand).toHaveStyle('transform: matrix(0.965926, 0.258819, -0.258819, 0.965926, 0, 0)'); // rotate(15deg)
3131
});
3232

3333
it('renders hand with 100% length by default', () => {

packages/react-clock/src/Mark.spec.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@ describe('Mark', () => {
1919

2020
const mark = container.querySelector('.react-clock__mark');
2121

22-
expect(mark).toHaveStyle('transform: rotate(0deg)');
22+
expect(mark).toHaveStyle('transform: matrix(1, 0, 0, 1, 0, 0)'); // rotate(0deg)
2323
});
2424

2525
it('renders properly angled mark given angle prop', () => {
2626
const { container } = render(<Mark angle={15} name="minute" />);
2727

2828
const mark = container.querySelector('.react-clock__mark');
2929

30-
expect(mark).toHaveStyle('transform: rotate(15deg)');
30+
expect(mark).toHaveStyle('transform: matrix(0.965926, 0.258819, -0.258819, 0.965926, 0, 0)'); // rotate(15deg)
3131
});
3232

3333
it('renders mark with 10% length by default', () => {

packages/react-clock/src/MarkNumber.spec.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,16 @@ describe('MarkNumber', () => {
1717

1818
const markNumber = container.querySelector('.react-clock__mark__number');
1919

20-
expect(markNumber).toHaveStyle('transform: rotate(-0deg)');
20+
expect(markNumber).toHaveStyle('transform: matrix(1, 0, 0, 1, 0, 0)'); // rotate(0deg)
2121
});
2222

2323
it('renders properly angled mark given angle prop', () => {
2424
const { container } = render(<MarkNumber angle={15} name="minute" number={1} />);
2525

2626
const markNumber = container.querySelector('.react-clock__mark__number');
2727

28-
expect(markNumber).toHaveStyle('transform: rotate(-15deg)');
28+
expect(markNumber).toHaveStyle(
29+
'transform: matrix(0.965926, -0.258819, 0.258819, 0.965926, 0, 0)',
30+
); // rotate(-15deg)
2931
});
3032
});

packages/react-clock/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"skipLibCheck": true,
1414
"strict": true,
1515
"target": "es2018",
16+
"types": ["@vitest/browser/matchers"],
1617
"verbatimModuleSyntax": true
1718
},
1819
"exclude": ["dist"]

packages/react-clock/vitest.config.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ import { defineConfig } from 'vitest/config';
22

33
export default defineConfig({
44
test: {
5-
environment: 'happy-dom',
5+
browser: {
6+
enabled: true,
7+
headless: true,
8+
instances: [{ browser: 'chromium' }],
9+
provider: 'playwright',
10+
},
611
setupFiles: 'vitest.setup.ts',
712
watch: false,
813
},

0 commit comments

Comments
 (0)