diff --git a/.gitignore b/.gitignore
index 162e443..3ab6773 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
web/dist/
web/node_modules/
+web/test-results/
diff --git a/web/package-lock.json b/web/package-lock.json
index ee46446..d33a312 100644
--- a/web/package-lock.json
+++ b/web/package-lock.json
@@ -22,6 +22,7 @@
"react-router-dom": "7.18.2"
},
"devDependencies": {
+ "@playwright/test": "1.62.1",
"@testing-library/dom": "10.4.1",
"@testing-library/react": "16.3.2",
"@vitejs/plugin-react": "6.1.0",
@@ -768,6 +769,22 @@
"url": "https://github.com/sponsors/Boshen"
}
},
+ "node_modules/@playwright/test": {
+ "version": "1.62.1",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.62.1.tgz",
+ "integrity": "sha512-DTcUc8qii+cpHvtOwggMtBRMjKZHXYWdw8syRYu2vtzuq4Wxphqq4NfCs5Zt44L6mA8rfDfj+PHnxFc/FeK6mQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "playwright": "1.62.1"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
"node_modules/@popperjs/core": {
"version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
@@ -2712,6 +2729,53 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
+ "node_modules/playwright": {
+ "version": "1.62.1",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.62.1.tgz",
+ "integrity": "sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "playwright-core": "1.62.1"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
+ "node_modules/playwright-core": {
+ "version": "1.62.1",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.62.1.tgz",
+ "integrity": "sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "playwright-core": "cli.js"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/playwright/node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
"node_modules/postcss": {
"version": "8.5.26",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz",
diff --git a/web/package.json b/web/package.json
index dd0d8e7..5fb3096 100644
--- a/web/package.json
+++ b/web/package.json
@@ -6,7 +6,8 @@
"scripts": {
"build": "vite build",
"dev": "vite",
- "test": "vitest run"
+ "test": "vitest run",
+ "test:responsive": "playwright test"
},
"dependencies": {
"@emotion/react": "11.14.0",
@@ -23,6 +24,7 @@
"react-router-dom": "7.18.2"
},
"devDependencies": {
+ "@playwright/test": "1.62.1",
"@testing-library/dom": "10.4.1",
"@testing-library/react": "16.3.2",
"@vitejs/plugin-react": "6.1.0",
diff --git a/web/playwright.config.js b/web/playwright.config.js
new file mode 100644
index 0000000..bc455fa
--- /dev/null
+++ b/web/playwright.config.js
@@ -0,0 +1,12 @@
+import { defineConfig } from '@playwright/test'
+
+export default defineConfig({
+ testDir: './tests',
+ testMatch: '*.e2e.js',
+ use: { baseURL: 'http://127.0.0.1:4173', channel: 'chrome' },
+ webServer: {
+ command: 'npm run dev -- --host 127.0.0.1 --port 4173',
+ url: 'http://127.0.0.1:4173',
+ reuseExistingServer: true,
+ },
+})
diff --git a/web/src/BrowserList.jsx b/web/src/BrowserList.jsx
index 0000dbf..0ef4f2a 100644
--- a/web/src/BrowserList.jsx
+++ b/web/src/BrowserList.jsx
@@ -116,7 +116,7 @@ export function BrowserList() {
<>
运行环境启动、停止并回收隔离的浏览器 Profile
-
+
环境名称 *
setName(event.target.value)} placeholder="例如:account-a" />
@@ -125,7 +125,7 @@ export function BrowserList() {
Fingerprint Seed *
setSeed(event.target.value)} />
-
+
{message ? { setLocalError(''); refetch() }}>重试} sx={{ mb: 2.5 }}>{message} : null}
diff --git a/web/src/layout.jsx b/web/src/layout.jsx
index 786e7fc..8ba70ff 100644
--- a/web/src/layout.jsx
+++ b/web/src/layout.jsx
@@ -66,7 +66,7 @@ export function CreatorHubLayout({ children }) {
{desktop ? setCollapsed(value => !value)} sx={{ position: 'absolute', right: 16, bottom: 24, color: '#a9b7cc', border: '1px solid #35506f' }}>{collapsed ? : } : null}
-
+
{children}
diff --git a/web/tests/responsive.e2e.js b/web/tests/responsive.e2e.js
new file mode 100644
index 0000000..4035319
--- /dev/null
+++ b/web/tests/responsive.e2e.js
@@ -0,0 +1,15 @@
+import { expect, test } from '@playwright/test'
+
+test('keeps the create form inside a 900px viewport', async ({ page }) => {
+ await page.route('**/api/browsers', route => route.fulfill({ json: [] }))
+ await page.setViewportSize({ width: 900, height: 800 })
+ await page.goto('/')
+
+ await expect(page.getByRole('button', { name: '收起导航' }).first()).toBeVisible()
+ const createButton = page.getByRole('button', { name: '创建环境' })
+ await expect(createButton).toBeVisible()
+ expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBeLessThanOrEqual(900)
+ const box = await createButton.boundingBox()
+ expect(box).not.toBeNull()
+ expect(box.x + box.width).toBeLessThanOrEqual(900)
+})