diff --git a/.github/workflows/github-ci.yml b/.github/workflows/github-ci.yml index 42065ab..f77271c 100644 --- a/.github/workflows/github-ci.yml +++ b/.github/workflows/github-ci.yml @@ -2,32 +2,103 @@ name: github-ci on: push: + branches: [main] pull_request: branches: [main] jobs: - build-and-deploy: + verify: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: 20 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Install Playwright Chromium + run: pnpm exec playwright install --with-deps chromium + + - name: Run unit tests + run: pnpm test:run + + - name: Run source e2e tests + run: pnpm test:e2e + + - name: Build library and docs + run: pnpm build + + - name: Run coverage on main + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + run: pnpm coverage + + smoke: + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + needs: verify runs-on: ubuntu-latest steps: - # checkout source - name: Checkout - uses: actions/checkout@v4 - # generate artifacts - - name: install and build - run: | - npm i -g pnpm - pnpm install - pnpm test - pnpm coverage - pnpm build - - name: github-ci + uses: actions/checkout@v6 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: 20 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Install Playwright Chromium + run: pnpm exec playwright install --with-deps chromium + + - name: Run packed package smoke tests + run: pnpm test:smoke + + deploy: + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + needs: [verify, smoke] + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: 20 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build library and docs + run: pnpm build + + - name: Deploy docs uses: crazy-max/ghaction-github-pages@v4 with: target_branch: gh-pages build_dir: docs/dist env: GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} - - name: codecov + + - name: Upload coverage to Codecov uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.gitignore b/.gitignore index 488cb67..2bbfa1b 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ lib # dependencies node_modules +.pnpm-store # logs *.log @@ -33,4 +34,8 @@ pnpm-debug.log* coverage demo *.rar -*.zip \ No newline at end of file +*.zip +playwright-report +test-results +smoke/.artifacts +*.tgz diff --git a/README.md b/README.md index a998d68..fb032e8 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ - Test Coverage + Test Coverage GitHub stars diff --git a/docs/plans/2026-03-08-e2e-smoke-ci-implementation.md b/docs/plans/2026-03-08-e2e-smoke-ci-implementation.md new file mode 100644 index 0000000..d6e02e8 --- /dev/null +++ b/docs/plans/2026-03-08-e2e-smoke-ci-implementation.md @@ -0,0 +1,216 @@ +# E2E, Smoke, and CI Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Add practical Playwright-based source E2E tests, packed-package smoke tests, and CI automation for this React component library without depending on the docs site. + +**Architecture:** Build a minimal `e2e` harness app that imports the library source in a real browser and exposes stable assertions through `data-testid`. Build a separate `smoke` consumer app that installs the packed tarball, verifies package exports in a realistic consumer environment, and runs Playwright against that app. Keep unit coverage in `vitest`, browser behavior in `Playwright`, and wire all three into GitHub Actions. + +**Tech Stack:** React 19, Vite, Playwright, pnpm, GitHub Actions + +--- + +### Task 1: Add test tooling and scripts + +**Files:** + +- Modify: `package.json` +- Modify: `pnpm-lock.yaml` + +**Step 1: Write the failing test** + +Create commands that should exist after the feature is complete: + +```bash +pnpm test:e2e --list +pnpm test:smoke --list +``` + +**Step 2: Run test to verify it fails** + +Run: `pnpm test:e2e --list` +Expected: FAIL because the script does not exist yet. + +**Step 3: Write minimal implementation** + +Add root dev dependencies and scripts: + +```json +{ + "scripts": { + "test:run": "vitest run", + "test:e2e": "playwright test", + "test:e2e:ui": "playwright test --ui", + "smoke:prepare": "node ./smoke/scripts/prepare-smoke-package.mjs", + "test:smoke": "playwright test --config smoke/playwright.config.ts" + }, + "devDependencies": { + "@playwright/test": "...", + "@vitejs/plugin-react": "...", + "vite": "..." + } +} +``` + +**Step 4: Run test to verify it passes** + +Run: `pnpm test:e2e --list` +Expected: Playwright starts and lists zero-or-more tests instead of script-not-found. + +### Task 2: Add source E2E harness and failing browser tests + +**Files:** + +- Create: `playwright.config.ts` +- Create: `e2e/index.html` +- Create: `e2e/app/main.tsx` +- Create: `e2e/app/App.tsx` +- Create: `e2e/global-setup.mjs` +- Create: `e2e/global-teardown.mjs` +- Create: `e2e/vite.config.ts` +- Create: `e2e/tests/components.spec.ts` + +**Step 1: Write the failing test** + +Add Playwright tests for these source-level scenarios: + +```ts +test('renders truncate scenario in browser', ...) +test('show-more expands and collapses', ...) +test('middle-truncate preserves suffix', ...) +test('truncate recalculates after resize', ...) +``` + +Use `data-testid` markers like `truncate-text`, `show-more-state`, `middle-text`, and `resize-width` in the harness app so the tests assert user-visible behavior. + +**Step 2: Run test to verify it fails** + +Run: `pnpm test:e2e e2e/tests/components.spec.ts` +Expected: FAIL because the harness app/config/files do not exist yet. + +**Step 3: Write minimal implementation** + +Build a Vite-powered React harness rooted at `e2e/` that renders: + +- a narrow `Truncate` example with long text +- a `ShowMore` example with clear expand/collapse labels +- a `MiddleTruncate` example with a deterministic file-like string +- a resize scenario driven by container width state and buttons + +Start/stop the harness server from Playwright global setup and teardown, matching the `vue-picture-cropper` pattern. + +**Step 4: Run test to verify it passes** + +Run: `pnpm test:e2e e2e/tests/components.spec.ts` +Expected: PASS for the new source E2E tests. + +### Task 3: Add packed-package smoke consumer and failing smoke tests + +**Files:** + +- Create: `smoke/consumer/index.html` +- Create: `smoke/consumer/package.json` +- Create: `smoke/consumer/tsconfig.json` +- Create: `smoke/consumer/vite.config.ts` +- Create: `smoke/consumer/src/main.tsx` +- Create: `smoke/consumer/src/App.tsx` +- Create: `smoke/global-setup.mjs` +- Create: `smoke/global-teardown.mjs` +- Create: `smoke/playwright.config.ts` +- Create: `smoke/scripts/prepare-smoke-package.mjs` +- Create: `smoke/tests/package-smoke.spec.ts` + +**Step 1: Write the failing test** + +Add smoke assertions for the packed tarball consumer: + +```ts +test('renders packed package consumer page', ...) +test('packed package show-more interaction works', ...) +``` + +The consumer should expose stable status nodes like `smoke-page-ready`, `smoke-package-imported`, `smoke-truncate-text`, and `smoke-show-more-state`. + +**Step 2: Run test to verify it fails** + +Run: `pnpm test:smoke --list` +Expected: FAIL because the smoke config/consumer do not exist yet. + +**Step 3: Write minimal implementation** + +Follow the reference repository pattern: + +- build the library tarball with `pnpm pack` +- rewrite `smoke/consumer/package.json` to install the tarball +- install consumer dependencies +- start the consumer app with Vite +- run Playwright against the consumer app + +Keep the smoke app minimal and only prove that real package exports render and interact correctly. + +**Step 4: Run test to verify it passes** + +Run: `pnpm test:smoke` +Expected: PASS for the smoke scenarios. + +### Task 4: Update CI for unit, E2E, smoke, and deploy + +**Files:** + +- Modify: `.github/workflows/github-ci.yml` + +**Step 1: Write the failing test** + +Define the expected CI stages as a checklist: + +- verify install on Node 20 with pnpm cache +- install Playwright Chromium +- run `pnpm test:run` +- run `pnpm test:e2e` +- build library and docs +- optionally run coverage and smoke on `main` + +**Step 2: Run test to verify it fails** + +Compare the current workflow against the checklist. +Expected: FAIL because it does not install Playwright or run the new E2E/smoke commands. + +**Step 3: Write minimal implementation** + +Restructure CI into separate `verify`, `smoke`, and `deploy` jobs following the proven layout from the reference repo, adapted to this repo’s docs output directory. + +**Step 4: Run test to verify it passes** + +Run: `pnpm exec tsc --noEmit` only if needed for changed TS files, then inspect `.github/workflows/github-ci.yml` against the checklist. +Expected: Workflow definition includes all required jobs and commands. + +### Task 5: Final verification + +**Files:** + +- Verify only + +**Step 1: Run focused checks** + +```bash +pnpm test:e2e e2e/tests/components.spec.ts +pnpm test:smoke +``` + +**Step 2: Run broader checks** + +```bash +pnpm test:run +pnpm build +``` + +**Step 3: Review changed files** + +```bash +git status --short +git diff --stat +``` + +**Step 4: Summarize evidence** + +Record exact commands run and whether they passed before claiming completion. diff --git a/docs/src/components/StatisticalData.tsx b/docs/src/components/StatisticalData.tsx index dcb9d2a..80014b6 100644 --- a/docs/src/components/StatisticalData.tsx +++ b/docs/src/components/StatisticalData.tsx @@ -22,7 +22,7 @@ const options = [ }, { link: `https://codecov.io/gh/${repo}`, - image: `https://codecov.io/gh/${repo}/graph/badge.svg?token=K9W9KRBTMA&color=${color}`, + image: `https://img.shields.io/codecov/c/github/${repo}?color=${color}`, title: 'Test Coverage', }, { diff --git a/e2e/app/App.tsx b/e2e/app/App.tsx new file mode 100644 index 0000000..f776979 --- /dev/null +++ b/e2e/app/App.tsx @@ -0,0 +1,117 @@ +import React, { useEffect, useMemo, useState } from 'react' +import { MiddleTruncate, ShowMore, Truncate } from '../../src' + +const LONG_TEXT = + 'This sentence keeps going so the component must clamp it safely. This extra copy ensures the browser truncates it in a narrow box.' +const SHOW_MORE_TEXT = + 'ShowMore should reveal the hidden text after interaction and allow collapsing again without leaving the browser page.' +const FILE_NAME = 'Quarterly-operating-report-final-reviewed-version-2026.pdf' +const RESIZE_TEXT = + 'Resizing the container should force truncation to recalculate and produce a shorter visible result in the narrow state.' + +const sectionStyle: React.CSSProperties = { + display: 'grid', + gap: '8px', +} + +const boxStyle: React.CSSProperties = { + border: '1px solid #d4d4d8', + borderRadius: '8px', + padding: '12px', + background: '#fff', +} + +export const App: React.FC = () => { + const [ready, setReady] = useState(false) + const [showMoreState, setShowMoreState] = useState<'collapsed' | 'expanded'>( + 'collapsed', + ) + const [resizeWidth, setResizeWidth] = useState(240) + + useEffect(() => { + setReady(true) + }, []) + + useEffect(() => { + window.dispatchEvent(new Event('resize')) + }, [resizeWidth]) + + const resizeStyle = useMemo( + () => ({ width: `${resizeWidth}px` }), + [resizeWidth], + ) + + return ( +
+

