From b566e51dcac3c0168cc6cd396ebbe5bb89528f9a Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Sat, 3 Oct 2026 12:33:12 +0900 Subject: [PATCH] test: build and drive a React 18 app in CI Refs #691 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .github/workflows/publish.yml | 55 +++++++++++++++++++++++++ apps/react-18/e2e/react-18.spec.ts | 24 +++++++++++ apps/react-18/index.html | 12 ++++++ apps/react-18/package.json | 28 +++++++++++++ apps/react-18/playwright.config.ts | 18 +++++++++ apps/react-18/src/App.tsx | 64 ++++++++++++++++++++++++++++++ apps/react-18/src/main.tsx | 10 +++++ apps/react-18/tsconfig.json | 23 +++++++++++ apps/react-18/vite.config.ts | 16 ++++++++ bun.lock | 31 +++++++++++++++ 10 files changed, 281 insertions(+) create mode 100644 apps/react-18/e2e/react-18.spec.ts create mode 100644 apps/react-18/index.html create mode 100644 apps/react-18/package.json create mode 100644 apps/react-18/playwright.config.ts create mode 100644 apps/react-18/src/App.tsx create mode 100644 apps/react-18/src/main.tsx create mode 100644 apps/react-18/tsconfig.json create mode 100644 apps/react-18/vite.config.ts diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index cd3b62a9f..ed8f50e5a 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -138,6 +138,61 @@ jobs: path: playwright-report/ retention-days: 1 + # The packages take React ^18 || ^19 as a peer. This builds a React 18.3 app + # with the Vite plugin and the UI kit, then checks refs, context and events + # in Chromium, so React-19-only APIs cannot slip back in. + react-18: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v7 + + - uses: actions-rust-lang/setup-rust-toolchain@v2 + + - name: Cache cargo registry + target + uses: actions/cache@v6 + with: + path: | + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: cargo-react-18-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }} + - uses: oven-sh/setup-bun@v2 + with: + bun-version: "1.4.0" + name: Install bun + + - name: Cache bun dependencies + uses: actions/cache@v6 + with: + path: ~/.bun/install/cache + key: bun-react-18-${{ runner.os }}-${{ hashFiles('**/bun.lock') }} + - uses: jetli/wasm-pack-action@v0.4.0 + with: + version: "latest" + - name: Install Node.js + uses: actions/setup-node@v7 + with: + node-version: 24 + - run: bun install + - run: bun run build + - name: Type-check and build the React 18 app + working-directory: apps/react-18 + run: | + bun ../../node_modules/@typescript/native/bin/tsc + bun run build + - name: Cache Playwright Browsers + uses: actions/cache@v6 + with: + path: ~/.cache/ms-playwright + key: playwright-react-18-${{ runner.os }}-${{ hashFiles('**/bun.lock') }} + - name: Install Playwright Browsers + run: bunx playwright install chromium --with-deps + - name: Run React 18 E2E Tests + working-directory: apps/react-18 + run: bun run test:e2e + publish: runs-on: ubuntu-latest permissions: diff --git a/apps/react-18/e2e/react-18.spec.ts b/apps/react-18/e2e/react-18.spec.ts new file mode 100644 index 000000000..3ff8e655d --- /dev/null +++ b/apps/react-18/e2e/react-18.spec.ts @@ -0,0 +1,24 @@ +import { expect, test } from '@playwright/test' + +test('Devup UI runs on React 18', async ({ page }) => { + const errors: string[] = [] + page.on('pageerror', (error) => errors.push(error.message)) + page.on('console', (message) => { + if (message.type() === 'error') errors.push(message.text()) + }) + await page.goto('/') + + await expect(page.getByTestId('version')).toHaveText(/^18\./) + await expect(page.getByTestId('refs')).toHaveText( + 'button,input,input,input,textarea,input', + ) + + await page.getByText('select', { exact: true }).click() + await page.getByText('second', { exact: true }).click() + await expect(page.getByTestId('selected')).toHaveText('second') + + await page.getByTestId('increase').click() + await expect(page.getByTestId('stepper')).toHaveValue('1') + + expect(errors).toEqual([]) +}) diff --git a/apps/react-18/index.html b/apps/react-18/index.html new file mode 100644 index 000000000..9dfc14d76 --- /dev/null +++ b/apps/react-18/index.html @@ -0,0 +1,12 @@ + + + + + + Devup UI on React 18 + + +
+ + + diff --git a/apps/react-18/package.json b/apps/react-18/package.json new file mode 100644 index 000000000..6044cca14 --- /dev/null +++ b/apps/react-18/package.json @@ -0,0 +1,28 @@ +{ + "name": "react-18-example", + "version": "0.1.0", + "type": "module", + "private": true, + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview --port 3098 --strictPort", + "test:e2e": "bunx playwright test -c playwright.config.ts", + "lint": "bun ../../node_modules/@typescript/native/bin/tsc && eslint" + }, + "dependencies": { + "@devup-ui/components": "workspace:^", + "@devup-ui/react": "workspace:^", + "react": "18.3.1", + "react-dom": "18.3.1" + }, + "devDependencies": { + "@devup-ui/vite-plugin": "workspace:^", + "@playwright/test": "^1.63.0", + "@types/react": "^18.3", + "@types/react-dom": "^18.3", + "@vitejs/plugin-react": "^6.1", + "typescript": "^7.0.2", + "vite": "^8.3" + } +} diff --git a/apps/react-18/playwright.config.ts b/apps/react-18/playwright.config.ts new file mode 100644 index 000000000..36905cf4c --- /dev/null +++ b/apps/react-18/playwright.config.ts @@ -0,0 +1,18 @@ +import { defineConfig, devices } from '@playwright/test' + +export default defineConfig({ + testDir: './e2e', + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + reporter: 'list', + use: { + baseURL: 'http://localhost:3098', + }, + projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }], + webServer: { + command: 'bun run preview', + url: 'http://localhost:3098', + reuseExistingServer: !process.env.CI, + timeout: 120_000, + }, +}) diff --git a/apps/react-18/src/App.tsx b/apps/react-18/src/App.tsx new file mode 100644 index 000000000..49ccf0502 --- /dev/null +++ b/apps/react-18/src/App.tsx @@ -0,0 +1,64 @@ +import { + Button, + Checkbox, + Input, + Radio, + Select, + SelectContainer, + SelectOption, + SelectTrigger, + Stepper, + StepperDecreaseButton, + StepperIncreaseButton, + StepperInput, + Textarea, +} from '@devup-ui/components' +import { Box, styled } from '@devup-ui/react' +import { useEffect, useRef, useState, version } from 'react' + +const Field = styled('input', { color: 'red', p: 1 }) + +export function App() { + const buttonRef = useRef(null) + const checkboxRef = useRef(null) + const inputRef = useRef(null) + const radioRef = useRef(null) + const textareaRef = useRef(null) + const fieldRef = useRef(null) + const [refs, setRefs] = useState('') + const [selected, setSelected] = useState('') + + useEffect(() => { + setRefs( + [buttonRef, checkboxRef, inputRef, radioRef, textareaRef, fieldRef] + .map((ref) => ref.current?.tagName.toLowerCase() ?? 'null') + .join(','), + ) + }, []) + + return ( + + {version} + + checkbox + + radio +