react-truncate e2e harness

+
{String(ready)}
+ +
+

Truncate

+
+ {LONG_TEXT} +
+
+ +
+

ShowMore

+
+
{showMoreState}
+
+ + setShowMoreState(expanded ? 'expanded' : 'collapsed') + } + > + {SHOW_MORE_TEXT} + +
+
+
+ +
+

MiddleTruncate

+
+ {FILE_NAME} +
+
+ +
+

Resize

+
+ + +
+
{resizeWidth}
+
+ {RESIZE_TEXT} +
+
+
+ ) +} diff --git a/e2e/app/main.tsx b/e2e/app/main.tsx new file mode 100644 index 0000000..6fbf0e0 --- /dev/null +++ b/e2e/app/main.tsx @@ -0,0 +1,9 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import { App } from './App' + +ReactDOM.createRoot(document.querySelector('#root')!).render( + + + , +) diff --git a/e2e/global-setup.mjs b/e2e/global-setup.mjs new file mode 100644 index 0000000..a90a2bf --- /dev/null +++ b/e2e/global-setup.mjs @@ -0,0 +1,70 @@ +import { execSync, spawn } from 'node:child_process' +import { readFileSync, rmSync, writeFileSync } from 'node:fs' +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +const PORT = 4173 +const PID_FILE = '/tmp/react-truncate-e2e-vite.pid' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) +const repoRoot = path.resolve(__dirname, '..') + +const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)) + +function stopExistingServer() { + try { + const pid = Number.parseInt(readFileSync(PID_FILE, 'utf8'), 10) + + if (!Number.isNaN(pid)) { + try { + process.kill(-pid, 'SIGTERM') + } catch { + process.kill(pid, 'SIGTERM') + } + } + } catch { + // No previous server to stop. + } + + rmSync(PID_FILE, { force: true }) +} + +export default async function globalSetup() { + stopExistingServer() + + const child = spawn( + 'pnpm', + [ + 'exec', + 'vite', + '--config', + 'e2e/vite.config.ts', + '--host', + '127.0.0.1', + '--port', + String(PORT), + ], + { + cwd: repoRoot, + detached: true, + stdio: 'ignore', + }, + ) + + child.unref() + writeFileSync(PID_FILE, String(child.pid)) + + for (let attempt = 0; attempt < 40; attempt += 1) { + try { + execSync(`curl -sf http://127.0.0.1:${PORT}/ > /dev/null`, { + cwd: repoRoot, + stdio: 'ignore', + }) + return + } catch { + await sleep(250) + } + } + + throw new Error(`Timed out waiting for the e2e Vite server on port ${PORT}`) +} diff --git a/e2e/global-teardown.mjs b/e2e/global-teardown.mjs new file mode 100644 index 0000000..80b6eed --- /dev/null +++ b/e2e/global-teardown.mjs @@ -0,0 +1,21 @@ +import { readFileSync, rmSync } from 'node:fs' + +const PID_FILE = '/tmp/react-truncate-e2e-vite.pid' + +export default function globalTeardown() { + try { + const pid = Number.parseInt(readFileSync(PID_FILE, 'utf8'), 10) + + if (!Number.isNaN(pid)) { + try { + process.kill(-pid, 'SIGTERM') + } catch { + process.kill(pid, 'SIGTERM') + } + } + } catch { + // Server already stopped or never started. + } + + rmSync(PID_FILE, { force: true }) +} diff --git a/e2e/index.html b/e2e/index.html new file mode 100644 index 0000000..e188bdc --- /dev/null +++ b/e2e/index.html @@ -0,0 +1,12 @@ + + + + + + react-truncate e2e + + +
+ + + diff --git a/e2e/tests/components.spec.ts b/e2e/tests/components.spec.ts new file mode 100644 index 0000000..f81d54b --- /dev/null +++ b/e2e/tests/components.spec.ts @@ -0,0 +1,68 @@ +import { expect, test } from '@playwright/test' + +test('renders truncate scenario in browser', async ({ page }) => { + await page.goto('/') + + await expect(page.getByTestId('e2e-ready')).toHaveText('true') + + const truncateExample = page.getByTestId('truncate-example') + await expect(truncateExample).toContainText('…') + + const text = await truncateExample.innerText() + + expect(text).toContain('…') + expect(text).not.toContain( + 'This sentence keeps going so the component must clamp it safely.', + ) +}) + +test('show-more expands and collapses', async ({ page }) => { + await page.goto('/') + + await expect(page.getByTestId('show-more-state')).toHaveText('collapsed') + const collapsedText = await page.getByTestId('show-more-example').innerText() + + await page.getByRole('link', { name: 'Expand' }).click() + + await expect(page.getByTestId('show-more-state')).toHaveText('expanded') + await expect(page.getByRole('link', { name: 'Collapse' })).toBeVisible() + + const expandedText = await page.getByTestId('show-more-example').innerText() + expect(expandedText.length).toBeGreaterThan(collapsedText.length) + + await page.getByRole('link', { name: 'Collapse' }).click() + await expect(page.getByTestId('show-more-state')).toHaveText('collapsed') +}) + +test('middle-truncate preserves suffix', async ({ page }) => { + await page.goto('/') + + const middleExample = page.getByTestId('middle-example') + await expect(middleExample).toContainText('.pdf') + + const text = await middleExample.innerText() + + expect(text).toContain('…') + expect(text.endsWith('.pdf')).toBeTruthy() +}) + +test('truncate recalculates after resize controls change width', async ({ + page, +}) => { + await page.goto('/') + + const resizeExample = page.getByTestId('resize-example') + await expect(resizeExample).toContainText('…') + + const wideText = await resizeExample.innerText() + + await page.getByRole('button', { name: 'Set narrow' }).click() + await expect(page.getByTestId('resize-width')).toHaveText('120') + + const narrowText = await resizeExample.innerText() + expect(narrowText).not.toBe(wideText) + expect(narrowText).toContain('…') + + await page.getByRole('button', { name: 'Set wide' }).click() + await expect(page.getByTestId('resize-width')).toHaveText('240') +}) diff --git a/e2e/vite.config.ts b/e2e/vite.config.ts new file mode 100644 index 0000000..2df0bc0 --- /dev/null +++ b/e2e/vite.config.ts @@ -0,0 +1,16 @@ +import path from 'node:path' +import { fileURLToPath } from 'node:url' +import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) + +export default defineConfig({ + plugins: [react()], + resolve: { + alias: { + '@': path.resolve(__dirname, '../src'), + }, + }, + root: './e2e', +}) diff --git a/package.json b/package.json index a1e3f54..db9e49e 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "license": "MIT", "homepage": "https://truncate.js.org", "type": "module", + "packageManager": "pnpm@10.15.1", "files": [ "dist" ], @@ -33,6 +34,11 @@ "gen:changelog": "pnpm exec changelog", "gen:release": "pnpm exec release", "test": "vitest", + "test:run": "vitest run", + "test:e2e": "playwright test", + "test:e2e:ui": "playwright test --ui", + "smoke:prepare": "node ./smoke/scripts/prepare-smoke-package.mjs", + "test:smoke": "playwright test --config smoke/playwright.config.ts", "coverage": "vitest run --coverage", "lint": "eslint src", "lint:inspector": "npx @eslint/config-inspector", @@ -67,6 +73,7 @@ "@bassist/tsconfig": "^0.1.1", "@commitlint/cli": "^19.8.1", "@commitlint/config-conventional": "^19.8.1", + "@playwright/test": "^1.54.2", "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.7.0", "@testing-library/react": "^16.3.0", @@ -75,6 +82,7 @@ "@types/react": "^19.1.10", "@types/react-dom": "^19.1.7", "@types/react-is": "^19.0.0", + "@vitejs/plugin-react": "^4.3.4", "@types/sinon": "^17.0.4", "@vitest/coverage-v8": "^3.2.4", "conventional-changelog-cli": "^4.1.0", @@ -92,6 +100,7 @@ "tsup": "^8.5.0", "tsx": "^4.20.4", "typescript": "^5.9.2", + "vite": "^5.4.14", "vitest": "^3.2.4" }, "lint-staged": { @@ -100,4 +109,4 @@ "eslint --fix" ] } -} \ No newline at end of file +} diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..ce2588b --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from '@playwright/test' + +export default defineConfig({ + testDir: './e2e/tests', + globalSetup: './e2e/global-setup.mjs', + globalTeardown: './e2e/global-teardown.mjs', + use: { + baseURL: 'http://127.0.0.1:4173', + trace: 'on-first-retry', + }, +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d55d0aa..92d3d47 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,6 +32,9 @@ importers: '@commitlint/config-conventional': specifier: ^19.8.1 version: 19.8.1 + '@playwright/test': + specifier: ^1.54.2 + version: 1.58.2 '@testing-library/dom': specifier: ^10.4.1 version: 10.4.1 @@ -59,6 +62,9 @@ importers: '@types/sinon': specifier: ^17.0.4 version: 17.0.4 + '@vitejs/plugin-react': + specifier: ^4.3.4 + version: 4.3.4(vite@5.4.14(@types/node@24.3.0)) '@vitest/coverage-v8': specifier: ^3.2.4 version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.3.0)(happy-dom@18.0.1)(jsdom@24.0.0)(msw@2.7.0(@types/node@24.3.0)(typescript@5.9.2))) @@ -107,6 +113,9 @@ importers: typescript: specifier: ^5.9.2 version: 5.9.2 + vite: + specifier: ^5.4.14 + version: 5.4.14(@types/node@24.3.0) vitest: specifier: ^3.2.4 version: 3.2.4(@types/debug@4.1.12)(@types/node@24.3.0)(happy-dom@18.0.1)(jsdom@24.0.0)(msw@2.7.0(@types/node@24.3.0)(typescript@5.9.2)) @@ -522,12 +531,6 @@ packages: '@emnapi/wasi-threads@1.0.3': resolution: {integrity: sha512-8K5IFFsQqF9wQNJptGbS6FNKgUTsSRYnTqNCG1vPP8jFdjSv18n2mQfJpkt2Oibo9iBEzcDnDxNwKTzC7svlJw==} - '@esbuild/aix-ppc64@0.21.4': - resolution: {integrity: sha512-Zrm+B33R4LWPLjDEVnEqt2+SLTATlru1q/xYKVn8oVTbiRBGmK2VIMoIYGJDGyftnGaC788IuzGFAlb7IQ0Y8A==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [aix] - '@esbuild/aix-ppc64@0.21.5': resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} engines: {node: '>=12'} @@ -540,12 +543,6 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.21.4': - resolution: {integrity: sha512-fYFnz+ObClJ3dNiITySBUx+oNalYUT18/AryMxfovLkYWbutXsct3Wz2ZWAcGGppp+RVVX5FiXeLYGi97umisA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - '@esbuild/android-arm64@0.21.5': resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} engines: {node: '>=12'} @@ -558,12 +555,6 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm@0.21.4': - resolution: {integrity: sha512-E7H/yTd8kGQfY4z9t3nRPk/hrhaCajfA3YSQSBrst8B+3uTcgsi8N+ZWYCaeIDsiVs6m65JPCaQN/DxBRclF3A==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - '@esbuild/android-arm@0.21.5': resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} engines: {node: '>=12'} @@ -576,12 +567,6 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-x64@0.21.4': - resolution: {integrity: sha512-mDqmlge3hFbEPbCWxp4fM6hqq7aZfLEHZAKGP9viq9wMUBVQx202aDIfc3l+d2cKhUJM741VrCXEzRFhPDKH3Q==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - '@esbuild/android-x64@0.21.5': resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} engines: {node: '>=12'} @@ -594,12 +579,6 @@ packages: cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.21.4': - resolution: {integrity: sha512-72eaIrDZDSiWqpmCzVaBD58c8ea8cw/U0fq/PPOTqE3c53D0xVMRt2ooIABZ6/wj99Y+h4ksT/+I+srCDLU9TA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - '@esbuild/darwin-arm64@0.21.5': resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} engines: {node: '>=12'} @@ -612,12 +591,6 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.21.4': - resolution: {integrity: sha512-uBsuwRMehGmw1JC7Vecu/upOjTsMhgahmDkWhGLWxIgUn2x/Y4tIwUZngsmVb6XyPSTXJYS4YiASKPcm9Zitag==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - '@esbuild/darwin-x64@0.21.5': resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} engines: {node: '>=12'} @@ -630,12 +603,6 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.21.4': - resolution: {integrity: sha512-8JfuSC6YMSAEIZIWNL3GtdUT5NhUA/CMUCpZdDRolUXNAXEE/Vbpe6qlGLpfThtY5NwXq8Hi4nJy4YfPh+TwAg==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - '@esbuild/freebsd-arm64@0.21.5': resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} engines: {node: '>=12'} @@ -648,12 +615,6 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.21.4': - resolution: {integrity: sha512-8d9y9eQhxv4ef7JmXny7591P/PYsDFc4+STaxC1GBv0tMyCdyWfXu2jBuqRsyhY8uL2HU8uPyscgE2KxCY9imQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - '@esbuild/freebsd-x64@0.21.5': resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} engines: {node: '>=12'} @@ -666,12 +627,6 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.21.4': - resolution: {integrity: sha512-/GLD2orjNU50v9PcxNpYZi+y8dJ7e7/LhQukN3S4jNDXCKkyyiyAz9zDw3siZ7Eh1tRcnCHAo/WcqKMzmi4eMQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - '@esbuild/linux-arm64@0.21.5': resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} engines: {node: '>=12'} @@ -684,12 +639,6 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.21.4': - resolution: {integrity: sha512-2rqFFefpYmpMs+FWjkzSgXg5vViocqpq5a1PSRgT0AvSgxoXmGF17qfGAzKedg6wAwyM7UltrKVo9kxaJLMF/g==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - '@esbuild/linux-arm@0.21.5': resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} engines: {node: '>=12'} @@ -702,12 +651,6 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.21.4': - resolution: {integrity: sha512-pNftBl7m/tFG3t2m/tSjuYeWIffzwAZT9m08+9DPLizxVOsUl8DdFzn9HvJrTQwe3wvJnwTdl92AonY36w/25g==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - '@esbuild/linux-ia32@0.21.5': resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} engines: {node: '>=12'} @@ -720,12 +663,6 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.21.4': - resolution: {integrity: sha512-cSD2gzCK5LuVX+hszzXQzlWya6c7hilO71L9h4KHwqI4qeqZ57bAtkgcC2YioXjsbfAv4lPn3qe3b00Zt+jIfQ==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - '@esbuild/linux-loong64@0.21.5': resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} engines: {node: '>=12'} @@ -738,12 +675,6 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.21.4': - resolution: {integrity: sha512-qtzAd3BJh7UdbiXCrg6npWLYU0YpufsV9XlufKhMhYMJGJCdfX/G6+PNd0+v877X1JG5VmjBLUiFB0o8EUSicA==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - '@esbuild/linux-mips64el@0.21.5': resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} engines: {node: '>=12'} @@ -756,12 +687,6 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.21.4': - resolution: {integrity: sha512-yB8AYzOTaL0D5+2a4xEy7OVvbcypvDR05MsB/VVPVA7nL4hc5w5Dyd/ddnayStDgJE59fAgNEOdLhBxjfx5+dg==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - '@esbuild/linux-ppc64@0.21.5': resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} engines: {node: '>=12'} @@ -774,12 +699,6 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.21.4': - resolution: {integrity: sha512-Y5AgOuVzPjQdgU59ramLoqSSiXddu7F3F+LI5hYy/d1UHN7K5oLzYBDZe23QmQJ9PIVUXwOdKJ/jZahPdxzm9w==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - '@esbuild/linux-riscv64@0.21.5': resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} engines: {node: '>=12'} @@ -792,12 +711,6 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.21.4': - resolution: {integrity: sha512-Iqc/l/FFwtt8FoTK9riYv9zQNms7B8u+vAI/rxKuN10HgQIXaPzKZc479lZ0x6+vKVQbu55GdpYpeNWzjOhgbA==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - '@esbuild/linux-s390x@0.21.5': resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} engines: {node: '>=12'} @@ -810,12 +723,6 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.21.4': - resolution: {integrity: sha512-Td9jv782UMAFsuLZINfUpoF5mZIbAj+jv1YVtE58rFtfvoKRiKSkRGQfHTgKamLVT/fO7203bHa3wU122V/Bdg==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - '@esbuild/linux-x64@0.21.5': resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} engines: {node: '>=12'} @@ -834,12 +741,6 @@ packages: cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.21.4': - resolution: {integrity: sha512-Awn38oSXxsPMQxaV0Ipb7W/gxZtk5Tx3+W+rAPdZkyEhQ6968r9NvtkjhnhbEgWXYbgV+JEONJ6PcdBS+nlcpA==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - '@esbuild/netbsd-x64@0.21.5': resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} engines: {node: '>=12'} @@ -858,12 +759,6 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.21.4': - resolution: {integrity: sha512-IsUmQeCY0aU374R82fxIPu6vkOybWIMc3hVGZ3ChRwL9hA1TwY+tS0lgFWV5+F1+1ssuvvXt3HFqe8roCip8Hg==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - '@esbuild/openbsd-x64@0.21.5': resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} engines: {node: '>=12'} @@ -882,12 +777,6 @@ packages: cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.21.4': - resolution: {integrity: sha512-hsKhgZ4teLUaDA6FG/QIu2q0rI6I36tZVfM4DBZv3BG0mkMIdEnMbhc4xwLvLJSS22uWmaVkFkqWgIS0gPIm+A==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - '@esbuild/sunos-x64@0.21.5': resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} engines: {node: '>=12'} @@ -900,12 +789,6 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.21.4': - resolution: {integrity: sha512-UUfMgMoXPoA/bvGUNfUBFLCh0gt9dxZYIx9W4rfJr7+hKe5jxxHmfOK8YSH4qsHLLN4Ck8JZ+v7Q5fIm1huErg==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - '@esbuild/win32-arm64@0.21.5': resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} engines: {node: '>=12'} @@ -918,12 +801,6 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.21.4': - resolution: {integrity: sha512-yIxbspZb5kGCAHWm8dexALQ9en1IYDfErzjSEq1KzXFniHv019VT3mNtTK7t8qdy4TwT6QYHI9sEZabONHg+aw==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - '@esbuild/win32-ia32@0.21.5': resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} engines: {node: '>=12'} @@ -936,12 +813,6 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.21.4': - resolution: {integrity: sha512-sywLRD3UK/qRJt0oBwdpYLBibk7KiRfbswmWRDabuncQYSlf8aLEEUor/oP6KRz8KEG+HoiVLBhPRD5JWjS8Sg==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - '@esbuild/win32-x64@0.21.5': resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} engines: {node: '>=12'} @@ -1288,6 +1159,11 @@ packages: resolution: {integrity: sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + '@playwright/test@1.58.2': + resolution: {integrity: sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==} + engines: {node: '>=18'} + hasBin: true + '@radix-ui/primitive@1.1.3': resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} @@ -1544,131 +1420,66 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.32.1': - resolution: {integrity: sha512-/pqA4DmqyCm8u5YIDzIdlLcEmuvxb0v8fZdFhVMszSpDTgbQKdw3/mB3eMUHIbubtJ6F9j+LtmyCnHTEqIHyzA==} - cpu: [arm] - os: [android] - '@rollup/rollup-android-arm-eabi@4.44.2': resolution: {integrity: sha512-g0dF8P1e2QYPOj1gu7s/3LVP6kze9A7m6x0BZ9iTdXK8N5c2V7cpBKHV3/9A4Zd8xxavdhK0t4PnqjkqVmUc9Q==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.32.1': - resolution: {integrity: sha512-If3PDskT77q7zgqVqYuj7WG3WC08G1kwXGVFi9Jr8nY6eHucREHkfpX79c0ACAjLj3QIWKPJR7w4i+f5EdLH5Q==} - cpu: [arm64] - os: [android] - '@rollup/rollup-android-arm64@4.44.2': resolution: {integrity: sha512-Yt5MKrOosSbSaAK5Y4J+vSiID57sOvpBNBR6K7xAaQvk3MkcNVV0f9fE20T+41WYN8hDn6SGFlFrKudtx4EoxA==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.32.1': - resolution: {integrity: sha512-zCpKHioQ9KgZToFp5Wvz6zaWbMzYQ2LJHQ+QixDKq52KKrF65ueu6Af4hLlLWHjX1Wf/0G5kSJM9PySW9IrvHA==} - cpu: [arm64] - os: [darwin] - '@rollup/rollup-darwin-arm64@4.44.2': resolution: {integrity: sha512-EsnFot9ZieM35YNA26nhbLTJBHD0jTwWpPwmRVDzjylQT6gkar+zenfb8mHxWpRrbn+WytRRjE0WKsfaxBkVUA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.32.1': - resolution: {integrity: sha512-sFvF+t2+TyUo/ZQqUcifrJIgznx58oFZbdHS9TvHq3xhPVL9nOp+yZ6LKrO9GWTP+6DbFtoyLDbjTpR62Mbr3Q==} - cpu: [x64] - os: [darwin] - '@rollup/rollup-darwin-x64@4.44.2': resolution: {integrity: sha512-dv/t1t1RkCvJdWWxQ2lWOO+b7cMsVw5YFaS04oHpZRWehI1h0fV1gF4wgGCTyQHHjJDfbNpwOi6PXEafRBBezw==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.32.1': - resolution: {integrity: sha512-NbOa+7InvMWRcY9RG+B6kKIMD/FsnQPH0MWUvDlQB1iXnF/UcKSudCXZtv4lW+C276g3w5AxPbfry5rSYvyeYA==} - cpu: [arm64] - os: [freebsd] - '@rollup/rollup-freebsd-arm64@4.44.2': resolution: {integrity: sha512-W4tt4BLorKND4qeHElxDoim0+BsprFTwb+vriVQnFFtT/P6v/xO5I99xvYnVzKWrK6j7Hb0yp3x7V5LUbaeOMg==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.32.1': - resolution: {integrity: sha512-JRBRmwvHPXR881j2xjry8HZ86wIPK2CcDw0EXchE1UgU0ubWp9nvlT7cZYKc6bkypBt745b4bglf3+xJ7hXWWw==} - cpu: [x64] - os: [freebsd] - '@rollup/rollup-freebsd-x64@4.44.2': resolution: {integrity: sha512-tdT1PHopokkuBVyHjvYehnIe20fxibxFCEhQP/96MDSOcyjM/shlTkZZLOufV3qO6/FQOSiJTBebhVc12JyPTA==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.32.1': - resolution: {integrity: sha512-PKvszb+9o/vVdUzCCjL0sKHukEQV39tD3fepXxYrHE3sTKrRdCydI7uldRLbjLmDA3TFDmh418XH19NOsDRH8g==} - cpu: [arm] - os: [linux] - '@rollup/rollup-linux-arm-gnueabihf@4.44.2': resolution: {integrity: sha512-+xmiDGGaSfIIOXMzkhJ++Oa0Gwvl9oXUeIiwarsdRXSe27HUIvjbSIpPxvnNsRebsNdUo7uAiQVgBD1hVriwSQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.32.1': - resolution: {integrity: sha512-9WHEMV6Y89eL606ReYowXuGF1Yb2vwfKWKdD1A5h+OYnPZSJvxbEjxTRKPgi7tkP2DSnW0YLab1ooy+i/FQp/Q==} - cpu: [arm] - os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.44.2': resolution: {integrity: sha512-bDHvhzOfORk3wt8yxIra8N4k/N0MnKInCW5OGZaeDYa/hMrdPaJzo7CSkjKZqX4JFUWjUGm88lI6QJLCM7lDrA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.32.1': - resolution: {integrity: sha512-tZWc9iEt5fGJ1CL2LRPw8OttkCBDs+D8D3oEM8mH8S1ICZCtFJhD7DZ3XMGM8kpqHvhGUTvNUYVDnmkj4BDXnw==} - cpu: [arm64] - os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.44.2': resolution: {integrity: sha512-NMsDEsDiYghTbeZWEGnNi4F0hSbGnsuOG+VnNvxkKg0IGDvFh7UVpM/14mnMwxRxUf9AdAVJgHPvKXf6FpMB7A==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.32.1': - resolution: {integrity: sha512-FTYc2YoTWUsBz5GTTgGkRYYJ5NGJIi/rCY4oK/I8aKowx1ToXeoVVbIE4LGAjsauvlhjfl0MYacxClLld1VrOw==} - cpu: [arm64] - os: [linux] - '@rollup/rollup-linux-arm64-musl@4.44.2': resolution: {integrity: sha512-lb5bxXnxXglVq+7imxykIp5xMq+idehfl+wOgiiix0191av84OqbjUED+PRC5OA8eFJYj5xAGcpAZ0pF2MnW+A==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.32.1': - resolution: {integrity: sha512-F51qLdOtpS6P1zJVRzYM0v6MrBNypyPEN1GfMiz0gPu9jN8ScGaEFIZQwteSsGKg799oR5EaP7+B2jHgL+d+Kw==} - cpu: [loong64] - os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.44.2': resolution: {integrity: sha512-Yl5Rdpf9pIc4GW1PmkUGHdMtbx0fBLE1//SxDmuf3X0dUC57+zMepow2LK0V21661cjXdTn8hO2tXDdAWAqE5g==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.32.1': - resolution: {integrity: sha512-wO0WkfSppfX4YFm5KhdCCpnpGbtgQNj/tgvYzrVYFKDpven8w2N6Gg5nB6w+wAMO3AIfSTWeTjfVe+uZ23zAlg==} - cpu: [ppc64] - os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.44.2': resolution: {integrity: sha512-03vUDH+w55s680YYryyr78jsO1RWU9ocRMaeV2vMniJJW/6HhoTBwyyiiTPVHNWLnhsnwcQ0oH3S9JSBEKuyqw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.32.1': - resolution: {integrity: sha512-iWswS9cIXfJO1MFYtI/4jjlrGb/V58oMu4dYJIKnR5UIwbkzR0PJ09O0PDZT0oJ3LYWXBSWahNf/Mjo6i1E5/g==} - cpu: [riscv64] - os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.44.2': resolution: {integrity: sha512-iYtAqBg5eEMG4dEfVlkqo05xMOk6y/JXIToRca2bAWuqjrJYJlx/I7+Z+4hSrsWU8GdJDFPL4ktV3dy4yBSrzg==} cpu: [riscv64] @@ -1679,61 +1490,31 @@ packages: cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.32.1': - resolution: {integrity: sha512-RKt8NI9tebzmEthMnfVgG3i/XeECkMPS+ibVZjZ6mNekpbbUmkNWuIN2yHsb/mBPyZke4nlI4YqIdFPgKuoyQQ==} - cpu: [s390x] - os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.44.2': resolution: {integrity: sha512-evFOtkmVdY3udE+0QKrV5wBx7bKI0iHz5yEVx5WqDJkxp9YQefy4Mpx3RajIVcM6o7jxTvVd/qpC1IXUhGc1Mw==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.32.1': - resolution: {integrity: sha512-WQFLZ9c42ECqEjwg/GHHsouij3pzLXkFdz0UxHa/0OM12LzvX7DzedlY0SIEly2v18YZLRhCRoHZDxbBSWoGYg==} - cpu: [x64] - os: [linux] - '@rollup/rollup-linux-x64-gnu@4.44.2': resolution: {integrity: sha512-/bXb0bEsWMyEkIsUL2Yt5nFB5naLAwyOWMEviQfQY1x3l5WsLKgvZf66TM7UTfED6erckUVUJQ/jJ1FSpm3pRQ==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.32.1': - resolution: {integrity: sha512-BLoiyHDOWoS3uccNSADMza6V6vCNiphi94tQlVIL5de+r6r/CCQuNnerf+1g2mnk2b6edp5dk0nhdZ7aEjOBsA==} - cpu: [x64] - os: [linux] - '@rollup/rollup-linux-x64-musl@4.44.2': resolution: {integrity: sha512-3D3OB1vSSBXmkGEZR27uiMRNiwN08/RVAcBKwhUYPaiZ8bcvdeEwWPvbnXvvXHY+A/7xluzcN+kaiOFNiOZwWg==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.32.1': - resolution: {integrity: sha512-w2l3UnlgYTNNU+Z6wOR8YdaioqfEnwPjIsJ66KxKAf0p+AuL2FHeTX6qvM+p/Ue3XPBVNyVSfCrfZiQh7vZHLQ==} - cpu: [arm64] - os: [win32] - '@rollup/rollup-win32-arm64-msvc@4.44.2': resolution: {integrity: sha512-VfU0fsMK+rwdK8mwODqYeM2hDrF2WiHaSmCBrS7gColkQft95/8tphyzv2EupVxn3iE0FI78wzffoULH1G+dkw==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.32.1': - resolution: {integrity: sha512-Am9H+TGLomPGkBnaPWie4F3x+yQ2rr4Bk2jpwy+iV+Gel9jLAu/KqT8k3X4jxFPW6Zf8OMnehyutsd+eHoq1WQ==} - cpu: [ia32] - os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.44.2': resolution: {integrity: sha512-+qMUrkbUurpE6DVRjiJCNGZBGo9xM4Y0FXU5cjgudWqIBWbcLkjE3XprJUsOFgC6xjBClwVa9k6O3A7K3vxb5Q==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.32.1': - resolution: {integrity: sha512-ar80GhdZb4DgmW3myIS9nRFYcpJRSME8iqWgzH2i44u+IdrzmiXVxeFnExQ5v4JYUSpg94bWjevMG8JHf1Da5Q==} - cpu: [x64] - os: [win32] - '@rollup/rollup-win32-x64-msvc@4.44.2': resolution: {integrity: sha512-3+QZROYfJ25PDcxFF66UEk8jGWigHJeecZILvkPkyQN7oc5BvFo4YEXFkOs154j3FTMp9mn9Ky8RCOwastduEA==} cpu: [x64] @@ -2911,11 +2692,6 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} - esbuild@0.21.4: - resolution: {integrity: sha512-sFMcNNrj+Q0ZDolrp5pDhH0nRPN9hLIM3fRPwgbLYJeSHHgnXSnbV3xYgSVuOeLWH9c73VwmEverVzupIv5xuA==} - engines: {node: '>=12'} - hasBin: true - esbuild@0.21.5: resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} engines: {node: '>=12'} @@ -3248,6 +3024,11 @@ packages: resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==} engines: {node: '>=14.14'} + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -4700,6 +4481,16 @@ packages: pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + playwright-core@1.58.2: + resolution: {integrity: sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.58.2: + resolution: {integrity: sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==} + engines: {node: '>=18'} + hasBin: true + pluralize@8.0.0: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} @@ -5113,11 +4904,6 @@ packages: rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rollup@4.32.1: - resolution: {integrity: sha512-z+aeEsOeEa3mEbS1Tjl6sAZ8NE3+AalQz1RJGj81M+fizusbdDMoEJwdJNHfaB40Scr4qNu+welOfes7maKonA==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - rollup@4.44.2: resolution: {integrity: sha512-PVoapzTwSEcelaWGth3uR66u7ZRo6qhPHc0f2uRO9fX6XDVNrIiGYS0Pj9+R8yIIYSD/mCx2b16Ws9itljKSPg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -5974,6 +5760,7 @@ packages: whatwg-encoding@3.1.1: resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} engines: {node: '>=18'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation whatwg-mimetype@3.0.0: resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} @@ -6705,153 +6492,102 @@ snapshots: tslib: 2.8.1 optional: true - '@esbuild/aix-ppc64@0.21.4': - optional: true - '@esbuild/aix-ppc64@0.21.5': optional: true '@esbuild/aix-ppc64@0.25.6': optional: true - '@esbuild/android-arm64@0.21.4': - optional: true - '@esbuild/android-arm64@0.21.5': optional: true '@esbuild/android-arm64@0.25.6': optional: true - '@esbuild/android-arm@0.21.4': - optional: true - '@esbuild/android-arm@0.21.5': optional: true '@esbuild/android-arm@0.25.6': optional: true - '@esbuild/android-x64@0.21.4': - optional: true - '@esbuild/android-x64@0.21.5': optional: true '@esbuild/android-x64@0.25.6': optional: true - '@esbuild/darwin-arm64@0.21.4': - optional: true - '@esbuild/darwin-arm64@0.21.5': optional: true '@esbuild/darwin-arm64@0.25.6': optional: true - '@esbuild/darwin-x64@0.21.4': - optional: true - '@esbuild/darwin-x64@0.21.5': optional: true '@esbuild/darwin-x64@0.25.6': optional: true - '@esbuild/freebsd-arm64@0.21.4': - optional: true - '@esbuild/freebsd-arm64@0.21.5': optional: true '@esbuild/freebsd-arm64@0.25.6': optional: true - '@esbuild/freebsd-x64@0.21.4': - optional: true - '@esbuild/freebsd-x64@0.21.5': optional: true '@esbuild/freebsd-x64@0.25.6': optional: true - '@esbuild/linux-arm64@0.21.4': - optional: true - '@esbuild/linux-arm64@0.21.5': optional: true '@esbuild/linux-arm64@0.25.6': optional: true - '@esbuild/linux-arm@0.21.4': - optional: true - '@esbuild/linux-arm@0.21.5': optional: true '@esbuild/linux-arm@0.25.6': optional: true - '@esbuild/linux-ia32@0.21.4': - optional: true - '@esbuild/linux-ia32@0.21.5': optional: true '@esbuild/linux-ia32@0.25.6': optional: true - '@esbuild/linux-loong64@0.21.4': - optional: true - '@esbuild/linux-loong64@0.21.5': optional: true '@esbuild/linux-loong64@0.25.6': optional: true - '@esbuild/linux-mips64el@0.21.4': - optional: true - '@esbuild/linux-mips64el@0.21.5': optional: true '@esbuild/linux-mips64el@0.25.6': optional: true - '@esbuild/linux-ppc64@0.21.4': - optional: true - '@esbuild/linux-ppc64@0.21.5': optional: true '@esbuild/linux-ppc64@0.25.6': optional: true - '@esbuild/linux-riscv64@0.21.4': - optional: true - '@esbuild/linux-riscv64@0.21.5': optional: true '@esbuild/linux-riscv64@0.25.6': optional: true - '@esbuild/linux-s390x@0.21.4': - optional: true - '@esbuild/linux-s390x@0.21.5': optional: true '@esbuild/linux-s390x@0.25.6': optional: true - '@esbuild/linux-x64@0.21.4': - optional: true - '@esbuild/linux-x64@0.21.5': optional: true @@ -6861,9 +6597,6 @@ snapshots: '@esbuild/netbsd-arm64@0.25.6': optional: true - '@esbuild/netbsd-x64@0.21.4': - optional: true - '@esbuild/netbsd-x64@0.21.5': optional: true @@ -6873,9 +6606,6 @@ snapshots: '@esbuild/openbsd-arm64@0.25.6': optional: true - '@esbuild/openbsd-x64@0.21.4': - optional: true - '@esbuild/openbsd-x64@0.21.5': optional: true @@ -6885,36 +6615,24 @@ snapshots: '@esbuild/openharmony-arm64@0.25.6': optional: true - '@esbuild/sunos-x64@0.21.4': - optional: true - '@esbuild/sunos-x64@0.21.5': optional: true '@esbuild/sunos-x64@0.25.6': optional: true - '@esbuild/win32-arm64@0.21.4': - optional: true - '@esbuild/win32-arm64@0.21.5': optional: true '@esbuild/win32-arm64@0.25.6': optional: true - '@esbuild/win32-ia32@0.21.4': - optional: true - '@esbuild/win32-ia32@0.21.5': optional: true '@esbuild/win32-ia32@0.25.6': optional: true - '@esbuild/win32-x64@0.21.4': - optional: true - '@esbuild/win32-x64@0.21.5': optional: true @@ -7285,6 +7003,10 @@ snapshots: '@pkgr/core@0.2.7': {} + '@playwright/test@1.58.2': + dependencies: + playwright: 1.58.2 + '@radix-ui/primitive@1.1.3': {} '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': @@ -7510,120 +7232,63 @@ snapshots: optionalDependencies: rollup: 4.44.2 - '@rollup/rollup-android-arm-eabi@4.32.1': - optional: true - '@rollup/rollup-android-arm-eabi@4.44.2': optional: true - '@rollup/rollup-android-arm64@4.32.1': - optional: true - '@rollup/rollup-android-arm64@4.44.2': optional: true - '@rollup/rollup-darwin-arm64@4.32.1': - optional: true - '@rollup/rollup-darwin-arm64@4.44.2': optional: true - '@rollup/rollup-darwin-x64@4.32.1': - optional: true - '@rollup/rollup-darwin-x64@4.44.2': optional: true - '@rollup/rollup-freebsd-arm64@4.32.1': - optional: true - '@rollup/rollup-freebsd-arm64@4.44.2': optional: true - '@rollup/rollup-freebsd-x64@4.32.1': - optional: true - '@rollup/rollup-freebsd-x64@4.44.2': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.32.1': - optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.44.2': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.32.1': - optional: true - '@rollup/rollup-linux-arm-musleabihf@4.44.2': optional: true - '@rollup/rollup-linux-arm64-gnu@4.32.1': - optional: true - '@rollup/rollup-linux-arm64-gnu@4.44.2': optional: true - '@rollup/rollup-linux-arm64-musl@4.32.1': - optional: true - '@rollup/rollup-linux-arm64-musl@4.44.2': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.32.1': - optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.44.2': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.32.1': - optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.44.2': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.32.1': - optional: true - '@rollup/rollup-linux-riscv64-gnu@4.44.2': optional: true '@rollup/rollup-linux-riscv64-musl@4.44.2': optional: true - '@rollup/rollup-linux-s390x-gnu@4.32.1': - optional: true - '@rollup/rollup-linux-s390x-gnu@4.44.2': optional: true - '@rollup/rollup-linux-x64-gnu@4.32.1': - optional: true - '@rollup/rollup-linux-x64-gnu@4.44.2': optional: true - '@rollup/rollup-linux-x64-musl@4.32.1': - optional: true - '@rollup/rollup-linux-x64-musl@4.44.2': optional: true - '@rollup/rollup-win32-arm64-msvc@4.32.1': - optional: true - '@rollup/rollup-win32-arm64-msvc@4.44.2': optional: true - '@rollup/rollup-win32-ia32-msvc@4.32.1': - optional: true - '@rollup/rollup-win32-ia32-msvc@4.44.2': optional: true - '@rollup/rollup-win32-x64-msvc@4.32.1': - optional: true - '@rollup/rollup-win32-x64-msvc@4.44.2': optional: true @@ -9012,32 +8677,6 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 - esbuild@0.21.4: - optionalDependencies: - '@esbuild/aix-ppc64': 0.21.4 - '@esbuild/android-arm': 0.21.4 - '@esbuild/android-arm64': 0.21.4 - '@esbuild/android-x64': 0.21.4 - '@esbuild/darwin-arm64': 0.21.4 - '@esbuild/darwin-x64': 0.21.4 - '@esbuild/freebsd-arm64': 0.21.4 - '@esbuild/freebsd-x64': 0.21.4 - '@esbuild/linux-arm': 0.21.4 - '@esbuild/linux-arm64': 0.21.4 - '@esbuild/linux-ia32': 0.21.4 - '@esbuild/linux-loong64': 0.21.4 - '@esbuild/linux-mips64el': 0.21.4 - '@esbuild/linux-ppc64': 0.21.4 - '@esbuild/linux-riscv64': 0.21.4 - '@esbuild/linux-s390x': 0.21.4 - '@esbuild/linux-x64': 0.21.4 - '@esbuild/netbsd-x64': 0.21.4 - '@esbuild/openbsd-x64': 0.21.4 - '@esbuild/sunos-x64': 0.21.4 - '@esbuild/win32-arm64': 0.21.4 - '@esbuild/win32-ia32': 0.21.4 - '@esbuild/win32-x64': 0.21.4 - esbuild@0.21.5: optionalDependencies: '@esbuild/aix-ppc64': 0.21.5 @@ -9504,6 +9143,9 @@ snapshots: jsonfile: 6.1.0 universalify: 2.0.1 + fsevents@2.3.2: + optional: true + fsevents@2.3.3: optional: true @@ -11619,6 +11261,14 @@ snapshots: mlly: 1.7.4 pathe: 2.0.2 + playwright-core@1.58.2: {} + + playwright@1.58.2: + dependencies: + playwright-core: 1.58.2 + optionalDependencies: + fsevents: 2.3.2 + pluralize@8.0.0: {} possible-typed-array-names@1.1.0: {} @@ -12164,31 +11814,6 @@ snapshots: rfdc@1.4.1: {} - rollup@4.32.1: - dependencies: - '@types/estree': 1.0.6 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.32.1 - '@rollup/rollup-android-arm64': 4.32.1 - '@rollup/rollup-darwin-arm64': 4.32.1 - '@rollup/rollup-darwin-x64': 4.32.1 - '@rollup/rollup-freebsd-arm64': 4.32.1 - '@rollup/rollup-freebsd-x64': 4.32.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.32.1 - '@rollup/rollup-linux-arm-musleabihf': 4.32.1 - '@rollup/rollup-linux-arm64-gnu': 4.32.1 - '@rollup/rollup-linux-arm64-musl': 4.32.1 - '@rollup/rollup-linux-loongarch64-gnu': 4.32.1 - '@rollup/rollup-linux-powerpc64le-gnu': 4.32.1 - '@rollup/rollup-linux-riscv64-gnu': 4.32.1 - '@rollup/rollup-linux-s390x-gnu': 4.32.1 - '@rollup/rollup-linux-x64-gnu': 4.32.1 - '@rollup/rollup-linux-x64-musl': 4.32.1 - '@rollup/rollup-win32-arm64-msvc': 4.32.1 - '@rollup/rollup-win32-ia32-msvc': 4.32.1 - '@rollup/rollup-win32-x64-msvc': 4.32.1 - fsevents: 2.3.3 - rollup@4.44.2: dependencies: '@types/estree': 1.0.8 @@ -13068,9 +12693,9 @@ snapshots: vite@5.4.14(@types/node@24.3.0): dependencies: - esbuild: 0.21.4 + esbuild: 0.21.5 postcss: 8.5.1 - rollup: 4.32.1 + rollup: 4.44.2 optionalDependencies: '@types/node': 24.3.0 fsevents: 2.3.3 diff --git a/smoke/consumer/index.html b/smoke/consumer/index.html new file mode 100644 index 0000000..e6ffc96 --- /dev/null +++ b/smoke/consumer/index.html @@ -0,0 +1,12 @@ + + + + + + react-truncate smoke + + +
+ + + diff --git a/smoke/consumer/package.json b/smoke/consumer/package.json new file mode 100644 index 0000000..d836292 --- /dev/null +++ b/smoke/consumer/package.json @@ -0,0 +1,20 @@ +{ + "name": "react-truncate-smoke-consumer", + "private": true, + "type": "module", + "scripts": { + "dev": "vite" + }, + "dependencies": { + "@re-dev/react-truncate": "/Users/chengpeiquan/Documents/projects/remanufacturing/react-truncate/smoke/.artifacts/re-dev-react-truncate-0.5.2.tgz", + "react": "^19.1.1", + "react-dom": "^19.1.1" + }, + "devDependencies": { + "@types/react": "^19.1.10", + "@types/react-dom": "^19.1.7", + "@vitejs/plugin-react": "^4.3.4", + "typescript": "^5.9.2", + "vite": "^5.4.14" + } +} diff --git a/smoke/consumer/src/App.tsx b/smoke/consumer/src/App.tsx new file mode 100644 index 0000000..90f0e49 --- /dev/null +++ b/smoke/consumer/src/App.tsx @@ -0,0 +1,67 @@ +import { MiddleTruncate, ShowMore, Truncate } from '@re-dev/react-truncate' +import React, { useEffect, useState } from 'react' + +const TRUNCATE_TEXT = + 'The packed package should still truncate text correctly when consumed by another app.' +const SHOW_MORE_TEXT = + 'The packed package should also keep interactive expand and collapse behavior in a consumer project.' +const MIDDLE_TEXT = 'customer-contract-archive-reference-2026.pdf' + +const imported = Boolean(Truncate && ShowMore && MiddleTruncate) + +export const App: React.FC = () => { + const [ready, setReady] = useState(false) + const [showMoreState, setShowMoreState] = useState<'collapsed' | 'expanded'>( + 'collapsed', + ) + + useEffect(() => { + setReady(true) + }, []) + + return ( +
+

react-truncate smoke consumer

+
{String(ready)}
+
{String(imported)}
+ +
+ {TRUNCATE_TEXT} +
+ +
+
{showMoreState}
+ + setShowMoreState(expanded ? 'expanded' : 'collapsed') + } + > + {SHOW_MORE_TEXT} + +
+ +
+ {MIDDLE_TEXT} +
+
+ ) +} diff --git a/smoke/consumer/src/main.tsx b/smoke/consumer/src/main.tsx new file mode 100644 index 0000000..6fbf0e0 --- /dev/null +++ b/smoke/consumer/src/main.tsx @@ -0,0 +1,9 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import { App } from './App' + +ReactDOM.createRoot(document.querySelector('#root')!).render( + + + , +) diff --git a/smoke/consumer/tsconfig.json b/smoke/consumer/tsconfig.json new file mode 100644 index 0000000..cbeea53 --- /dev/null +++ b/smoke/consumer/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "Bundler", + "allowImportingTsExtensions": false, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "types": ["vite/client"] + }, + "include": ["src"] +} diff --git a/smoke/consumer/vite.config.ts b/smoke/consumer/vite.config.ts new file mode 100644 index 0000000..ae74518 --- /dev/null +++ b/smoke/consumer/vite.config.ts @@ -0,0 +1,6 @@ +import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite' + +export default defineConfig({ + plugins: [react()], +}) diff --git a/smoke/global-setup.mjs b/smoke/global-setup.mjs new file mode 100644 index 0000000..c304e61 --- /dev/null +++ b/smoke/global-setup.mjs @@ -0,0 +1,75 @@ +import { execSync, spawn } from 'node:child_process' +import { readFileSync, rmSync, writeFileSync } from 'node:fs' +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +const PORT = 4174 +const PID_FILE = '/tmp/react-truncate-smoke-vite.pid' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) +const smokeRoot = path.resolve(__dirname) +const repoRoot = path.resolve(smokeRoot, '..') +const consumerRoot = path.resolve(smokeRoot, 'consumer') + +const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)) + +function stopExistingServer() { + try { + const pid = Number.parseInt(readFileSync(PID_FILE, 'utf8'), 10) + + if (!Number.isNaN(pid)) { + try { + process.kill(-pid, 'SIGTERM') + } catch { + process.kill(pid, 'SIGTERM') + } + } + } catch { + // No previous server to stop. + } + + rmSync(PID_FILE, { force: true }) +} + +export default async function globalSetup() { + stopExistingServer() + + execSync('pnpm smoke:prepare', { + cwd: repoRoot, + stdio: 'inherit', + }) + + const child = spawn( + 'pnpm', + [ + '--ignore-workspace', + 'dev', + '--host', + '127.0.0.1', + '--port', + String(PORT), + ], + { + cwd: consumerRoot, + detached: true, + stdio: 'ignore', + }, + ) + + child.unref() + writeFileSync(PID_FILE, String(child.pid)) + + for (let attempt = 0; attempt < 40; attempt += 1) { + try { + execSync(`curl -sf http://127.0.0.1:${PORT}/ > /dev/null`, { + cwd: repoRoot, + stdio: 'ignore', + }) + return + } catch { + await sleep(250) + } + } + + throw new Error(`Timed out waiting for the smoke Vite server on port ${PORT}`) +} diff --git a/smoke/global-teardown.mjs b/smoke/global-teardown.mjs new file mode 100644 index 0000000..0712a69 --- /dev/null +++ b/smoke/global-teardown.mjs @@ -0,0 +1,21 @@ +import { readFileSync, rmSync } from 'node:fs' + +const PID_FILE = '/tmp/react-truncate-smoke-vite.pid' + +export default function globalTeardown() { + try { + const pid = Number.parseInt(readFileSync(PID_FILE, 'utf8'), 10) + + if (!Number.isNaN(pid)) { + try { + process.kill(-pid, 'SIGTERM') + } catch { + process.kill(pid, 'SIGTERM') + } + } + } catch { + // Server already stopped or never started. + } + + rmSync(PID_FILE, { force: true }) +} diff --git a/smoke/playwright.config.ts b/smoke/playwright.config.ts new file mode 100644 index 0000000..47006f6 --- /dev/null +++ b/smoke/playwright.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from '@playwright/test' + +export default defineConfig({ + testDir: './tests', + globalSetup: './global-setup.mjs', + globalTeardown: './global-teardown.mjs', + use: { + baseURL: 'http://127.0.0.1:4174', + trace: 'on-first-retry', + }, +}) diff --git a/smoke/scripts/prepare-smoke-package.mjs b/smoke/scripts/prepare-smoke-package.mjs new file mode 100644 index 0000000..87e618f --- /dev/null +++ b/smoke/scripts/prepare-smoke-package.mjs @@ -0,0 +1,82 @@ +import { execSync } from 'node:child_process' +import { + existsSync, + mkdirSync, + readFileSync, + readdirSync, + renameSync, + rmSync, + writeFileSync, +} from 'node:fs' +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) +const smokeRoot = path.resolve(__dirname, '..') +const repoRoot = path.resolve(smokeRoot, '..') +const consumerRoot = path.resolve(smokeRoot, 'consumer') +const artifactsRoot = path.resolve(smokeRoot, '.artifacts') +const consumerPackageJsonPath = path.resolve(consumerRoot, 'package.json') +const packageName = '@re-dev/react-truncate' + +mkdirSync(artifactsRoot, { recursive: true }) + +execSync('pnpm build:lib', { + cwd: repoRoot, + stdio: 'inherit', +}) + +const before = new Set( + readdirSync(repoRoot).filter((name) => name.endsWith('.tgz')), +) + +execSync('pnpm pack', { + cwd: repoRoot, + stdio: 'inherit', +}) + +const after = readdirSync(repoRoot).filter((name) => name.endsWith('.tgz')) +const tarballName = after.find((name) => !before.has(name)) + +if (!tarballName) { + throw new Error('Failed to locate the generated package tarball') +} + +const sourceTarball = path.resolve(repoRoot, tarballName) +const targetTarball = path.resolve(artifactsRoot, tarballName) + +rmSync(targetTarball, { force: true }) +renameSync(sourceTarball, targetTarball) + +const installedPackagePath = path.resolve( + consumerRoot, + 'node_modules/@re-dev/react-truncate', +) +const originalConsumerPackageJson = readFileSync( + consumerPackageJsonPath, + 'utf8', +) +const consumerPackage = JSON.parse(originalConsumerPackageJson) + +if (existsSync(installedPackagePath)) { + rmSync(installedPackagePath, { recursive: true, force: true }) +} + +consumerPackage.dependencies = { + ...consumerPackage.dependencies, + [packageName]: targetTarball, +} + +writeFileSync( + consumerPackageJsonPath, + `${JSON.stringify(consumerPackage, null, 2)}\n`, +) + +try { + execSync('pnpm install --force --no-lockfile --ignore-workspace', { + cwd: consumerRoot, + stdio: 'inherit', + }) +} finally { + writeFileSync(consumerPackageJsonPath, originalConsumerPackageJson) +} diff --git a/smoke/tests/package-smoke.spec.ts b/smoke/tests/package-smoke.spec.ts new file mode 100644 index 0000000..d85ed45 --- /dev/null +++ b/smoke/tests/package-smoke.spec.ts @@ -0,0 +1,22 @@ +import { expect, test } from '@playwright/test' + +test('renders packed package consumer page', async ({ page }) => { + await page.goto('/') + + await expect(page.getByTestId('smoke-page-ready')).toHaveText('true') + await expect(page.getByTestId('smoke-package-imported')).toHaveText('true') + await expect(page.getByTestId('smoke-truncate-example')).toContainText('…') +}) + +test('packed package show-more interaction works', async ({ page }) => { + await page.goto('/') + + await expect(page.getByTestId('smoke-show-more-state')).toHaveText( + 'collapsed', + ) + + await page.getByRole('link', { name: 'Expand' }).click() + + await expect(page.getByTestId('smoke-show-more-state')).toHaveText('expanded') + await expect(page.getByRole('link', { name: 'Collapse' })).toBeVisible() +}) diff --git a/vitest.config.ts b/vitest.config.ts index 0a45be8..97b151d 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,9 +1,10 @@ -import { defineConfig } from 'vitest/config' import { resolve } from 'node:path' +import { configDefaults, defineConfig } from 'vitest/config' export default defineConfig({ test: { environment: 'happy-dom', + exclude: [...configDefaults.exclude, 'e2e/**', 'smoke/**'], globals: true, setupFiles: ['./test/config/vitest-setup.ts'], coverage: {