diff --git a/bin/imports.js b/bin/imports.js
index bc792f2692a..c6896080870 100644
--- a/bin/imports.js
+++ b/bin/imports.js
@@ -22,7 +22,15 @@ const devDependencies = new Set([
'@parcel/macros',
'@adobe/spectrum-tokens',
'playwright',
- 'axe-playwright'
+ 'axe-playwright',
+ 'vitest',
+ '@vitejs/plugin-react',
+ '@vitest/browser',
+ '@vitest/browser-playwright',
+ '@vitest/ui',
+ 'unplugin-parcel-macros',
+ 'vite',
+ 'vite-plugin-svgr'
]);
module.exports = {
diff --git a/package.json b/package.json
index bcf6427197d..41ddbb4358c 100644
--- a/package.json
+++ b/package.json
@@ -36,6 +36,7 @@
"start:mcp": "yarn workspace @react-spectrum/s2-docs generate:md && yarn build:mcp && node packages/dev/mcp/s2/dist/index.js && node packages/dev/mcp/react-aria/dist/index.js",
"test:mcp": "yarn build:s2-docs && yarn build:mcp && node packages/dev/mcp/scripts/smoke-list-pages.mjs",
"test": "cross-env STRICT_MODE=1 VIRT_ON=1 yarn jest",
+ "test:s2": "yarn workspace @react-spectrum/s2 test",
"test:lint": "node packages/**/*.test-lint.js",
"test-loose": "cross-env VIRT_ON=1 yarn jest",
"test-storybook": "test-storybook --url http://localhost:9003 --browsers chromium --no-cache",
@@ -138,6 +139,8 @@
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@typescript/native-preview": "^7.0.0-dev.20251223.1",
+ "@vitest/browser-playwright": "^4.0.17",
+ "@vitest/browser-preview": "^4.0.17",
"@yarnpkg/types": "^4.0.0",
"autoprefixer": "^9.6.0",
"axe-playwright": "^1.1.11",
@@ -204,6 +207,9 @@
"typescript": "^5.8.2",
"typescript-eslint": "^8.38.0",
"verdaccio": "^6.0.0",
+ "vite-plugin-svgr": "^4.5.0",
+ "vitest": "^4.0.17",
+ "vitest-browser-react": "^2.0.2",
"walk-object": "^4.0.0",
"xml": "^1.0.1"
},
diff --git a/packages/@react-spectrum/s2/package.json b/packages/@react-spectrum/s2/package.json
index 544fb05905a..3c026e4beaf 100644
--- a/packages/@react-spectrum/s2/package.json
+++ b/packages/@react-spectrum/s2/package.json
@@ -137,16 +137,32 @@
"scripts": {
"prepublishOnly": "rm -rf dist/page.cjs* && mv dist/page*.css page.css && mv dist/page.css.map page.css.map || true",
"prepack": "npm pkg delete scripts devDependencies resolutions alias targets",
- "postpack": "git checkout -- package.json"
+ "postpack": "git checkout -- package.json",
+ "test": "vitest run",
+ "test:watch": "vitest",
+ "test:ui": "vitest --ui",
+ "test:coverage": "vitest run --coverage"
},
"devDependencies": {
"@adobe/spectrum-tokens": "^14.0.0",
"@react-aria/test-utils": "^1.0.0-alpha.8",
"@storybook/jest": "^0.2.3",
"@testing-library/dom": "^10.1.0",
+ "@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.0.0",
"@testing-library/user-event": "^14.0.0",
- "jest": "^29.5.0"
+ "@vitejs/plugin-react": "^4.3.4",
+ "@vitest/browser": "^4.0.0",
+ "@vitest/browser-playwright": "^4.0.0",
+ "@vitest/ui": "^4.0.0",
+ "glob": "^10.3.0",
+ "jsdom": "^25.0.0",
+ "playwright": "^1.45.3",
+ "unplugin-parcel-macros": "^0.1.2-alpha.1",
+ "vite": "^6.0.0",
+ "vite-plugin-svgr": "^4.3.0",
+ "vitest": "^4.0.0",
+ "vitest-browser-react": "^2.0.2"
},
"dependencies": {
"@internationalized/date": "^3.10.1",
diff --git a/packages/@react-spectrum/s2/test/Accordion.test.tsx b/packages/@react-spectrum/s2/test/Accordion.test.tsx
new file mode 100644
index 00000000000..bc75dd51a4f
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/Accordion.test.tsx
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {
+ Accordion,
+ AccordionItem,
+ AccordionItemPanel,
+ AccordionItemTitle
+} from '../src';
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+import {style} from '../style' with {type: 'macro'};
+
+describe('Accordion', () => {
+ it('renders', async () => {
+ const {container} = await render(
+
+
+ Personal Information
+ Personal information form here.
+
+
+ Billing Address
+ Billing address form here.
+
+
+ );
+ const buttons = container.querySelectorAll('button');
+ expect(buttons.length).toBe(2);
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/ActionBar.test.tsx b/packages/@react-spectrum/s2/test/ActionBar.test.tsx
new file mode 100644
index 00000000000..08c2c320d35
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/ActionBar.test.tsx
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {
+ ActionBar,
+ ActionButton,
+ Cell,
+ Column,
+ Row,
+ TableBody,
+ TableHeader,
+ TableView,
+ Text
+} from '../src';
+import Copy from '@react-spectrum/s2/icons/Copy';
+import Delete from '@react-spectrum/s2/icons/Delete';
+import {describe, expect, it} from 'vitest';
+import Edit from '@react-spectrum/s2/icons/Edit';
+import React from 'react';
+import {render} from './utils/render';
+import {style} from '../style' with {type: 'macro'};
+
+describe('ActionBar', () => {
+ it('renders', async () => {
+ let rows = [
+ {id: 1, name: 'Charizard', type: 'Fire, Flying', level: '67'},
+ {id: 2, name: 'Blastoise', type: 'Water', level: '56'},
+ {id: 3, name: 'Venusaur', type: 'Grass, Poison', level: '83'},
+ {id: 4, name: 'Pikachu', type: 'Electric', level: '100'}
+ ];
+
+ const screen = await render(
+ (
+
+ alert('Edit action')}>
+
+ Edit
+
+ alert('Copy action')}>
+
+ Copy
+
+ alert('Delete action')}>
+
+ Delete
+
+
+ )}>
+
+ Name
+ Type
+ Level
+
+
+ {item => (
+
+ | {item.name} |
+ {item.type} |
+ {item.level} |
+
+ )}
+
+
+ );
+ expect(screen.getByText('Edit')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/ActionButton.test.tsx b/packages/@react-spectrum/s2/test/ActionButton.test.tsx
new file mode 100644
index 00000000000..aec4cf2a4cb
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/ActionButton.test.tsx
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {ActionButton} from '../src';
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('ActionButton', () => {
+ it('renders', async () => {
+ const screen = await render(Action );
+ await expect.element(screen.getByRole('button')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/ActionButtonGroup.test.tsx b/packages/@react-spectrum/s2/test/ActionButtonGroup.test.tsx
index 9565351a525..d1d2b17ddb9 100644
--- a/packages/@react-spectrum/s2/test/ActionButtonGroup.test.tsx
+++ b/packages/@react-spectrum/s2/test/ActionButtonGroup.test.tsx
@@ -11,55 +11,31 @@
*/
import {ActionButton, ActionButtonGroup, Text} from '../src';
-import {render} from '@react-spectrum/test-utils-internal';
+import Copy from '@react-spectrum/s2/icons/Copy';
+import Cut from '@react-spectrum/s2/icons/Cut';
+import {describe, expect, it} from 'vitest';
+import Paste from '@react-spectrum/s2/icons/Paste';
+import {render} from './utils/render';
describe('ActionButtonGroup', () => {
-
- it('can disable all buttons from the group', async () => {
- let {getAllByRole} = render(
-
- Bold
- Italic
- Underline
-
- );
-
-
- let buttons = getAllByRole('button');
- expect(buttons[0]).toBeDisabled();
- expect(buttons[1]).toBeDisabled();
- expect(buttons[2]).toBeDisabled();
- });
-
- it('can set disable individually', async () => {
- let {getAllByRole} = render(
+ it('renders', async () => {
+ const screen = await render(
- Bold
- Italic
- Underline
+
+
+ Cut
+
+
+
+ Copy
+
+
+
+ Paste
+
);
-
-
- let buttons = getAllByRole('button');
- expect(buttons[0]).toBeDisabled();
- expect(buttons[1]).not.toBeDisabled();
- expect(buttons[2]).not.toBeDisabled();
- });
-
- it('can override the group disable', async () => {
- let {getAllByRole} = render(
-
- Bold
- Italic
- Underline
-
- );
-
-
- let buttons = getAllByRole('button');
- expect(buttons[0]).not.toBeDisabled();
- expect(buttons[1]).toBeDisabled();
- expect(buttons[2]).toBeDisabled();
+ const buttons = screen.container.querySelectorAll('button');
+ expect(buttons.length).toBe(3);
});
});
diff --git a/packages/@react-spectrum/s2/test/ActionMenu.test.tsx b/packages/@react-spectrum/s2/test/ActionMenu.test.tsx
new file mode 100644
index 00000000000..5611333661e
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/ActionMenu.test.tsx
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {ActionMenu, Keyboard, MenuItem, Text} from '../src';
+import Copy from '@react-spectrum/s2/icons/Copy';
+import Cut from '@react-spectrum/s2/icons/Cut';
+import {describe, expect, it} from 'vitest';
+import Paste from '@react-spectrum/s2/icons/Paste';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('ActionMenu', () => {
+ it('renders', async () => {
+ const screen = await render(
+
+ alert('copy')}>
+
+ Copy
+ Copy the selected text
+ ⌘C
+
+ alert('cut')}>
+
+ Cut
+ Cut the selected text
+ ⌘X
+
+ alert('paste')}>
+
+ Paste
+ Paste the copied text
+ ⌘V
+
+
+ );
+ expect(screen.getByRole('button')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/AlertDialog.test.tsx b/packages/@react-spectrum/s2/test/AlertDialog.test.tsx
new file mode 100644
index 00000000000..c167c45af30
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/AlertDialog.test.tsx
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {ActionButton, AlertDialog, DialogTrigger} from '../src';
+import {describe, expect, it, vi} from 'vitest';
+import React from 'react';
+import {render} from './utils';
+
+describe('AlertDialog', () => {
+ it('renders', async () => {
+ vi.useFakeTimers();
+ const screen = await render(
+
+ Open
+
+ Are you sure you want to delete this item?
+
+
+ );
+ vi.runAllTimers();
+ expect(screen.getByRole('alertdialog')).toBeInTheDocument();
+ vi.useRealTimers();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/Avatar.test.tsx b/packages/@react-spectrum/s2/test/Avatar.test.tsx
new file mode 100644
index 00000000000..f23a46e38f7
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/Avatar.test.tsx
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {Avatar} from '../src';
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+
+const avatarSrc = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAIAAADTED8xAAADMElEQVR4nOzVwQnAIBQFQYXff81RUkQCOyDj1YOPnbXWPmeTRef+/3O/OyBjzh3CD95BfqICMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMO0TAAD//2Anhf4QtqobAAAAAElFTkSuQmCC';
+
+describe('Avatar', () => {
+ it('renders', async () => {
+ const screen = await render( );
+ expect(screen.getByAltText('Avatar')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/AvatarGroup.test.tsx b/packages/@react-spectrum/s2/test/AvatarGroup.test.tsx
new file mode 100644
index 00000000000..c66527c099c
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/AvatarGroup.test.tsx
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {Avatar, AvatarGroup} from '../src';
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('AvatarGroup', () => {
+ it('renders', async () => {
+ const screen = await render(
+
+
+
+
+
+
+ );
+ expect(screen.getByRole('group')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/Badge.test.tsx b/packages/@react-spectrum/s2/test/Badge.test.tsx
new file mode 100644
index 00000000000..407cdd1039c
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/Badge.test.tsx
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {Badge} from '../src';
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('Badge', () => {
+ it('renders', async () => {
+ const screen = await render(Licensed );
+ expect(screen.container.firstChild).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/Breadcrumbs.test.tsx b/packages/@react-spectrum/s2/test/Breadcrumbs.test.tsx
new file mode 100644
index 00000000000..5e65f7de8e2
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/Breadcrumbs.test.tsx
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {Breadcrumb, Breadcrumbs} from '../src';
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('Breadcrumbs', () => {
+ it('renders', async () => {
+ const screen = await render(
+
+ Home
+ React Spectrum
+ Breadcrumbs
+
+ );
+ expect(screen.getByRole('list')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/Button.test.tsx b/packages/@react-spectrum/s2/test/Button.test.tsx
new file mode 100644
index 00000000000..5663096c030
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/Button.test.tsx
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {Button} from '../src';
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('Button', () => {
+ it('renders', async () => {
+ const screen = await render(Save );
+ expect(screen.getByRole('button')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/ButtonGroup.test.tsx b/packages/@react-spectrum/s2/test/ButtonGroup.test.tsx
new file mode 100644
index 00000000000..5f4bb76506a
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/ButtonGroup.test.tsx
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {Button, ButtonGroup} from '../src';
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('ButtonGroup', () => {
+ it('renders', async () => {
+ const screen = await render(
+
+ Rate Now
+ No, thanks
+ Remind me later
+
+ );
+ const buttons = screen.container.querySelectorAll('button');
+ expect(buttons.length).toBe(3);
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/Calendar.test.tsx b/packages/@react-spectrum/s2/test/Calendar.test.tsx
new file mode 100644
index 00000000000..68702cc8857
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/Calendar.test.tsx
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {Calendar} from '../src';
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('Calendar', () => {
+ it('renders', async () => {
+ const screen = await render(
+
+ );
+ expect(screen.getByRole('grid')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/Card.test.tsx b/packages/@react-spectrum/s2/test/Card.test.tsx
new file mode 100644
index 00000000000..cf724bff21d
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/Card.test.tsx
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {
+ ActionMenu,
+ Card,
+ CardPreview,
+ Content,
+ Footer,
+ Image,
+ MenuItem,
+ StatusLight,
+ Text
+} from '../src';
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('Card', () => {
+ it('renders', async () => {
+ const screen = await render(
+
+
+
+
+
+ Card title
+
+ Edit
+ Share
+ Delete
+
+ Card description. Give a concise overview of the context or functionality that's mentioned in the card title.
+
+
+
+ );
+ expect(screen.container.firstChild).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/CardView.test.tsx b/packages/@react-spectrum/s2/test/CardView.test.tsx
new file mode 100644
index 00000000000..411ee266ef6
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/CardView.test.tsx
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {
+ AssetCard,
+ CardPreview,
+ CardView,
+ Content,
+ Image,
+ Text
+} from '../src';
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('CardView', () => {
+ it('renders', async () => {
+ const screen = await render(
+
+
+
+
+
+
+ Desert Sunset
+ PNG • 2/3/2024
+
+
+
+
+
+
+
+ Hiking Trail
+ JPEG • 1/10/2022
+
+
+
+
+
+
+
+ Lion
+ JPEG • 8/28/2021
+
+
+
+
+
+
+
+ Mountain Sunrise
+ PNG • 3/15/2015
+
+
+
+
+
+
+
+ Giraffe tongue
+ PNG • 11/27/2019
+
+
+
+
+
+
+
+ Golden Hour
+ WEBP • 7/24/2024
+
+
+
+
+
+
+
+ Architecture
+ PNG • 12/24/2016
+
+
+
+
+
+
+
+ Peeking leopard
+ JPEG • 3/2/2016
+
+
+
+
+
+
+
+ Roofs
+ JPEG • 4/24/2025
+
+
+
+
+
+
+
+ Half Dome Deer
+ DNG • 8/28/2018
+
+
+
+ );
+ expect(screen.container.firstChild).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/Checkbox.test.tsx b/packages/@react-spectrum/s2/test/Checkbox.test.tsx
new file mode 100644
index 00000000000..06ef22e271a
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/Checkbox.test.tsx
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {Checkbox} from '../src';
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('Checkbox', () => {
+ it('renders', async () => {
+ const screen = await render(Unsubscribe );
+ expect(screen.getByRole('checkbox')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/CheckboxGroup.test.tsx b/packages/@react-spectrum/s2/test/CheckboxGroup.test.tsx
index 8081ede4888..2c9e5d1fd3a 100644
--- a/packages/@react-spectrum/s2/test/CheckboxGroup.test.tsx
+++ b/packages/@react-spectrum/s2/test/CheckboxGroup.test.tsx
@@ -10,88 +10,20 @@
* governing permissions and limitations under the License.
*/
-import {act, pointerMap, render, User} from '@react-spectrum/test-utils-internal';
-import {Checkbox, CheckboxGroup, Form, Provider} from '../src';
+import {Checkbox, CheckboxGroup} from '../src';
+import {describe, expect, it} from 'vitest';
import React from 'react';
-import userEvent from '@testing-library/user-event';
+import {render} from './utils/render';
describe('CheckboxGroup', () => {
- let testUtilUser = new User();
- let user;
- beforeAll(() => {
- user = userEvent.setup({delay: null, pointerMap});
- });
-
- it('should not require all checkboxes to be checked when Form has isRequired', async () => {
- let {getByRole, getAllByRole, getByTestId} = render(
-
- );
-
-
- let group = getByRole('group');
- let checkbox = getAllByRole('checkbox')[0];
-
- await user.click(checkbox);
- act(() => {(getByTestId('form') as HTMLFormElement).checkValidity();});
- expect(group).not.toHaveAttribute('aria-describedby');
- expect(group).not.toHaveAttribute('data-invalid');
-
- await user.click(checkbox);
- act(() => {(getByTestId('form') as HTMLFormElement).checkValidity();});
- expect(group).toHaveAttribute('data-invalid');
- expect(group).toHaveAttribute('aria-describedby');
- let errorMsg = document.getElementById(group.getAttribute('aria-describedby')!);
- expect(errorMsg).toHaveTextContent('Constraints not satisfied');
- });
-
- it.each`
- Name | props
- ${'ltr + vertical'} | ${{locale: 'de-DE', orientation: 'vertical'}}
- ${'rtl + verfical'} | ${{locale: 'ar-AE', orientation: 'vertical'}}
- ${'ltr + horizontal'} | ${{locale: 'de-DE', orientation: 'horizontal'}}
- ${'rtl + horizontal'} | ${{locale: 'ar-AE', orientation: 'horizontal'}}
- `('$Name should select the correct checkbox regardless of orientation and disabled checkboxes', async function ({props}) {
- let {getByRole} = render(
-
-
- Soccer
- Baseball
- Basketball
- Tennis
- Rugby
-
-
+ it('renders', async () => {
+ const screen = await render(
+
+ Soccer
+ Baseball
+ Basketball
+
);
-
- let checkboxGroupTester = testUtilUser.createTester('CheckboxGroup', {root: getByRole('group')});
- expect(checkboxGroupTester.checkboxgroup).toHaveAttribute('role');
- let checkboxes = checkboxGroupTester.checkboxes;
- await checkboxGroupTester.toggleCheckbox({checkbox: checkboxes[0]});
- expect(checkboxes[0]).toBeChecked();
- expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(1);
-
- await checkboxGroupTester.toggleCheckbox({checkbox: 4, interactionType: 'keyboard'});
- expect(checkboxes[4]).toBeChecked();
- expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(2);
-
- let checkbox4 = checkboxGroupTester.findCheckbox({checkboxIndexOrText: 3});
- await checkboxGroupTester.toggleCheckbox({checkbox: checkbox4, interactionType: 'keyboard'});
- expect(checkboxes[3]).toBeChecked();
- expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(3);
-
- await checkboxGroupTester.toggleCheckbox({checkbox: 'Soccer', interactionType: 'keyboard'});
- expect(checkboxes[0]).not.toBeChecked();
- expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(2);
-
- let checkbox5 = checkboxGroupTester.findCheckbox({checkboxIndexOrText: 'Rugby'});
- await checkboxGroupTester.toggleCheckbox({checkbox: checkbox5, interactionType: 'mouse'});
- expect(checkboxes[4]).not.toBeChecked();
- expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(1);
+ expect(screen.getByRole('group')).toBeInTheDocument();
});
});
diff --git a/packages/@react-spectrum/s2/test/CloseButton.test.tsx b/packages/@react-spectrum/s2/test/CloseButton.test.tsx
new file mode 100644
index 00000000000..0a3b8a5bf51
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/CloseButton.test.tsx
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {CloseButton} from '../src';
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('CloseButton', () => {
+ it('renders', async () => {
+ const screen = await render( );
+ await expect.element(screen.getByRole('button')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/CoachMark.test.tsx b/packages/@react-spectrum/s2/test/CoachMark.test.tsx
index 6814ac9d717..01500041376 100644
--- a/packages/@react-spectrum/s2/test/CoachMark.test.tsx
+++ b/packages/@react-spectrum/s2/test/CoachMark.test.tsx
@@ -10,69 +10,29 @@
* governing permissions and limitations under the License.
*/
-import {act, pointerMap, render} from '@react-spectrum/test-utils-internal';
-import {
- ActionMenu,
- Button,
- CardPreview,
- Checkbox,
- Content,
- Footer,
- Image,
- Keyboard,
- MenuItem,
- Text
-} from '../src';
+import {Checkbox, Content, Text} from '../src';
import {CoachMark, CoachMarkTrigger} from '../src/CoachMark';
+import {describe, expect, it, vi} from 'vitest';
import React from 'react';
-import userEvent, {UserEvent} from '@testing-library/user-event';
-
-const mockAnimations = () => {
- Element.prototype.animate = jest.fn().mockImplementation(() => ({finished: Promise.resolve()}));
-};
+import {render} from './utils/render';
describe('CoachMark', () => {
- let user: UserEvent | null = null;
- beforeAll(() => {
- jest.useFakeTimers();
- mockAnimations();
- });
- beforeEach(() => {
- user = userEvent.setup({delay: null, pointerMap});
- });
- afterAll(() => {
- act(() => {jest.runAllTimers();});
- });
-
- it('renders a coachmark', async () => {
- let onPress = jest.fn();
- let {getAllByRole} = render(
+ it('renders', async () => {
+ vi.useFakeTimers();
+ Element.prototype.animate = vi.fn().mockImplementation(() => ({finished: Promise.resolve()}));
+ const screen = await render(
Sync with CC
-
-
-
Hello
-
- Skip tour
- Restart tour
-
- Command + B
This is the description
-
- 1 of 10
- Previous
- Next
-
);
- act(() => {jest.runAllTimers();});
- expect(getAllByRole('button').length).toBe(4); // 2 Dismiss + 2 actions
- await user?.click(getAllByRole('button')[2]);
- expect(onPress).toHaveBeenCalled();
+ vi.runAllTimers();
+ expect(screen.container.firstChild).toBeInTheDocument();
+ vi.useRealTimers();
});
});
diff --git a/packages/@react-spectrum/s2/test/ColorArea.test.tsx b/packages/@react-spectrum/s2/test/ColorArea.test.tsx
new file mode 100644
index 00000000000..956b81e32f5
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/ColorArea.test.tsx
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {ColorArea} from '../src';
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('ColorArea', () => {
+ it('renders', async () => {
+ const screen = await render(
+
+ );
+ expect(screen.getByRole('slider')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/ColorField.test.tsx b/packages/@react-spectrum/s2/test/ColorField.test.tsx
new file mode 100644
index 00000000000..9b34419a90e
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/ColorField.test.tsx
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {ColorField} from '../src';
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('ColorField', () => {
+ it('renders', async () => {
+ const screen = await render(
+
+ );
+ expect(screen.getByRole('textbox')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/ColorSlider.test.tsx b/packages/@react-spectrum/s2/test/ColorSlider.test.tsx
new file mode 100644
index 00000000000..e1ea840d43c
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/ColorSlider.test.tsx
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {ColorSlider} from '../src';
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('ColorSlider', () => {
+ it('renders', async () => {
+ const screen = await render(
+
+ );
+ expect(screen.getByRole('slider')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/ColorSwatch.test.tsx b/packages/@react-spectrum/s2/test/ColorSwatch.test.tsx
new file mode 100644
index 00000000000..4041706f02f
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/ColorSwatch.test.tsx
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {ColorSwatch} from '../src';
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('ColorSwatch', () => {
+ it('renders', async () => {
+ const screen = await render(
+
+
+
+
+
+
+
+ );
+ expect(screen.container.firstChild).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/ColorSwatchPicker.test.tsx b/packages/@react-spectrum/s2/test/ColorSwatchPicker.test.tsx
new file mode 100644
index 00000000000..7766648c231
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/ColorSwatchPicker.test.tsx
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {ColorSwatch, ColorSwatchPicker} from '../src';
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('ColorSwatchPicker', () => {
+ it('renders', async () => {
+ const screen = await render(
+
+
+
+
+
+
+
+
+ );
+ expect(screen.getByRole('listbox')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/ColorWheel.test.tsx b/packages/@react-spectrum/s2/test/ColorWheel.test.tsx
new file mode 100644
index 00000000000..492aaa2cc85
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/ColorWheel.test.tsx
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {ColorWheel} from '../src';
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('ColorWheel', () => {
+ it('renders', async () => {
+ const screen = await render( );
+ expect(screen.getByRole('slider')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/Combobox.test.tsx b/packages/@react-spectrum/s2/test/Combobox.test.tsx
index e90c8119a0a..f28c2ae8030 100644
--- a/packages/@react-spectrum/s2/test/Combobox.test.tsx
+++ b/packages/@react-spectrum/s2/test/Combobox.test.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2025 Adobe. All rights reserved.
+ * Copyright 2026 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
@@ -10,119 +10,15 @@
* governing permissions and limitations under the License.
*/
-jest.mock('@react-aria/live-announcer');
-import {act, pointerMap, render, setupIntersectionObserverMock, within} from '@react-spectrum/test-utils-internal';
-import {announce} from '@react-aria/live-announcer';
-import {ComboBox, ComboBoxItem, Content, ContextualHelp, Heading, Text} from '../src';
+import {ComboBox, ComboBoxItem} from '../src';
+import {describe, expect, it} from 'vitest';
import React from 'react';
-import {User} from '@react-aria/test-utils';
-import userEvent from '@testing-library/user-event';
+import {render} from './utils/render';
describe('Combobox', () => {
- let user;
- let testUtilUser = new User();
-
- function DynamicCombobox(props) {
- let {items, loadingState, onLoadMore, ...otherProps} = props;
- return (
-
- {(item: any) => {item.name} }
-
- );
- }
-
- beforeAll(function () {
- jest.useFakeTimers();
- jest.spyOn(window.HTMLElement.prototype, 'clientWidth', 'get').mockImplementation(() => 100);
- jest.spyOn(window.HTMLElement.prototype, 'clientHeight', 'get').mockImplementation(() => 100);
- jest.spyOn(window.HTMLElement.prototype, 'scrollHeight', 'get').mockImplementation(() => 50);
- user = userEvent.setup({delay: null, pointerMap});
- });
-
- afterEach(() => {
- jest.clearAllMocks();
- act(() => jest.runAllTimers());
- });
-
- afterAll(function () {
- jest.restoreAllMocks();
- });
-
- it('should render the sentinel when the combobox is empty', async () => {
- let tree = render(
-
- {[]}
-
- );
-
- let comboboxTester = testUtilUser.createTester('ComboBox', {root: tree.container});
- expect(comboboxTester.listbox).toBeFalsy();
- comboboxTester.setInteractionType('mouse');
- await comboboxTester.open();
-
- let options = comboboxTester.options();
- expect(options).toHaveLength(1);
- expect(comboboxTester.listbox).toBeTruthy();
- expect(options[0]).toHaveTextContent('No results');
- expect(within(comboboxTester.listbox!).getByTestId('loadMoreSentinel')).toBeInTheDocument();
- });
-
- it('should only call loadMore whenever intersection is detected', async () => {
- let onLoadMore = jest.fn();
- let observe = jest.fn();
- let observer = setupIntersectionObserverMock({
- observe
- });
-
- let tree = render(
-
- Chocolate
- Mint
- Strawberry
- Vanilla
- Chocolate Chip Cookie Dough
-
- );
-
- let comboboxTester = testUtilUser.createTester('ComboBox', {root: tree.container});
- expect(comboboxTester.listbox).toBeFalsy();
- comboboxTester.setInteractionType('mouse');
- await comboboxTester.open();
-
- expect(onLoadMore).toHaveBeenCalledTimes(0);
- let sentinel = tree.getByTestId('loadMoreSentinel');
- expect(observe).toHaveBeenLastCalledWith(sentinel);
-
-
- act(() => {observer.instance.triggerCallback([{isIntersecting: true}]);});
- act(() => {jest.runAllTimers();});
-
- tree.rerender(
-
- Chocolate
- Mint
- Strawberry
- Vanilla
- Chocolate Chip Cookie Dough
-
- );
-
- act(() => {observer.instance.triggerCallback([{isIntersecting: true}]);});
- act(() => {jest.runAllTimers();});
- // Note that if this was using useAsyncList, we'd be shielded from extranous onLoadMore calls but
- // we want to leave that to user discretion
- expect(onLoadMore).toHaveBeenCalledTimes(2);
- });
-
- it('should omit the loader from the count of items', async () => {
- jest.spyOn(navigator, 'platform', 'get').mockImplementation(() => 'MacIntel');
- let tree = render(
-
+ it('renders', async () => {
+ const screen = await render(
+
Chocolate
Mint
Strawberry
@@ -130,87 +26,6 @@ describe('Combobox', () => {
Chocolate Chip Cookie Dough
);
-
- let comboboxTester = testUtilUser.createTester('ComboBox', {root: tree.container, interactionType: 'mouse'});
- await comboboxTester.open();
-
- expect(announce).toHaveBeenLastCalledWith('5 options available.');
- expect(within(comboboxTester.listbox!).getByRole('progressbar', {hidden: true})).toBeInTheDocument();
-
- await user.keyboard('C');
- expect(announce).toHaveBeenLastCalledWith('2 options available.');
- });
-
- it('should properly calculate the expected row index values even when the content changes', async () => {
- let items = [{name: 'Chocolate'}, {name: 'Mint'}, {name: 'Chocolate Chip'}];
- let tree = render( );
-
- let comboboxTester = testUtilUser.createTester('ComboBox', {root: tree.container, interactionType: 'mouse'});
- await comboboxTester.open();
- let options = comboboxTester.options();
- for (let [index, option] of options.entries()) {
- expect(option).toHaveAttribute('aria-posinset', `${index + 1}`);
- }
-
- tree.rerender( );
- options = comboboxTester.options();
- for (let [index, option] of options.entries()) {
- expect(option).toHaveAttribute('aria-posinset', `${index + 1}`);
- }
-
- // A bit contrived, but essentially testing a combinaiton of insertions/deletions along side some of the old entries remaining
- let newItems = [{name: 'Chocolate'}, {name: 'Chocolate Mint'}, {name: 'Chocolate Chip Cookie Dough'}, {name: 'Chocolate Chip'}];
- tree.rerender( );
-
- options = comboboxTester.options();
- for (let [index, option] of options.entries()) {
- expect(option).toHaveAttribute('aria-posinset', `${index + 1}`);
- }
- });
-
- it('should support contextual help', async () => {
- // Issue with how we don't render the contextual help button in the fake DOM since PressResponder isn't using createHideableComponent
- let warn = jest.spyOn(global.console, 'warn').mockImplementation();
- let user = userEvent.setup({delay: null, pointerMap});
- let tree = render(
-
- Title here
-
-
- Contents
-
-
-
- }
- label="test">
- Chocolate
- Mint
- Strawberry
- Vanilla
- Chocolate Chip Cookie Dough
-
- );
-
- let comboboxTester = testUtilUser.createTester('ComboBox', {root: tree.getByTestId('testcombobox')});
- let buttons = tree.getAllByRole('button');
- expect(buttons).toHaveLength(2);
- expect(buttons[1]).toBe(comboboxTester.trigger);
-
- await user.click(buttons[0]);
-
- act(() => {
- jest.runAllTimers();
- });
-
- let dialog = tree.getByRole('dialog');
- expect(dialog).toBeVisible();
-
- // Because of the fake DOM we'll see this twice
- expect(tree.getAllByText('Title here')[1]).toBeVisible();
- expect(tree.getAllByText('Contents')[1]).toBeVisible();
- warn.mockRestore();
+ expect(screen.getByRole('combobox')).toBeInTheDocument();
});
});
diff --git a/packages/@react-spectrum/s2/test/ContextualHelp.test.tsx b/packages/@react-spectrum/s2/test/ContextualHelp.test.tsx
new file mode 100644
index 00000000000..5351e5009a1
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/ContextualHelp.test.tsx
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {Content, ContextualHelp, Heading} from '../src';
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('ContextualHelp', () => {
+ it('renders', async () => {
+ const screen = await render(
+
+ Permission required
+ Your admin must grant you permission before you can create a segment.
+
+ );
+ expect(screen.getByRole('button')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/CustomDialog.test.tsx b/packages/@react-spectrum/s2/test/CustomDialog.test.tsx
index 0dd0ce47bb8..dc6d4997145 100644
--- a/packages/@react-spectrum/s2/test/CustomDialog.test.tsx
+++ b/packages/@react-spectrum/s2/test/CustomDialog.test.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2025 Adobe. All rights reserved.
+ * Copyright 2026 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
@@ -10,48 +10,27 @@
* governing permissions and limitations under the License.
*/
-import {act, pointerMap, render} from '@react-spectrum/test-utils-internal';
import {ActionButton, CustomDialog, DialogTrigger, Tag, TagGroup} from '../src';
+import {describe, expect, it, vi} from 'vitest';
import React from 'react';
-import userEvent from '@testing-library/user-event';
+import {render} from './utils/render';
describe('CustomDialog', () => {
- let user;
- beforeAll(() => {
- jest.useFakeTimers();
- user = userEvent.setup({delay: null, pointerMap});
- });
-
- afterEach(() => {
- jest.clearAllMocks();
- act(() => jest.runAllTimers());
- });
-
- afterAll(function () {
- jest.restoreAllMocks();
- });
-
- it('should allow you to render a taggroup inside', async () => {
- let {getByRole} = render(
-
+ it('renders', async () => {
+ vi.useFakeTimers();
+ const screen = await render(
+
Open dialog
- {}}>
+ {}}>
Chocolate
Mint
- Strawberry
- Vanilla
);
-
- let trigger = getByRole('button');
- await user.click(trigger);
- act(() => {jest.runAllTimers();});
- expect(getByRole('dialog')).toBeVisible();
+ vi.runAllTimers();
+ expect(screen.getByRole('dialog')).toBeInTheDocument();
+ vi.useRealTimers();
});
});
diff --git a/packages/@react-spectrum/s2/test/DateField.test.tsx b/packages/@react-spectrum/s2/test/DateField.test.tsx
new file mode 100644
index 00000000000..920215993e0
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/DateField.test.tsx
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {DateField} from '../src';
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('DateField', () => {
+ it('renders', async () => {
+ const screen = await render( );
+ expect(screen.getByRole('group')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/DatePicker.test.tsx b/packages/@react-spectrum/s2/test/DatePicker.test.tsx
new file mode 100644
index 00000000000..8dac4681667
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/DatePicker.test.tsx
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {DatePicker} from '../src';
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('DatePicker', () => {
+ it('renders', async () => {
+ const screen = await render( );
+ expect(screen.getByRole('button')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/DateRangePicker.test.tsx b/packages/@react-spectrum/s2/test/DateRangePicker.test.tsx
new file mode 100644
index 00000000000..2cd520905cb
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/DateRangePicker.test.tsx
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {DateRangePicker} from '../src';
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('DateRangePicker', () => {
+ it('renders', async () => {
+ const screen = await render( );
+ expect(screen.getByRole('button')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/Dialog.test.tsx b/packages/@react-spectrum/s2/test/Dialog.test.tsx
new file mode 100644
index 00000000000..dfeb7f9cacb
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/Dialog.test.tsx
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {
+ Button,
+ ButtonGroup,
+ Checkbox,
+ Content,
+ Dialog,
+ DialogTrigger,
+ Footer,
+ Form,
+ Heading,
+ Image,
+ TextField
+} from '../src';
+import {describe, expect, it, vi} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('Dialog', () => {
+ it('renders', async () => {
+ vi.useFakeTimers();
+ const screen = await render(
+
+ Open Dialog
+
+ {({close}) => (
+ <>
+
+ Subscribe to our newsletter
+
+ Enter your information to subscribe to our newsletter and receive updates about new features and announcements.
+
+
+
+ Don't show this again
+
+
+ Cancel
+ Subscribe
+
+ >
+ )}
+
+
+ );
+ vi.runAllTimers();
+ expect(screen.getByRole('dialog')).toBeInTheDocument();
+ vi.useRealTimers();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/Disclosure.test.tsx b/packages/@react-spectrum/s2/test/Disclosure.test.tsx
new file mode 100644
index 00000000000..9ccbdb271c1
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/Disclosure.test.tsx
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {describe, expect, it} from 'vitest';
+import {Disclosure, DisclosurePanel, DisclosureTitle} from '../src';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('Disclosure', () => {
+ it('renders', async () => {
+ const screen = await render(
+
+ System Requirements
+ Details about system requirements here.
+
+ );
+ expect(screen.getByRole('button')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/Divider.test.tsx b/packages/@react-spectrum/s2/test/Divider.test.tsx
new file mode 100644
index 00000000000..bcf03fa0710
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/Divider.test.tsx
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {describe, expect, it} from 'vitest';
+import {Divider} from '../src';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('Divider', () => {
+ it('renders', async () => {
+ const screen = await render( );
+ expect(screen.container.firstChild).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/DropZone.test.tsx b/packages/@react-spectrum/s2/test/DropZone.test.tsx
new file mode 100644
index 00000000000..5a497b71bca
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/DropZone.test.tsx
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {Button, ButtonGroup, Content, DropZone, FileTrigger, Heading, IllustratedMessage} from '../src';
+import CloudUpload from '@react-spectrum/s2/illustrations/gradient/generic1/CloudUpload';
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+import {style} from '../style' with {type: 'macro'};
+
+describe('DropZone', () => {
+ it('renders', async () => {
+ const screen = await render(
+ (
+ ['text/plain', 'image/jpeg', 'image/png', 'image/gif'].some(t => types.has(t))
+ ? 'copy'
+ : 'cancel'
+ )}
+ onDrop={async () => {}}>
+
+
+ Drag and drop your file
+ or
+
+
+ Select a file
+
+
+
+
+ );
+ expect(screen.getByText('Select a file')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/EditableTableView.test.tsx b/packages/@react-spectrum/s2/test/EditableTableView.test.tsx
deleted file mode 100644
index 560fdd4a338..00000000000
--- a/packages/@react-spectrum/s2/test/EditableTableView.test.tsx
+++ /dev/null
@@ -1,868 +0,0 @@
-/*
- * Copyright 2025 Adobe. All rights reserved.
- * This file is licensed to you under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License. You may obtain a copy
- * of the License at http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under
- * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
- * OF ANY KIND, either express or implied. See the License for the specific language
- * governing permissions and limitations under the License.
- */
-
-jest.mock('@react-aria/live-announcer');
-jest.mock('@react-aria/utils/src/scrollIntoView');
-import {act, render, within} from '@react-spectrum/test-utils-internal';
-import {
- ActionButton,
- Cell,
- Column,
- ColumnProps,
- EditableCell,
- Picker,
- PickerItem,
- Row,
- StatusLight,
- TableBody,
- TableHeader,
- TableView,
- TableViewProps,
- Text,
- TextField
-} from '../src';
-import Edit from '../s2wf-icons/S2_Icon_Edit_20_N.svg';
-import {installPointerEvent, pointerMap, User} from '@react-aria/test-utils';
-import {Key} from '@react-types/shared';
-import React, {useCallback, useEffect, useRef} from 'react';
-import {useEffectEvent} from '@react-aria/utils';
-import {useListData} from '@react-stately/data';
-import userEvent from '@testing-library/user-event';
-
-// @ts-ignore
-window.getComputedStyle = (el) => el.style;
-
-describe('TableView', () => {
- let user;
- let offsetWidth, offsetHeight;
- let testUtilUser = new User({advanceTimer: jest.advanceTimersByTime});
- beforeAll(function () {
- offsetWidth = jest.spyOn(window.HTMLElement.prototype, 'clientWidth', 'get').mockImplementation(() => 400);
- offsetHeight = jest.spyOn(window.HTMLElement.prototype, 'clientHeight', 'get').mockImplementation(() => 200);
- jest.spyOn(window.HTMLElement.prototype, 'scrollHeight', 'get').mockImplementation(() => 50);
- jest.useFakeTimers();
- });
-
- beforeEach(function () {
- user = userEvent.setup({delay: null, pointerMap});
- });
-
- afterAll(function () {
- offsetWidth.mockReset();
- offsetHeight.mockReset();
- });
-
- afterEach(() => {
- act(() => {jest.runAllTimers();});
- });
- let defaultItems = [
- {id: 1,
- fruits: 'Apples', task: 'Collect', status: 'Pending', farmer: 'Eva',
- isSaving: {}
- },
- {id: 2,
- fruits: 'Oranges', task: 'Collect', status: 'Pending', farmer: 'Steven',
- isSaving: {}
- },
- {id: 3,
- fruits: 'Pears', task: 'Collect', status: 'Pending', farmer: 'Michael',
- isSaving: {}
- },
- {id: 4,
- fruits: 'Cherries', task: 'Collect', status: 'Pending', farmer: 'Sara',
- isSaving: {}
- },
- {id: 5,
- fruits: 'Dates', task: 'Collect', status: 'Pending', farmer: 'Karina',
- isSaving: {}
- },
- {id: 6,
- fruits: 'Bananas', task: 'Collect', status: 'Pending', farmer: 'Otto',
- isSaving: {}
- },
- {id: 7,
- fruits: 'Melons', task: 'Collect', status: 'Pending', farmer: 'Matt',
- isSaving: {}
- },
- {id: 8,
- fruits: 'Figs', task: 'Collect', status: 'Pending', farmer: 'Emily',
- isSaving: {}
- },
- {id: 9,
- fruits: 'Blueberries', task: 'Collect', status: 'Pending', farmer: 'Amelia',
- isSaving: {}
- },
- {id: 10,
- fruits: 'Blackberries', task: 'Collect', status: 'Pending', farmer: 'Isla',
- isSaving: {}
- }
- ];
-
- let editableColumns: Array & {name: string}> = [
- {name: 'Fruits', id: 'fruits', isRowHeader: true, width: '6fr', minWidth: 300},
- {name: 'Task', id: 'task', width: '2fr', minWidth: 100},
- {name: 'Status', id: 'status', width: '2fr', showDivider: true, minWidth: 100},
- {name: 'Farmer', id: 'farmer', width: '2fr', minWidth: 150}
- ];
-
- interface EditableTableProps extends TableViewProps {}
-
- function EditableTable(props: EditableTableProps & {delay?: number, onCancel?: () => void}) {
- let {delay = 0, onCancel} = props;
- let columns = editableColumns;
- let data = useListData({initialItems: defaultItems});
-
- let saveItem = useEffectEvent((id: Key, columnId: Key) => {
- data.update(id, (prevItem) => ({...prevItem, isSaving: {...prevItem.isSaving, [columnId]: false}}));
- currentRequests.current.delete(id);
- });
- let currentRequests = useRef}>>(new Map());
- let onChange = useCallback((id: Key, columnId: Key, values: any) => {
- let value = values[columnId];
- if (value === null) {
- return;
- }
- let alreadySaving = currentRequests.current.get(id);
- if (alreadySaving) {
- // remove and cancel the previous request
- currentRequests.current.delete(id);
- clearTimeout(alreadySaving.request);
- }
- data.update(id, (prevItem) => ({...prevItem, [columnId]: value, isSaving: {...prevItem.isSaving, [columnId]: true}}));
- }, [data]);
-
- useEffect(() => {
- // if any item is saving and we don't have a request for it, start a timer to commit it
- for (const item of data.items) {
- for (const columnId in item.isSaving) {
- if (item.isSaving[columnId] && !currentRequests.current.has(item.id)) {
- let timeout = setTimeout(() => {
- saveItem(item.id, columnId);
- }, delay);
- currentRequests.current.set(item.id, {request: timeout});
- }
- }
- }
- }, [data, delay]);
-
- return (
-
-
-
- {(column) => (
- {column.name}
- )}
-
-
- {item => (
-
- {(column) => {
- if (column.id === 'fruits') {
- return (
- {
- e.preventDefault();
- let formData = new FormData(e.target as HTMLFormElement);
- let values = Object.fromEntries(formData.entries());
- onChange(item.id, column.id!, values);
- }}
- onCancel={onCancel}
- isSaving={item.isSaving[column.id!]}
- renderEditing={() => (
- value.length > 0 ? null : 'Fruit name is required'}
- defaultValue={item[column.id!]}
- name={column.id! as string} />
- )}>
-
-
- );
- }
- if (column.id === 'farmer') {
- return (
- {
- e.preventDefault();
- let formData = new FormData(e.target as HTMLFormElement);
- let values = Object.fromEntries(formData.entries());
- onChange(item.id, column.id!, values);
- }}
- onCancel={onCancel}
- isSaving={item.isSaving[column.id!]}
- renderEditing={() => (
-
- Eva
- Steven
- Michael
- Sara
- Karina
- Otto
- Matt
- Emily
- Amelia
- Isla
-
- )}>
-
-
- );
- }
- if (column.id === 'status') {
- return (
-
- {item[column.id]}
- |
- );
- }
- return {item[column.id!]} | ;
- }}
-
- )}
-
-
-
After
-
- );
- }
-
- describe('keyboard', () => {
- it('should edit text in a cell either through a TextField or a Picker', async () => {
- let {getByRole} = render(
-
- );
-
- let tableTester = testUtilUser.createTester('Table', {root: getByRole('grid')});
- await user.tab();
- await user.keyboard('{ArrowRight}');
- let dialogTrigger = document.activeElement! as HTMLElement;
- let dialogTester = testUtilUser.createTester('Dialog', {root: dialogTrigger, interactionType: 'keyboard', overlayType: 'modal'});
- await dialogTester.open();
- let dialog = dialogTester.dialog;
- expect(dialog).toBeVisible();
-
- let input = within(dialog!).getByRole('textbox');
- expect(input).toHaveFocus();
-
- await user.keyboard('Apples Crisp');
- await user.keyboard('{Enter}'); // implicitly submit through form
-
- act(() => {jest.runAllTimers();});
-
- expect(dialog).not.toBeInTheDocument();
-
- expect(tableTester.findRow({rowIndexOrText: 'Apples Crisp'})).toBeInTheDocument();
-
- // navigate to Farmer column
- await user.keyboard('{ArrowRight}');
- await user.keyboard('{ArrowRight}');
- await user.keyboard('{ArrowRight}');
- dialogTrigger = document.activeElement! as HTMLElement;
- dialogTester = testUtilUser.createTester('Dialog', {root: dialogTrigger, interactionType: 'keyboard', overlayType: 'modal'});
- await dialogTester.open();
- dialog = dialogTester.dialog;
- // TODO: also weird that it is dialog.dialog?
- expect(dialog).toBeVisible();
-
- let selectTester = testUtilUser.createTester('Select', {root: dialog!});
- expect(selectTester.trigger).toHaveFocus();
- await selectTester.selectOption({option: 'Steven'});
- act(() => {jest.runAllTimers();});
- await user.tab();
- await user.tab();
- expect(within(dialog!).getByRole('button', {name: 'Save'})).toHaveFocus();
- await user.keyboard('{Enter}');
-
- act(() => {jest.runAllTimers();});
-
- expect(dialog).not.toBeInTheDocument();
- expect(within(tableTester.findRow({rowIndexOrText: 'Apples Crisp'})).getByText('Steven')).toBeInTheDocument();
-
- await user.tab();
- expect(getByRole('button', {name: 'After'})).toHaveFocus();
-
- await user.tab({shift: true});
- expect(within(tableTester.findRow({rowIndexOrText: 'Apples Crisp'})).getByRole('button', {name: 'Edit farmer'})).toHaveFocus();
- });
-
- it('should perform validation when editing text in a cell', async () => {
- let {getByRole} = render(
-
- );
-
- let tableTester = testUtilUser.createTester('Table', {root: getByRole('grid')});
- await user.tab();
- await user.keyboard('{ArrowRight}');
- await user.keyboard('{Enter}');
-
- let dialog = getByRole('dialog');
- expect(dialog).toBeVisible();
-
- let input = within(dialog).getByRole('textbox');
- expect(input).toHaveFocus();
-
- await user.clear(input);
- await user.keyboard('{Enter}');
-
- act(() => {jest.runAllTimers();});
-
- expect(dialog).toBeInTheDocument();
- expect(input).toHaveFocus();
- expect(document.getElementById(input.getAttribute('aria-describedby')!)).toHaveTextContent('Fruit name is required');
-
- await user.keyboard('Peaches');
- await user.tab();
- await user.tab();
- await user.keyboard('{Enter}');
-
- act(() => {jest.runAllTimers();});
-
- expect(dialog).not.toBeInTheDocument();
-
- expect(tableTester.findRow({rowIndexOrText: 'Peaches'})).toBeInTheDocument();
- });
-
- it('should be cancellable through the buttons in the dialog', async () => {
- let onCancel = jest.fn();
- let {getByRole} = render(
-
- );
-
- let tableTester = testUtilUser.createTester('Table', {root: getByRole('grid')});
- await user.tab();
- await user.keyboard('{ArrowRight}');
- await user.keyboard('{Enter}');
-
- let dialog = getByRole('dialog');
- expect(dialog).toBeVisible();
-
- let input = within(dialog).getByRole('textbox');
- expect(input).toHaveFocus();
-
- await user.keyboard(' Crisp');
- await user.tab();
- await user.keyboard('{Enter}');
-
- act(() => {jest.runAllTimers();});
-
- expect(dialog).not.toBeInTheDocument();
-
- expect(tableTester.findRow({rowIndexOrText: 'Apples'})).toBeInTheDocument();
- expect(onCancel).toHaveBeenCalled();
- });
-
- it('should be cancellable through Escape key', async () => {
- let onCancel = jest.fn();
- let {getByRole} = render(
-
- );
-
- let tableTester = testUtilUser.createTester('Table', {root: getByRole('grid')});
- await user.tab();
- await user.keyboard('{ArrowRight}');
- await user.keyboard('{Enter}');
-
- let dialog = getByRole('dialog');
- expect(dialog).toBeVisible();
-
- let input = within(dialog).getByRole('textbox');
- expect(input).toHaveFocus();
-
- await user.keyboard(' Crisp');
- await user.keyboard('{Escape}');
-
- act(() => {jest.runAllTimers();});
-
- expect(dialog).not.toBeInTheDocument();
- expect(tableTester.findRow({rowIndexOrText: 'Apples'})).toBeInTheDocument();
- expect(onCancel).toHaveBeenCalled();
- });
- });
-
- describe('pointer', () => {
- installPointerEvent();
-
- it('should edit text in a cell', async () => {
- let {getByRole} = render(
-
- );
-
- let tableTester = testUtilUser.createTester('Table', {root: getByRole('grid')});
- await user.click(within(tableTester.findCell({text: 'Apples'})).getByRole('button'));
-
- let dialog = getByRole('dialog');
- expect(dialog).toBeVisible();
-
- await user.click(within(dialog).getByRole('textbox'));
- await user.keyboard(' Crisp');
- await user.click(document.body);
-
- act(() => {jest.runAllTimers();});
-
- expect(dialog).not.toBeInTheDocument();
- expect(tableTester.findRow({rowIndexOrText: 'Apples Crisp'})).toBeInTheDocument();
- });
- });
-
- describe('pending', () => {
- it('should display a pending state when editing a cell', async () => {
- let {getByRole} = render(
-
- );
-
- let tableTester = testUtilUser.createTester('Table', {root: getByRole('grid')});
- await user.tab();
- await user.keyboard('{ArrowRight}');
- await user.keyboard('{Enter}');
-
- let dialog = getByRole('dialog');
- expect(dialog).toBeVisible();
-
- let input = within(dialog).getByRole('textbox');
- expect(input).toHaveFocus();
-
- await user.keyboard('Apples Crisp');
- await user.keyboard('{Enter}'); // implicitly submit through form
-
- act(() => {jest.advanceTimersByTime(5000);});
-
- expect(dialog).not.toBeInTheDocument();
- expect(tableTester.findRow({rowIndexOrText: 'Apples Crisp'})).toBeInTheDocument();
- let button = within(tableTester.findCell({text: 'Apples Crisp'})).getByRole('button');
- expect(button).toHaveAttribute('aria-disabled', 'true');
- expect(button).toHaveFocus();
-
- act(() => {jest.runAllTimers();});
-
- expect(button).not.toHaveAttribute('aria-disabled');
- expect(button).toHaveFocus();
- });
-
- it('should allow tabbing off a pending button', async () => {
- let {getByRole} = render(
-
- );
-
- let tableTester = testUtilUser.createTester('Table', {root: getByRole('grid')});
- await user.tab();
- await user.keyboard('{ArrowRight}');
- await user.keyboard('{Enter}');
-
- let dialog = getByRole('dialog');
- expect(dialog).toBeVisible();
-
- let input = within(dialog).getByRole('textbox');
- expect(input).toHaveFocus();
-
- await user.keyboard('Apples Crisp');
- await user.keyboard('{Enter}'); // implicitly submit through form
-
- act(() => {jest.advanceTimersByTime(5000);});
-
- expect(dialog).not.toBeInTheDocument();
- expect(tableTester.findRow({rowIndexOrText: 'Apples Crisp'})).toBeInTheDocument();
- let button = within(tableTester.findCell({text: 'Apples Crisp'})).getByRole('button');
- expect(button).toHaveAttribute('aria-disabled', 'true');
- expect(button).toHaveFocus();
-
- await user.tab();
- expect(getByRole('button', {name: 'After'})).toHaveFocus();
-
- act(() => {jest.runAllTimers();});
-
- expect(button).not.toHaveAttribute('aria-disabled');
- });
- });
-
- if (parseInt(React.version, 10) >= 19) {
- describe('using action instead of onSubmit', () => {
- function ActionEditableTable(props: EditableTableProps & {delay?: number, onCancel?: () => void}) {
- let {delay = 0, onCancel} = props;
- let columns = editableColumns;
- let data = useListData({initialItems: defaultItems});
-
- let saveItem = useEffectEvent((id: Key, columnId: Key) => {
- data.update(id, (prevItem) => ({...prevItem, isSaving: {...prevItem.isSaving, [columnId]: false}}));
- currentRequests.current.delete(id);
- });
- let currentRequests = useRef}>>(new Map());
- let onChange = useCallback((id: Key, columnId: Key, values: any) => {
- let value = values.get(columnId);
- if (value === null) {
- return;
- }
- let alreadySaving = currentRequests.current.get(id);
- if (alreadySaving) {
- // remove and cancel the previous request
- currentRequests.current.delete(id);
- clearTimeout(alreadySaving.request);
- }
- data.update(id, (prevItem) => ({...prevItem, [columnId]: value, isSaving: {...prevItem.isSaving, [columnId]: true}}));
- }, [data]);
-
- useEffect(() => {
- // if any item is saving and we don't have a request for it, start a timer to commit it
- for (const item of data.items) {
- for (const columnId in item.isSaving) {
- if (item.isSaving[columnId] && !currentRequests.current.has(item.id)) {
- let timeout = setTimeout(() => {
- saveItem(item.id, columnId);
- }, delay);
- currentRequests.current.set(item.id, {request: timeout});
- }
- }
- }
- }, [data, delay]);
-
- return (
-
-
-
- {(column) => (
- {column.name}
- )}
-
-
- {item => (
-
- {(column) => {
- if (column.id === 'fruits') {
- return (
- {
- onChange(item.id, column.id!, e);
- }}
- onCancel={onCancel}
- isSaving={item.isSaving[column.id!]}
- renderEditing={() => (
- value.length > 0 ? null : 'Fruit name is required'}
- defaultValue={item[column.id!]}
- name={column.id! as string} />
- )}>
-
-
- );
- }
- if (column.id === 'farmer') {
- return (
- {
- onChange(item.id, column.id!, e);
- }}
- onCancel={onCancel}
- isSaving={item.isSaving[column.id!]}
- renderEditing={() => (
-
- Eva
- Steven
- Michael
- Sara
- Karina
- Otto
- Matt
- Emily
- Amelia
- Isla
-
- )}>
-
-
- );
- }
- if (column.id === 'status') {
- return (
-
- {item[column.id]}
- |
- );
- }
- return {item[column.id!]} | ;
- }}
-
- )}
-
-
-
After
-
- );
- }
-
- describe('keyboard', () => {
- it('should edit text in a cell either through a TextField or a Picker', async () => {
- let {getByRole} = render(
-
- );
-
- let tableTester = testUtilUser.createTester('Table', {root: getByRole('grid')});
- await user.tab();
- await user.keyboard('{ArrowRight}');
- let dialogTrigger = document.activeElement! as HTMLElement;
- let dialogTester = testUtilUser.createTester('Dialog', {root: dialogTrigger, interactionType: 'keyboard', overlayType: 'modal'});
- await dialogTester.open();
- let dialog = dialogTester.dialog;
- expect(dialog).toBeVisible();
-
- let input = within(dialog!).getByRole('textbox');
- expect(input).toHaveFocus();
-
- await user.keyboard('Apples Crisp');
- await user.keyboard('{Enter}'); // implicitly submit through form
-
- act(() => {jest.runAllTimers();});
-
- expect(dialog).not.toBeInTheDocument();
-
- expect(tableTester.findRow({rowIndexOrText: 'Apples Crisp'})).toBeInTheDocument();
-
- // navigate to Farmer column
- await user.keyboard('{ArrowRight}');
- await user.keyboard('{ArrowRight}');
- await user.keyboard('{ArrowRight}');
- dialogTrigger = document.activeElement! as HTMLElement;
- dialogTester = testUtilUser.createTester('Dialog', {root: dialogTrigger, interactionType: 'keyboard', overlayType: 'modal'});
- await dialogTester.open();
- dialog = dialogTester.dialog;
- // TODO: also weird that it is dialog.dialog?
- expect(dialog).toBeVisible();
-
- let selectTester = testUtilUser.createTester('Select', {root: dialog!});
- expect(selectTester.trigger).toHaveFocus();
- await selectTester.selectOption({option: 'Steven'});
- act(() => {jest.runAllTimers();});
- await user.tab();
- await user.tab();
- expect(within(dialog!).getByRole('button', {name: 'Save'})).toHaveFocus();
- await user.keyboard('{Enter}');
-
- act(() => {jest.runAllTimers();});
-
- expect(dialog).not.toBeInTheDocument();
- expect(within(tableTester.findRow({rowIndexOrText: 'Apples Crisp'})).getByText('Steven')).toBeInTheDocument();
-
- await user.tab();
- expect(getByRole('button', {name: 'After'})).toHaveFocus();
-
- await user.tab({shift: true});
- expect(within(tableTester.findRow({rowIndexOrText: 'Apples Crisp'})).getByRole('button', {name: 'Edit farmer'})).toHaveFocus();
- });
-
- it('should perform validation when editing text in a cell', async () => {
- let {getByRole} = render(
-
- );
-
- let tableTester = testUtilUser.createTester('Table', {root: getByRole('grid')});
- await user.tab();
- await user.keyboard('{ArrowRight}');
- await user.keyboard('{Enter}');
-
- let dialog = getByRole('dialog');
- expect(dialog).toBeVisible();
-
- let input = within(dialog).getByRole('textbox');
- expect(input).toHaveFocus();
-
- await user.clear(input);
- await user.keyboard('{Enter}');
-
- act(() => {jest.runAllTimers();});
-
- expect(dialog).toBeInTheDocument();
- expect(input).toHaveFocus();
- expect(document.getElementById(input.getAttribute('aria-describedby')!)).toHaveTextContent('Fruit name is required');
-
- await user.keyboard('Peaches');
- await user.tab();
- await user.tab();
- await user.keyboard('{Enter}');
-
- act(() => {jest.runAllTimers();});
-
- expect(dialog).not.toBeInTheDocument();
-
- expect(tableTester.findRow({rowIndexOrText: 'Peaches'})).toBeInTheDocument();
- });
-
- it('should be cancellable through the buttons in the dialog', async () => {
- let onCancel = jest.fn();
- let {getByRole} = render(
-
- );
-
- let tableTester = testUtilUser.createTester('Table', {root: getByRole('grid')});
- await user.tab();
- await user.keyboard('{ArrowRight}');
- await user.keyboard('{Enter}');
-
- let dialog = getByRole('dialog');
- expect(dialog).toBeVisible();
-
- let input = within(dialog).getByRole('textbox');
- expect(input).toHaveFocus();
-
- await user.keyboard(' Crisp');
- await user.tab();
- await user.keyboard('{Enter}');
-
- act(() => {jest.runAllTimers();});
-
- expect(dialog).not.toBeInTheDocument();
-
- expect(tableTester.findRow({rowIndexOrText: 'Apples'})).toBeInTheDocument();
- expect(onCancel).toHaveBeenCalled();
- });
-
- it('should be cancellable through Escape key', async () => {
- let onCancel = jest.fn();
- let {getByRole} = render(
-
- );
-
- let tableTester = testUtilUser.createTester('Table', {root: getByRole('grid')});
- await user.tab();
- await user.keyboard('{ArrowRight}');
- await user.keyboard('{Enter}');
-
- let dialog = getByRole('dialog');
- expect(dialog).toBeVisible();
-
- let input = within(dialog).getByRole('textbox');
- expect(input).toHaveFocus();
-
- await user.keyboard(' Crisp');
- await user.keyboard('{Escape}');
-
- act(() => {jest.runAllTimers();});
-
- expect(dialog).not.toBeInTheDocument();
- expect(tableTester.findRow({rowIndexOrText: 'Apples'})).toBeInTheDocument();
- expect(onCancel).toHaveBeenCalled();
- });
- });
-
- describe('pointer', () => {
- installPointerEvent();
-
- it('should edit text in a cell', async () => {
- let {getByRole} = render(
-
- );
-
- let tableTester = testUtilUser.createTester('Table', {root: getByRole('grid')});
- await user.click(within(tableTester.findCell({text: 'Apples'})).getByRole('button'));
-
- let dialog = getByRole('dialog');
- expect(dialog).toBeVisible();
-
- await user.click(within(dialog).getByRole('textbox'));
- await user.keyboard(' Crisp');
- await user.click(document.body);
-
- act(() => {jest.runAllTimers();});
-
- expect(dialog).not.toBeInTheDocument();
- expect(tableTester.findRow({rowIndexOrText: 'Apples Crisp'})).toBeInTheDocument();
- });
- });
-
- describe('pending', () => {
- it('should display a pending state when editing a cell', async () => {
- let {getByRole} = render(
-
- );
-
- let tableTester = testUtilUser.createTester('Table', {root: getByRole('grid')});
- await user.tab();
- await user.keyboard('{ArrowRight}');
- await user.keyboard('{Enter}');
-
- let dialog = getByRole('dialog');
- expect(dialog).toBeVisible();
-
- let input = within(dialog).getByRole('textbox');
- expect(input).toHaveFocus();
-
- await user.keyboard('Apples Crisp');
- await user.keyboard('{Enter}'); // implicitly submit through form
-
- act(() => {jest.advanceTimersByTime(5000);});
-
- expect(dialog).not.toBeInTheDocument();
- expect(tableTester.findRow({rowIndexOrText: 'Apples Crisp'})).toBeInTheDocument();
- let button = within(tableTester.findCell({text: 'Apples Crisp'})).getByRole('button');
- expect(button).toHaveAttribute('aria-disabled', 'true');
- expect(button).toHaveFocus();
-
- act(() => {jest.runAllTimers();});
-
- expect(button).not.toHaveAttribute('aria-disabled');
- expect(button).toHaveFocus();
- });
-
- it('should allow tabbing off a pending button', async () => {
- let {getByRole} = render(
-
- );
-
- let tableTester = testUtilUser.createTester('Table', {root: getByRole('grid')});
- await user.tab();
- await user.keyboard('{ArrowRight}');
- await user.keyboard('{Enter}');
-
- let dialog = getByRole('dialog');
- expect(dialog).toBeVisible();
-
- let input = within(dialog).getByRole('textbox');
- expect(input).toHaveFocus();
-
- await user.keyboard('Apples Crisp');
- await user.keyboard('{Enter}'); // implicitly submit through form
-
- act(() => {jest.advanceTimersByTime(5000);});
-
- expect(dialog).not.toBeInTheDocument();
- expect(tableTester.findRow({rowIndexOrText: 'Apples Crisp'})).toBeInTheDocument();
- let button = within(tableTester.findCell({text: 'Apples Crisp'})).getByRole('button');
- expect(button).toHaveAttribute('aria-disabled', 'true');
- expect(button).toHaveFocus();
-
- await user.tab();
- expect(getByRole('button', {name: 'After'})).toHaveFocus();
-
- act(() => {jest.runAllTimers();});
-
- expect(button).not.toHaveAttribute('aria-disabled');
- });
- });
- });
- }
-});
diff --git a/packages/@react-spectrum/s2/test/Form.test.tsx b/packages/@react-spectrum/s2/test/Form.test.tsx
new file mode 100644
index 00000000000..8f12f960552
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/Form.test.tsx
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {describe, expect, it} from 'vitest';
+import {Form} from '../src';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('Form', () => {
+ it('renders', async () => {
+ const screen = await render(
+
+ );
+ expect(screen.container.firstChild).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/FullscreenDialog.test.tsx b/packages/@react-spectrum/s2/test/FullscreenDialog.test.tsx
new file mode 100644
index 00000000000..c35e7233a1d
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/FullscreenDialog.test.tsx
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {ActionButton, DialogTrigger, FullscreenDialog} from '../src';
+import {describe, expect, it, vi} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('FullscreenDialog', () => {
+ it('renders', async () => {
+ vi.useFakeTimers();
+ const screen = await render(
+
+ Open dialog
+
+ Content
+
+
+ );
+ vi.runAllTimers();
+ expect(screen.getByRole('dialog')).toBeInTheDocument();
+ vi.useRealTimers();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/IllustratedMessage.test.tsx b/packages/@react-spectrum/s2/test/IllustratedMessage.test.tsx
new file mode 100644
index 00000000000..38a8af7df85
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/IllustratedMessage.test.tsx
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {Button, ButtonGroup, Content, Heading, IllustratedMessage} from '../src';
+import {describe, expect, it} from 'vitest';
+import Image from '@react-spectrum/s2/illustrations/gradient/generic1/Image';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('IllustratedMessage', () => {
+ it('renders', async () => {
+ const screen = await render(
+
+
+ Create your first asset.
+ Get started by uploading or importing some assets.
+
+ Import
+ Upload
+
+
+ );
+ expect(screen.container.firstChild).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/Image.test.tsx b/packages/@react-spectrum/s2/test/Image.test.tsx
index 014a0f01cbf..5e9a769639f 100644
--- a/packages/@react-spectrum/s2/test/Image.test.tsx
+++ b/packages/@react-spectrum/s2/test/Image.test.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2025 Adobe. All rights reserved.
+ * Copyright 2026 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
@@ -10,56 +10,17 @@
* governing permissions and limitations under the License.
*/
-import {Image, Provider} from '../src';
-import {render} from '@react-spectrum/test-utils-internal';
+import {describe, expect, it} from 'vitest';
+import {Image} from '../src';
+import {render} from './utils/render';
-describe('Image', () => {
- it('should support conditional sources', async () => {
- let {getByRole} = render(
- = 500px)'},
- {srcSet: 'default.png'}
- ]} />
- );
-
- let img = getByRole('img');
- let picture = img.parentElement!;
- expect(picture.tagName).toBe('PICTURE');
- let sources = picture.querySelectorAll('source');
-
- expect(sources).toHaveLength(3);
- expect(sources[0]).toHaveAttribute('srcset', 'foo.png');
- expect(sources[0]).toHaveAttribute('type', 'image/png');
- expect(sources[0]).toHaveAttribute('media', '(prefers-color-scheme: light)');
- expect(sources[1]).toHaveAttribute('srcset', 'bar.png');
- expect(sources[1]).toHaveAttribute('media', '(width >= 500px) and (prefers-color-scheme: dark)');
- expect(sources[2]).toHaveAttribute('srcset', 'default.png');
- });
+const imageSrc = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAIAAADTED8xAAADMElEQVR4nOzVwQnAIBQFQYXff81RUkQCOyDj1YOPnbXWPmeTRef+/3O/OyBjzh3CD95BfqICMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMO0TAAD//2Anhf4QtqobAAAAAElFTkSuQmCC';
- it('should support conditional sources with Provider colorScheme override', async () => {
- let {getByRole} = render(
-
- = 500px)'},
- {srcSet: 'default.png'}
- ]} />
-
+describe('Image', () => {
+ it('renders', async () => {
+ const screen = await render(
+
);
-
- let img = getByRole('img');
- let picture = img.parentElement!;
- expect(picture.tagName).toBe('PICTURE');
- let sources = picture.querySelectorAll('source');
-
- expect(sources).toHaveLength(2);
- expect(sources[0]).toHaveAttribute('srcset', 'bar.png');
- expect(sources[0]).toHaveAttribute('media', '(width >= 500px)');
- expect(sources[1]).toHaveAttribute('srcset', 'default.png');
+ expect(screen.getByAltText('Test')).toBeInTheDocument();
});
});
diff --git a/packages/@react-spectrum/s2/test/ImageCoordinator.test.tsx b/packages/@react-spectrum/s2/test/ImageCoordinator.test.tsx
new file mode 100644
index 00000000000..4e4b40d5125
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/ImageCoordinator.test.tsx
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {describe, expect, it} from 'vitest';
+import {Image, ImageCoordinator} from '../src';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('ImageCoordinator', () => {
+ it('renders', async () => {
+ const screen = await render(
+
+
+
+ );
+ expect(screen.container.firstChild).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/InlineAlert.test.tsx b/packages/@react-spectrum/s2/test/InlineAlert.test.tsx
new file mode 100644
index 00000000000..f927db8d66e
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/InlineAlert.test.tsx
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {Content, Heading, InlineAlert} from '../src';
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('InlineAlert', () => {
+ it('renders', async () => {
+ const screen = await render(
+
+ Payment Information
+ Enter your billing address, shipping address, and payment method to complete your purchase.
+
+ );
+ expect(screen.getByRole('alert')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/Link.test.tsx b/packages/@react-spectrum/s2/test/Link.test.tsx
new file mode 100644
index 00000000000..9cc135a3db2
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/Link.test.tsx
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {describe, expect, it} from 'vitest';
+import {Link} from '../src';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('Link', () => {
+ it('renders', async () => {
+ const screen = await render(
+ Link
+ );
+ expect(screen.getByRole('link')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/LinkButton.test.tsx b/packages/@react-spectrum/s2/test/LinkButton.test.tsx
new file mode 100644
index 00000000000..8a2bd6e7857
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/LinkButton.test.tsx
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {describe, expect, it} from 'vitest';
+import {LinkButton} from '../src';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('LinkButton', () => {
+ it('renders', async () => {
+ const screen = await render(Link );
+ expect(screen.getByRole('link')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/Menu.test.tsx b/packages/@react-spectrum/s2/test/Menu.test.tsx
index 32bfae1ee75..885148d1d7a 100644
--- a/packages/@react-spectrum/s2/test/Menu.test.tsx
+++ b/packages/@react-spectrum/s2/test/Menu.test.tsx
@@ -10,181 +10,33 @@
* governing permissions and limitations under the License.
*/
-import {AriaMenuTests} from '../../../react-aria-components/test/AriaMenu.test-util';
-import {Button, Collection, Header, Heading, Menu, MenuItem, MenuSection, MenuTrigger, SubmenuTrigger} from '../src';
+import {Button, Menu, MenuItem, MenuSection, MenuTrigger} from '../src';
+import {describe, expect, it, vi} from 'vitest';
import React from 'react';
-import {render} from '@react-spectrum/test-utils-internal';
-import {Selection} from '@react-types/shared';
-
-// better to accept items from the test? or just have the test have a requirement that you render a certain-ish structure?
-// what about the button label?
-// where and how can i define the requirements/assumptions for setup for the test?
-let withSection = [
- {id: 'heading 1', name: 'Heading 1', children: [
- {id: 'foo', name: 'Foo'},
- {id: 'bar', name: 'Bar'},
- {id: 'baz', name: 'Baz'}
- ]}
-];
-
-let items = [
- {id: 'foo', name: 'Foo'},
- {id: 'bar', name: 'Bar'},
- {id: 'baz', name: 'Baz'}
-];
-
-function SelectionStatic(props) {
- let {selectionMode = 'single'} = props;
- let [selected, setSelected] = React.useState(new Set());
- return (
-
- Menu Button
-
-
-
- Foo
- Bar
- Baz
- Fizz
-
-
-
- );
-}
-
-AriaMenuTests({
- prefix: 'spectrum2-static',
- renderers: {
- standard: () => render(
-
- Menu Button
-
+import {render} from './utils/render';
+
+describe('Menu', () => {
+ it('renders', async () => {
+ vi.useFakeTimers();
+ const screen = await render(
+
+ Publish
+
-
- Foo
- Bar
- Baz
+ alert('Quick export')}>
+ Quick Export
+
+ Open a copy
-
-
- ),
- disabledTrigger: () => render(
-
- Menu Button
-
-
-
- Foo
- Bar
- Baz
-
-
-
- ),
- singleSelection: () => render(
-
- ),
- multipleSelection: () => render(
-
- ),
- submenus: () => render(
-
- Menu Button
-
-
-
-
- Open
- Rename…
- Duplicate
-
- Share…
-
-
-
- Email…
-
- Share…
-
-
-
- Work
- Personal
-
-
-
- SMS
- X
-
-
-
+
+ Show files
+ Show folders
- )
- }
-});
-
-AriaMenuTests({
- prefix: 'spectrum2-dynamic',
- renderers: {
- standard: () => render(
-
- Menu Button
-
- {(section) => {
- return (
-
-
-
- {item => {
- return {item.name} ;
- }}
-
-
- );
- }}
-
-
- ),
- disabledTrigger: () => render(
-
- Menu Button
-
- {(section) => {
- return (
-
-
-
- {item => {
- return {item.name} ;
- }}
-
-
- );
- }}
-
-
- )
- }
-});
-
-AriaMenuTests({
- prefix: 'spectrum2-dynamic-no-section',
- renderers: {
- standard: () => render(
-
- Menu Button
-
- {(item) =>
- {item.name}
- }
-
-
- )
- }
+ );
+ vi.runAllTimers();
+ expect(screen.getByRole('menu')).toBeInTheDocument();
+ vi.useRealTimers();
+ });
});
diff --git a/packages/@react-spectrum/s2/test/Meter.test.tsx b/packages/@react-spectrum/s2/test/Meter.test.tsx
new file mode 100644
index 00000000000..ab6588a19bb
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/Meter.test.tsx
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {describe, expect, it} from 'vitest';
+import {Meter} from '../src';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('Meter', () => {
+ it('renders', async () => {
+ const screen = await render( );
+ expect(screen.getByRole('meter')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/NotificationBadge.test.tsx b/packages/@react-spectrum/s2/test/NotificationBadge.test.tsx
new file mode 100644
index 00000000000..d96b7ee3f80
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/NotificationBadge.test.tsx
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {describe, expect, it} from 'vitest';
+import {NotificationBadge} from '../src';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('NotificationBadge', () => {
+ it('renders', async () => {
+ const screen = await render( );
+ expect(screen.container.firstChild).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/NumberField.test.tsx b/packages/@react-spectrum/s2/test/NumberField.test.tsx
new file mode 100644
index 00000000000..fbce2536077
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/NumberField.test.tsx
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {describe, expect, it} from 'vitest';
+import {NumberField} from '../src';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('NumberField', () => {
+ it('renders', async () => {
+ const screen = await render(
+
+ );
+ expect(screen.getByRole('textbox')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/Picker.test.tsx b/packages/@react-spectrum/s2/test/Picker.test.tsx
index cc6769f7475..9dd273be3e9 100644
--- a/packages/@react-spectrum/s2/test/Picker.test.tsx
+++ b/packages/@react-spectrum/s2/test/Picker.test.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2025 Adobe. All rights reserved.
+ * Copyright 2026 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
@@ -9,76 +9,16 @@
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
-
-import {act, render, setupIntersectionObserverMock} from '@react-spectrum/test-utils-internal';
-import {Content, ContextualHelp, Heading, Picker, PickerItem, Text} from '../src';
-import {pointerMap, User} from '@react-aria/test-utils';
+import {describe, expect, it, vi} from 'vitest';
+import {Picker, PickerItem} from '../src';
import React from 'react';
-import userEvent from '@testing-library/user-event';
+import {render} from './utils/render';
describe('Picker', () => {
- let testUtilUser = new User();
- function DynamicPicker(props) {
- let {items, loadingState, onLoadMore, ...otherProps} = props;
- return (
-
- {(item: any) => {item.name} }
-
- );
- }
-
- beforeAll(function () {
- jest.useFakeTimers();
- jest.spyOn(window.HTMLElement.prototype, 'clientWidth', 'get').mockImplementation(() => 100);
- jest.spyOn(window.HTMLElement.prototype, 'clientHeight', 'get').mockImplementation(() => 100);
- jest.spyOn(window.HTMLElement.prototype, 'scrollHeight', 'get').mockImplementation(() => 50);
- });
-
- afterEach(() => {
- jest.clearAllMocks();
- act(() => jest.runAllTimers());
- });
-
- afterAll(function () {
- jest.restoreAllMocks();
- });
-
- it('should only call loadMore whenever intersection is detected', async () => {
- let onLoadMore = jest.fn();
- let observe = jest.fn();
- let observer = setupIntersectionObserverMock({
- observe
- });
-
- let tree = render(
-
- Chocolate
- Mint
- Strawberry
- Vanilla
- Chocolate Chip Cookie Dough
-
- );
-
- let selectTester = testUtilUser.createTester('Select', {root: tree.container});
- expect(selectTester.listbox).toBeFalsy();
- selectTester.setInteractionType('mouse');
- await selectTester.open();
-
- expect(onLoadMore).toHaveBeenCalledTimes(0);
- let sentinel = tree.getByTestId('loadMoreSentinel');
- expect(observe).toHaveBeenLastCalledWith(sentinel);
-
- act(() => {observer.instance.triggerCallback([{isIntersecting: true}]);});
- act(() => {jest.runAllTimers();});
-
- tree.rerender(
-
+ it('renders', async () => {
+ vi.useFakeTimers();
+ const screen = await render(
+
Chocolate
Mint
Strawberry
@@ -86,92 +26,8 @@ describe('Picker', () => {
Chocolate Chip Cookie Dough
);
-
- act(() => {observer.instance.triggerCallback([{isIntersecting: true}]);});
- act(() => {jest.runAllTimers();});
- // Note that if this was using useAsyncList, we'd be shielded from extranous onLoadMore calls but
- // we want to leave that to user discretion
- expect(onLoadMore).toHaveBeenCalledTimes(2);
- });
-
- it('should properly calculate the expected row index values', async () => {
- let items = [{name: 'Chocolate'}, {name: 'Mint'}, {name: 'Chocolate Chip'}];
- let tree = render( );
-
- let selectTester = testUtilUser.createTester('Select', {root: tree.container});
- await selectTester.open();
- let options = selectTester.options();
- for (let [index, option] of options.entries()) {
- expect(option).toHaveAttribute('aria-posinset', `${index + 1}`);
- expect(option).toHaveAttribute('aria-setsize', `${items.length}`);
- }
-
- tree.rerender( );
- options = selectTester.options();
- for (let [index, option] of options.entries()) {
- if (index === options.length - 1) {
- // The last row is the loader here which shouldn't have posinset
- expect(option).not.toHaveAttribute('aria-posinset');
- expect(option).not.toHaveAttribute('aria-setsize');
- } else {
- expect(option).toHaveAttribute('aria-posinset', `${index + 1}`);
- expect(option).toHaveAttribute('aria-setsize', `${items.length}`);
- }
- }
-
- let newItems = [...items, {name: 'Chocolate Mint'}, {name: 'Chocolate Chip Cookie Dough'}];
- tree.rerender( );
-
- options = selectTester.options();
- for (let [index, option] of options.entries()) {
- expect(option).toHaveAttribute('aria-posinset', `${index + 1}`);
- expect(option).toHaveAttribute('aria-setsize', `${newItems.length}`);
- }
- });
-
- it('should support contextual help', async () => {
- // Issue with how we don't render the contextual help button in the fake DOM since PressResponder isn't using createHideableComponent
- let warn = jest.spyOn(global.console, 'warn').mockImplementation();
- let user = userEvent.setup({delay: null, pointerMap});
- let tree = render(
-
- Title here
-
-
- Contents
-
-
-
- }
- label="test">
- Chocolate
- Mint
- Strawberry
- Vanilla
- Chocolate Chip Cookie Dough
-
- );
-
- let selectTester = testUtilUser.createTester('Select', {root: tree.getByTestId('testpicker')});
- let buttons = tree.getAllByRole('button');
- expect(buttons).toHaveLength(2);
- expect(buttons[1]).toBe(selectTester.trigger);
-
- await user.click(buttons[0]);
-
- act(() => {
- jest.runAllTimers();
- });
-
- let dialog = tree.getByRole('dialog');
- expect(dialog).toBeVisible();
-
- // Because of the fake DOM we'll see this twice
- expect(tree.getAllByText('Title here')[1]).toBeVisible();
- expect(tree.getAllByText('Contents')[1]).toBeVisible();
- warn.mockRestore();
+ vi.runAllTimers();
+ expect(screen.getByRole('button')).toBeInTheDocument();
+ vi.useRealTimers();
});
});
diff --git a/packages/@react-spectrum/s2/test/Popover.test.tsx b/packages/@react-spectrum/s2/test/Popover.test.tsx
new file mode 100644
index 00000000000..11e39f47887
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/Popover.test.tsx
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {
+ ActionButton,
+ Button,
+ DialogTrigger,
+ Form,
+ Popover,
+ Switch,
+ TextField
+} from '../src';
+import {describe, expect, it, vi} from 'vitest';
+import Feedback from '@react-spectrum/s2/icons/Feedback';
+import React from 'react';
+import {render} from './utils';
+
+describe('Popover', () => {
+ it('renders', async () => {
+ vi.useFakeTimers();
+ const screen = await render(
+
+
+
+
+
+
+
How are we doing? Share your feedback here.
+
+
+
+
+ );
+ vi.runAllTimers();
+ expect(screen.getByRole('dialog')).toBeInTheDocument();
+ vi.useRealTimers();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/ProgressBar.test.tsx b/packages/@react-spectrum/s2/test/ProgressBar.test.tsx
new file mode 100644
index 00000000000..b484ef71578
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/ProgressBar.test.tsx
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {describe, expect, it} from 'vitest';
+import {ProgressBar} from '../src';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('ProgressBar', () => {
+ it('renders', async () => {
+ const screen = await render( );
+ expect(screen.getByRole('progressbar')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/ProgressCircle.test.tsx b/packages/@react-spectrum/s2/test/ProgressCircle.test.tsx
new file mode 100644
index 00000000000..c8e524bb6a4
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/ProgressCircle.test.tsx
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {describe, expect, it} from 'vitest';
+import {ProgressCircle} from '../src';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('ProgressCircle', () => {
+ it('renders', async () => {
+ const screen = await render( );
+ expect(screen.getByRole('progressbar')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/Provider.test.tsx b/packages/@react-spectrum/s2/test/Provider.test.tsx
new file mode 100644
index 00000000000..d030b905351
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/Provider.test.tsx
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {describe, expect, it} from 'vitest';
+import {Provider} from '../src';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('Provider', () => {
+ it('renders', async () => {
+ const screen = await render(
+
+ Hello React Spectrum!
+
+ );
+ expect(screen.container.firstChild).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/RadioGroup.test.tsx b/packages/@react-spectrum/s2/test/RadioGroup.test.tsx
index 1868fe10e7e..9983844465f 100644
--- a/packages/@react-spectrum/s2/test/RadioGroup.test.tsx
+++ b/packages/@react-spectrum/s2/test/RadioGroup.test.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2025 Adobe. All rights reserved.
+ * Copyright 2026 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
@@ -10,68 +10,20 @@
* governing permissions and limitations under the License.
*/
-jest.mock('@react-aria/live-announcer');
-import {Direction} from '@react-types/shared';
-import {pointerMap, render, User} from '@react-spectrum/test-utils-internal';
-import {Provider, Radio, RadioGroup} from '../src';
+import {describe, expect, it} from 'vitest';
+import {Radio, RadioGroup} from '../src';
import React from 'react';
-import userEvent from '@testing-library/user-event';
+import {render} from './utils/render';
describe('RadioGroup', () => {
- let testUtilUser = new User();
- let user;
- beforeAll(() => {
- user = userEvent.setup({delay: null, pointerMap});
- });
-
- it.each`
- Name | props
- ${'ltr + vertical'} | ${{locale: 'de-DE', orientation: 'vertical'}}
- ${'rtl + verfical'} | ${{locale: 'ar-AE', orientation: 'vertical'}}
- ${'ltr + horizontal'} | ${{locale: 'de-DE', orientation: 'horizontal'}}
- ${'rtl + horizontal'} | ${{locale: 'ar-AE', orientation: 'horizontal'}}
- `('$Name should select the correct radio via keyboard regardless of orientation and disabled radios', async function ({props}) {
- let {getByRole} = render(
-
-
- Dogs
- Cats
- Dragons
- Unicorns
- Chocobo
-
-
+ it('renders', async () => {
+ const screen = await render(
+
+ Cat
+ Dog
+ Dragon
+
);
- let direction = props.locale === 'ar-AE' ? 'rtl' : 'ltr' as Direction;
- let radioGroupTester = testUtilUser.createTester('RadioGroup', {root: getByRole('radiogroup'), direction});
- expect(radioGroupTester.radiogroup).toHaveAttribute('aria-orientation', props.orientation);
- let radios = radioGroupTester.radios;
- await radioGroupTester.triggerRadio({radio: radios[0]});
- expect(radios[0]).toBeChecked();
-
- await radioGroupTester.triggerRadio({radio: 4, interactionType: 'keyboard'});
- expect(radios[4]).toBeChecked();
-
- let radio4 = radioGroupTester.findRadio({radioIndexOrText: 3});
- await radioGroupTester.triggerRadio({radio: radio4, interactionType: 'keyboard'});
- expect(radios[3]).toBeChecked();
-
- await radioGroupTester.triggerRadio({radio: 'Dogs', interactionType: 'mouse'});
- expect(radios[0]).toBeChecked();
-
- let radio5 = radioGroupTester.findRadio({radioIndexOrText: 'Chocobo'});
- await radioGroupTester.triggerRadio({radio: radio5, interactionType: 'mouse'});
- expect(radios[4]).toBeChecked();
-
- // This isn't using the radioGroup tester because the tester uses the attached aria-orientation to determine
- // what arrow to press, which won't reproduce the original bug where we forgot to pass the orientation to the RadioGroup.
- // The tester ends up still pressing the correct keys (ArrowUp/ArrowDown) to navigate properly even in the horizontal orientation
- // instead of using ArrowLeft/ArrowRight
- await user.keyboard('[ArrowLeft]');
- if (props.locale === 'ar-AE' && props.orientation === 'horizontal') {
- expect(radioGroupTester.selectedRadio).toBe(radios[0]);
- } else {
- expect(radioGroupTester.selectedRadio).toBe(radios[3]);
- }
+ expect(screen.getByRole('radiogroup')).toBeInTheDocument();
});
});
diff --git a/packages/@react-spectrum/s2/test/RangeCalendar.test.tsx b/packages/@react-spectrum/s2/test/RangeCalendar.test.tsx
new file mode 100644
index 00000000000..8cc26883cad
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/RangeCalendar.test.tsx
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {describe, expect, it} from 'vitest';
+import {RangeCalendar} from '../src';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('RangeCalendar', () => {
+ it('renders', async () => {
+ const screen = await render(
+
+ );
+ expect(screen.getByRole('grid')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/RangeSlider.test.tsx b/packages/@react-spectrum/s2/test/RangeSlider.test.tsx
new file mode 100644
index 00000000000..c7e86d652e4
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/RangeSlider.test.tsx
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {describe, expect, it} from 'vitest';
+import {RangeSlider} from '../src';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('RangeSlider', () => {
+ it('renders', async () => {
+ const screen = await render(
+
+ );
+ // Check for sliders using input[type="range"] since RangeSlider uses native range inputs
+ const sliders = screen.container.querySelectorAll('input[type="range"]');
+ expect(sliders.length).toBe(2);
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/SearchField.test.tsx b/packages/@react-spectrum/s2/test/SearchField.test.tsx
index a8ccddc2ae3..6ac8a9c471b 100644
--- a/packages/@react-spectrum/s2/test/SearchField.test.tsx
+++ b/packages/@react-spectrum/s2/test/SearchField.test.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2025 Adobe. All rights reserved.
+ * Copyright 2026 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
@@ -10,35 +10,18 @@
* governing permissions and limitations under the License.
*/
-import {Autocomplete} from 'react-aria-components';
-import {Menu, MenuItem, SearchField} from '../src';
-import {pointerMap, render} from '@react-spectrum/test-utils-internal';
+import {describe, expect, it} from 'vitest';
import React from 'react';
-import userEvent from '@testing-library/user-event';
+import {render} from './utils/render';
+import {SearchField} from '../src';
describe('SearchField', () => {
- let user;
- beforeAll(() => {
- user = userEvent.setup({delay: null, pointerMap});
- });
-
- it('should not apply the focus visible styles on the group when typing in the Autocomplete wrapped SearchField', async () => {
- let {getByRole} = render(
-
-
-
- Foo
- Bar
- Baz
-
-
+ it('renders', async () => {
+ const screen = await render(
+
);
-
- let input = getByRole('searchbox');
- await user.click(input);
- let group = getByRole('group');
- expect(group).not.toHaveAttribute('data-focus-visible');
- await user.keyboard('Foo');
- expect(group).not.toHaveAttribute('data-focus-visible');
+ expect(screen.getByRole('searchbox')).toBeInTheDocument();
});
});
diff --git a/packages/@react-spectrum/s2/test/SegmentedControl.test.tsx b/packages/@react-spectrum/s2/test/SegmentedControl.test.tsx
new file mode 100644
index 00000000000..8f5c0d591bd
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/SegmentedControl.test.tsx
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+import {SegmentedControl, SegmentedControlItem} from '../src';
+
+describe('SegmentedControl', () => {
+ it('renders', async () => {
+ const screen = await render(
+
+ Day
+ Week
+ Month
+ Year
+
+ );
+ const radios = screen.container.querySelectorAll('[role="radio"]');
+ expect(radios.length).toBe(4);
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/SelectBoxGroup.test.tsx b/packages/@react-spectrum/s2/test/SelectBoxGroup.test.tsx
index 8fd18a33e3e..9e10b272aa7 100644
--- a/packages/@react-spectrum/s2/test/SelectBoxGroup.test.tsx
+++ b/packages/@react-spectrum/s2/test/SelectBoxGroup.test.tsx
@@ -1,913 +1,38 @@
-import {act, pointerMap, render, screen, waitFor} from '@react-spectrum/test-utils-internal';
-import Calendar from '../spectrum-illustrations/linear/Calendar';
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {describe, expect, it} from 'vitest';
import React from 'react';
+import {render} from './utils/render';
import {SelectBox, SelectBoxGroup, Text} from '../src';
-import {Selection} from '@react-types/shared';
-import {User} from '@react-aria/test-utils';
-import userEvent from '@testing-library/user-event';
-
-function ControlledSingleSelectBox() {
- const [selectedKeys, setSelectedKeys] = React.useState(new Set());
- return (
-
-
- Option 1
-
-
- Option 2
-
-
- Option 3
-
-
- );
-}
-
-function ControlledMultiSelectBox() {
- const [selectedKeys, setSelectedKeys] = React.useState(new Set());
- return (
-
-
- Option 1
-
-
- Option 2
-
-
- Option 3
-
-
- );
-}
-
-function UncontrolledSelectBox({selectionMode = 'single'}: {selectionMode?: 'single' | 'multiple'}) {
- return (
-
-
- Option 1
-
-
- Option 2
-
-
- Option 3
-
-
- );
-}
-
-function DisabledSelectBox() {
- return (
- {}}
- selectedKeys={new Set()}
- isDisabled>
-
- Option 1
-
-
- Option 2
-
-
- );
-}
describe('SelectBoxGroup', () => {
- let user;
- let testUtilUser = new User();
-
- beforeAll(function () {
- jest.useFakeTimers();
- user = userEvent.setup({delay: null, pointerMap});
- });
-
- afterEach(() => {
- jest.clearAllMocks();
- act(() => jest.runAllTimers());
- });
-
- afterAll(function () {
- jest.restoreAllMocks();
- });
-
- describe('Basic functionality', () => {
- it('renders as a listbox with options', () => {
- render( );
- expect(screen.getByRole('listbox')).toBeInTheDocument();
- expect(screen.getAllByRole('option')).toHaveLength(3);
- expect(screen.getByText('Option 1')).toBeInTheDocument();
- });
-
- it('renders multiple selection mode', () => {
- render( );
- expect(screen.getByRole('listbox')).toBeInTheDocument();
- expect(screen.getAllByRole('option')).toHaveLength(3);
- expect(screen.getByText('Option 1')).toBeInTheDocument();
- });
- });
-
- describe('Uncontrolled behavior', () => {
- it('handles uncontrolled click selection in single mode', async () => {
- render( );
- let listboxTester = testUtilUser.createTester('ListBox', {root: screen.getByRole('listbox')});
-
- await listboxTester.toggleOptionSelection({option: 0});
- expect(listboxTester.options()[0]).toHaveAttribute('aria-selected', 'true');
- });
-
- it('handles uncontrolled click selection in multiple mode', async () => {
- render( );
- let listboxTester = testUtilUser.createTester('ListBox', {root: screen.getByRole('listbox')});
-
- await listboxTester.toggleOptionSelection({option: 0});
- await listboxTester.toggleOptionSelection({option: 1});
-
- expect(listboxTester.options()[0]).toHaveAttribute('aria-selected', 'true');
- expect(listboxTester.options()[1]).toHaveAttribute('aria-selected', 'true');
- });
-
- it('handles uncontrolled selection toggle', async () => {
- render( );
- let listboxTester = testUtilUser.createTester('ListBox', {root: screen.getByRole('listbox')});
-
- await listboxTester.toggleOptionSelection({option: 0});
- expect(listboxTester.options()[0]).toHaveAttribute('aria-selected', 'true');
-
- // Toggle off in single mode by selecting another
- await listboxTester.toggleOptionSelection({option: 1});
- expect(listboxTester.options()[0]).toHaveAttribute('aria-selected', 'false');
- expect(listboxTester.options()[1]).toHaveAttribute('aria-selected', 'true');
- });
-
- it('handles uncontrolled keyboard selection', async () => {
- render( );
- await user.tab();
-
- await act(async () => {
- await user.keyboard(' ');
- });
-
- const option1 = screen.getByRole('option', {name: 'Option 1'});
- expect(option1).toHaveAttribute('aria-selected', 'true');
- });
- });
-
- describe('Controlled behavior', () => {
- it('handles controlled selection in single mode', async () => {
- render( );
- let listboxTester = testUtilUser.createTester('ListBox', {root: screen.getByRole('listbox')});
-
- await listboxTester.toggleOptionSelection({option: 0});
- expect(listboxTester.options()[0]).toHaveAttribute('aria-selected', 'true');
- });
-
- it('handles controlled multiple selection', async () => {
- render( );
- let listboxTester = testUtilUser.createTester('ListBox', {root: screen.getByRole('listbox')});
-
- await listboxTester.toggleOptionSelection({option: 0});
- await listboxTester.toggleOptionSelection({option: 1});
-
- expect(listboxTester.options()[0]).toHaveAttribute('aria-selected', 'true');
- expect(listboxTester.options()[1]).toHaveAttribute('aria-selected', 'true');
- });
-
- it('calls onSelectionChange when selection changes in controlled mode', async () => {
- const onSelectionChange = jest.fn();
- const TestComponent = () => {
- const [selectedKeys, setSelectedKeys] = React.useState(new Set());
- return (
- {
- setSelectedKeys(keys);
- onSelectionChange(keys);
- }}
- selectedKeys={selectedKeys}>
-
- Option 1
-
-
- Option 2
-
-
- );
- };
-
- render( );
- const option1 = screen.getByRole('option', {name: 'Option 1'});
- await user.click(option1);
-
- expect(onSelectionChange).toHaveBeenCalledTimes(1);
- const receivedSelection = onSelectionChange.mock.calls[0][0];
- expect(Array.from(receivedSelection)).toEqual(['option1']);
- });
-
- it('calls onSelectionChange with Set for multiple selection in controlled mode', async () => {
- const onSelectionChange = jest.fn();
- const TestComponent = () => {
- const [selectedKeys, setSelectedKeys] = React.useState(new Set());
- return (
- {
- setSelectedKeys(keys);
- onSelectionChange(keys);
- }}
- selectedKeys={selectedKeys}>
-
- Option 1
-
-
- Option 2
-
-
- );
- };
-
- render( );
- const option1 = screen.getByRole('option', {name: 'Option 1'});
- await user.click(option1);
-
- expect(onSelectionChange).toHaveBeenCalledTimes(1);
- const receivedSelection = onSelectionChange.mock.calls[0][0];
- expect(Array.from(receivedSelection)).toEqual(['option1']);
- });
- });
-
- describe('Disabled behavior', () => {
- it('renders disabled state correctly', () => {
- render( );
- const listbox = screen.getByRole('listbox');
- expect(listbox).toBeInTheDocument();
-
- const options = screen.getAllByRole('option');
- expect(options.length).toBeGreaterThan(0);
- });
-
- it('prevents interaction when group is disabled', async () => {
- const onSelectionChange = jest.fn();
- render(
-
-
- Option 1
-
-
- Option 2
-
-
- );
-
- const option1 = screen.getByRole('option', {name: 'Option 1'});
- const option2 = screen.getByRole('option', {name: 'Option 2'});
- await user.click(option1);
- await user.click(option2);
- expect(onSelectionChange).not.toHaveBeenCalled();
-
- expect(option1).toHaveAttribute('aria-disabled', 'true');
- expect(option2).toHaveAttribute('aria-disabled', 'true');
- });
-
- it('prevents uncontrolled interaction when group is disabled', async () => {
- render(
-
-
- Option 1
-
-
- Option 2
-
-
- );
-
- const option1 = screen.getByRole('option', {name: 'Option 1'});
- const option2 = screen.getByRole('option', {name: 'Option 2'});
-
- await user.click(option1);
- await user.click(option2);
-
- // should have disabled attributes and no selection
- expect(option1).toHaveAttribute('aria-disabled', 'true');
- expect(option2).toHaveAttribute('aria-disabled', 'true');
- expect(option1).toHaveAttribute('aria-selected', 'false');
- expect(option2).toHaveAttribute('aria-selected', 'false');
- });
- });
-
- describe('Checkbox functionality', () => {
- it('shows checkbox when item is selected in controlled mode', async () => {
- render(
- {}}
- selectedKeys={new Set(['option1'])}>
-
- Option 1
-
-
- Option 2
-
-
- );
-
- const selectedRow = screen.getByRole('option', {name: 'Option 1'});
- expect(selectedRow).toHaveAttribute('aria-selected', 'true');
-
- const checkboxDiv = selectedRow.querySelector('[aria-hidden="true"]');
- expect(checkboxDiv).toBeInTheDocument();
- });
-
- it('shows checkbox when item is selected in uncontrolled mode', async () => {
- render( );
-
- const option1 = screen.getByRole('option', {name: 'Option 1'});
- await user.click(option1);
-
- expect(option1).toHaveAttribute('aria-selected', 'true');
- const checkboxDiv = option1.querySelector('[aria-hidden="true"]');
- expect(checkboxDiv).toBeInTheDocument();
- });
-
- it('shows checkbox on hover for non-disabled items in controlled mode', async () => {
- render(
- {}}
- selectedKeys={new Set()}>
-
- Option 1
-
-
- );
-
- const row = screen.getByRole('option', {name: 'Option 1'});
-
- await user.hover(row);
- await waitFor(() => {
- const checkboxDiv = row.querySelector('[aria-hidden="true"]');
- expect(checkboxDiv).toBeInTheDocument();
- });
- });
-
- it('shows checkbox on hover for non-disabled items in uncontrolled mode', async () => {
- render( );
-
- const row = screen.getByRole('option', {name: 'Option 1'});
-
- await user.hover(row);
- await waitFor(() => {
- const checkboxDiv = row.querySelector('[aria-hidden="true"]');
- expect(checkboxDiv).toBeInTheDocument();
- });
- });
-
- it('shows checkbox for disabled but selected items', () => {
- render(
- {}}
- defaultSelectedKeys={new Set(['option1'])}>
-
- Option 1
-
-
- );
-
- const row = screen.getByRole('option', {name: 'Option 1'});
-
- const checkboxDiv = row.querySelector('[aria-hidden="true"]');
- expect(checkboxDiv).toBeInTheDocument();
- });
-
- it('shows checkbox for disabled items (always show checkboxes)', async () => {
- render(
- {}}
- selectedKeys={new Set()}>
-
- Option 1
-
-
- );
-
- const row = screen.getByRole('option', {name: 'Option 1'});
-
- // checkbox always present
- const checkboxDiv = row.querySelector('[aria-hidden="true"]');
- expect(checkboxDiv).toBeInTheDocument();
- });
- });
-
- describe('Props and configuration', () => {
- it('supports different orientations', () => {
- render(
- {}}
- selectedKeys={new Set()}
- orientation="horizontal">
-
- Option 1
-
-
- );
- expect(screen.getByRole('listbox')).toBeInTheDocument();
- });
-
- it('auto-fits columns based on orientation (vertical)', () => {
- render(
-
- {}}
- selectedKeys={new Set()}
- orientation="vertical">
-
- Option 1
-
-
- Option 2
-
-
- Option 3
-
-
-
- );
-
- const listbox = screen.getByRole('listbox');
- expect(listbox).toBeInTheDocument();
- });
-
- it('auto-fits columns based on orientation (horizontal)', () => {
- render(
-
- {}}
- selectedKeys={new Set()}
- orientation="horizontal">
-
- Option 1
-
-
- Option 2
-
-
-
- );
-
- const listbox = screen.getByRole('listbox');
- expect(listbox).toBeInTheDocument();
- });
- });
-
- describe('Controlled behavior', () => {
- it('handles initial id selection', () => {
- render(
- {}}
- selectedKeys={new Set(['option1'])}>
-
- Option 1
-
-
- Option 2
-
-
- );
-
- const option1 = screen.getByRole('option', {name: 'Option 1'});
- const option2 = screen.getByRole('option', {name: 'Option 2'});
-
- expect(option1).toHaveAttribute('aria-selected', 'true');
- expect(option2).toHaveAttribute('aria-selected', 'false');
- });
-
- it('handles multiple selection with initial ids', () => {
- render(
- {}}
- selectedKeys={new Set(['option1', 'option2'])}>
-
- Option 1
-
-
- Option 2
-
-
- Option 3
-
-
- );
-
- const option1 = screen.getByRole('option', {name: 'Option 1'});
- const option2 = screen.getByRole('option', {name: 'Option 2'});
- const option3 = screen.getByRole('option', {name: 'Option 3'});
-
- expect(option1).toHaveAttribute('aria-selected', 'true');
- expect(option2).toHaveAttribute('aria-selected', 'true');
- expect(option3).toHaveAttribute('aria-selected', 'false');
- });
-
- it('handles uncontrolled selection with defaultSelectedKeys', async () => {
- render(
-
-
- Option 1
-
-
- Option 2
-
-
- );
-
- const option1 = screen.getByRole('option', {name: 'Option 1'});
- const option2 = screen.getByRole('option', {name: 'Option 2'});
-
- expect(option1).toHaveAttribute('aria-selected', 'true');
- expect(option2).toHaveAttribute('aria-selected', 'false');
-
- // click should update selection
- await user.click(option2);
- expect(option1).toHaveAttribute('aria-selected', 'false');
- expect(option2).toHaveAttribute('aria-selected', 'true');
- });
-
- it('handles uncontrolled multiple selection with defaultSelectedKeys', async () => {
- render(
-
-
- Option 1
-
-
- Option 2
-
-
- Option 3
-
-
- );
-
- const option1 = screen.getByRole('option', {name: 'Option 1'});
- const option2 = screen.getByRole('option', {name: 'Option 2'});
- const option3 = screen.getByRole('option', {name: 'Option 3'});
-
- expect(option1).toHaveAttribute('aria-selected', 'true');
- expect(option2).toHaveAttribute('aria-selected', 'true');
- expect(option3).toHaveAttribute('aria-selected', 'false');
-
- await user.click(option3);
- expect(option1).toHaveAttribute('aria-selected', 'true');
- expect(option2).toHaveAttribute('aria-selected', 'true');
- expect(option3).toHaveAttribute('aria-selected', 'true');
-
- // click should remove from selection
- await user.click(option1);
- expect(option1).toHaveAttribute('aria-selected', 'false');
- expect(option2).toHaveAttribute('aria-selected', 'true');
- expect(option3).toHaveAttribute('aria-selected', 'true');
- });
-
- it('handles controlled component updates', async () => {
- function ControlledTest() {
- const [selectedKeys, setSelectedKeys] = React.useState(new Set());
-
- return (
-
- setSelectedKeys(new Set(['option2']))}>
- Select Option 2
-
-
-
- Option 1
-
-
- Option 2
-
-
-
- );
- }
-
- render( );
-
- const button = screen.getByRole('button', {name: 'Select Option 2'});
- await user.click(button);
-
- const option2 = screen.getByRole('option', {name: 'Option 2'});
- expect(option2).toHaveAttribute('aria-selected', 'true');
- });
-
- it('handles "all" selection', () => {
- render(
- {}}
- selectedKeys="all">
-
- Option 1
-
-
- Option 2
-
-
- );
-
- const option1 = screen.getByRole('option', {name: 'Option 1'});
- const option2 = screen.getByRole('option', {name: 'Option 2'});
-
- expect(option1).toHaveAttribute('aria-selected', 'true');
- expect(option2).toHaveAttribute('aria-selected', 'true');
- });
- });
-
- describe('Grid navigation', () => {
- it('supports keyboard navigation and grid layout', async () => {
- render(
- {}}
- selectedKeys={new Set()}>
-
- Option 1
-
-
- Option 2
-
-
- Option 3
-
-
- Option 4
-
-
- );
-
- const listbox = screen.getByRole('listbox');
- const options = screen.getAllByRole('option');
-
- expect(listbox).toBeInTheDocument();
- expect(options).toHaveLength(4);
- });
-
- it('supports space key selection in uncontrolled mode', async () => {
- render( );
- const option1 = screen.getByRole('option', {name: 'Option 1'});
-
- await user.tab();
- await user.keyboard(' ');
-
- expect(option1).toHaveAttribute('aria-selected', 'true');
- });
-
- it('supports arrow key navigation', async () => {
- render(
- {}}
- selectedKeys={new Set()}>
-
- Option 1
-
-
- Option 2
-
-
- );
-
- await user.tab();
-
- await user.keyboard('{ArrowDown}');
-
- // check that navigation works by verifying an option has focus
- const option1 = screen.getByRole('option', {name: 'Option 1'});
- expect(option1).toHaveFocus();
- });
- });
-
- describe('Accessibility', () => {
- it('has proper listbox structure', () => {
- render(
- {}}
- selectedKeys={new Set()}>
-
- Option 1
-
-
- Option 2
-
-
- );
-
- expect(screen.getByRole('listbox')).toBeInTheDocument();
- expect(screen.getAllByRole('option')).toHaveLength(2);
- });
-
- it('supports aria-label and aria-labelledby', () => {
- render(
-
-
My SelectBoxGroup
- {}}
- selectedKeys={new Set()}>
-
- Option 1
-
-
-
- );
-
- const listbox = screen.getByRole('listbox');
- // verify the listbox has an aria-labelledby attribute
- expect(listbox).toHaveAttribute('aria-labelledby');
- expect(listbox.getAttribute('aria-labelledby')).toBeTruthy();
- });
- });
-
- describe('Edge cases', () => {
- it('handles complex children with slots', () => {
- render(
- {}}
- selectedKeys={new Set()}
- orientation="horizontal">
-
-
- Complex Option
- With description
-
-
- );
-
- expect(screen.getByText('Complex Option')).toBeInTheDocument();
- expect(screen.getByText('With description')).toBeInTheDocument();
- });
-
- it('handles different id types', () => {
- render(
- {}}
- selectedKeys={new Set()}>
-
- Option 1
-
-
- Option 2
-
-
- );
-
- const option1 = screen.getByRole('option', {name: 'Option 1'});
- const option2 = screen.getByRole('option', {name: 'Option 2'});
- expect(option1).toBeInTheDocument();
- expect(option2).toBeInTheDocument();
- });
-
- it('handles empty children gracefully', () => {
- render(
- {}}
- selectedKeys={new Set()}>
- {null}
- {undefined}
-
- Valid Option
-
- {false}
-
- );
-
- expect(screen.getByRole('listbox')).toBeInTheDocument();
- expect(screen.getAllByRole('option')).toHaveLength(1);
- expect(screen.getByText('Valid Option')).toBeInTheDocument();
- });
-
- it('handles individual disabled items', () => {
- render(
- {}}
- selectedKeys={new Set()}>
-
- Option 1
-
-
- Option 2
-
-
- );
-
- const rows = screen.getAllByRole('option');
- expect(rows.length).toBe(2);
-
- const option1 = screen.getByRole('option', {name: 'Option 1'});
- expect(option1).toHaveAttribute('aria-disabled', 'true');
- });
-
- it('prevents interaction with individually disabled items', async () => {
- const onSelectionChange = jest.fn();
- render(
-
-
- Option 1
-
-
- );
-
- const option1 = screen.getByRole('option', {name: 'Option 1'});
- await user.click(option1);
-
- expect(onSelectionChange).not.toHaveBeenCalled();
- });
-
- it('prevents uncontrolled interaction with individually disabled items', async () => {
- render(
-
-
- Option 1
-
-
- Option 2
-
-
- );
-
- const option1 = screen.getByRole('option', {name: 'Option 1'});
- const option2 = screen.getByRole('option', {name: 'Option 2'});
-
- await user.click(option1);
- expect(option1).toHaveAttribute('aria-disabled', 'true');
- expect(option1).toHaveAttribute('aria-selected', 'false');
-
- // clicking enabled item should still work
- await user.click(option2);
- expect(option2).toHaveAttribute('aria-selected', 'true');
- });
+ it('renders', async () => {
+ const screen = await render(
+
+
+ Amazon Web Services
+
+
+ Microsoft Azure
+
+
+ Google Cloud Platform
+
+
+ IBM Cloud
+
+
+ );
+ expect(screen.getByRole('listbox')).toBeInTheDocument();
});
});
diff --git a/packages/@react-spectrum/s2/test/Skeleton.test.tsx b/packages/@react-spectrum/s2/test/Skeleton.test.tsx
new file mode 100644
index 00000000000..bdaad4579de
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/Skeleton.test.tsx
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+import {Skeleton, Text} from '../src';
+
+describe('Skeleton', () => {
+ it('renders', async () => {
+ const screen = await render(
+
+
+ Placeholder title
+
+
+ );
+ expect(screen.container.firstChild).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/SkeletonCollection.test.tsx b/packages/@react-spectrum/s2/test/SkeletonCollection.test.tsx
new file mode 100644
index 00000000000..f16c99b6167
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/SkeletonCollection.test.tsx
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {Card, CardView, Collection, SkeletonCollection} from '../src';
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('SkeletonCollection', () => {
+ it('renders', async () => {
+ const screen = await render(
+
+
+ {() => Item }
+
+
+ {() => Loading }
+
+
+ );
+ expect(screen.container.firstChild).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/Slider.test.tsx b/packages/@react-spectrum/s2/test/Slider.test.tsx
new file mode 100644
index 00000000000..21341701a14
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/Slider.test.tsx
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+import {Slider} from '../src';
+
+describe('Slider', () => {
+ it('renders', async () => {
+ const screen = await render( );
+ expect(screen.getByRole('slider')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/StatusLight.test.tsx b/packages/@react-spectrum/s2/test/StatusLight.test.tsx
new file mode 100644
index 00000000000..f97c6f5a469
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/StatusLight.test.tsx
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+import {StatusLight} from '../src';
+
+describe('StatusLight', () => {
+ it('renders', async () => {
+ const screen = await render(Status );
+ expect(screen.container.firstChild).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/Switch.test.tsx b/packages/@react-spectrum/s2/test/Switch.test.tsx
new file mode 100644
index 00000000000..78579ff2b48
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/Switch.test.tsx
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+import {Switch} from '../src';
+
+describe('Switch', () => {
+ it('renders', async () => {
+ const screen = await render(
+
+ Low power mode
+
+ );
+ expect(screen.getByRole('switch')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/TableView.test.tsx b/packages/@react-spectrum/s2/test/TableView.test.tsx
index 5b49c9d40d4..27831d2dfe4 100644
--- a/packages/@react-spectrum/s2/test/TableView.test.tsx
+++ b/packages/@react-spectrum/s2/test/TableView.test.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2025 Adobe. All rights reserved.
+ * Copyright 2026 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
@@ -10,102 +10,51 @@
* governing permissions and limitations under the License.
*/
-jest.mock('@react-aria/live-announcer');
-jest.mock('@react-aria/utils/src/scrollIntoView');
-import {act, render} from '@react-spectrum/test-utils-internal';
import {
Cell,
Column,
- MenuItem,
- MenuSection,
Row,
TableBody,
TableHeader,
- TableView,
- Text
+ TableView
} from '../src';
-import Filter from '../s2wf-icons/S2_Icon_Filter_20_N.svg';
+import {describe, expect, it} from 'vitest';
import React from 'react';
-import {User} from '@react-aria/test-utils';
-
-// @ts-ignore
-window.getComputedStyle = (el) => el.style;
+import {render} from './utils/render';
describe('TableView', () => {
- let offsetWidth, offsetHeight;
- let testUtilUser = new User({advanceTimer: jest.advanceTimersByTime});
- beforeAll(function () {
- offsetWidth = jest.spyOn(window.HTMLElement.prototype, 'clientWidth', 'get').mockImplementation(() => 400);
- offsetHeight = jest.spyOn(window.HTMLElement.prototype, 'clientHeight', 'get').mockImplementation(() => 200);
- jest.useFakeTimers();
- });
-
- afterAll(function () {
- offsetWidth.mockReset();
- offsetHeight.mockReset();
- });
-
- afterEach(() => {
- act(() => {jest.runAllTimers();});
- });
-
- let columns = [
- {name: 'Foo', id: 'foo', isRowHeader: true},
- {name: 'Bar', id: 'bar'},
- {name: 'Baz', id: 'baz'},
- {name: 'Yah', id: 'yah'}
- ];
-
- let items = [
- {id: 1, foo: 'Foo 1', bar: 'Bar 1', baz: 'Baz 1', yah: 'Yah long long long 1'},
- {id: 2, foo: 'Foo 2', bar: 'Bar 2', baz: 'Baz 2', yah: 'Yah long long long 2'},
- {id: 3, foo: 'Foo 3', bar: 'Bar 3', baz: 'Baz 3', yah: 'Yah long long long 3'},
- {id: 4, foo: 'Foo 4', bar: 'Bar 4', baz: 'Baz 4', yah: 'Yah long long long 4'},
- {id: 5, foo: 'Foo 5', bar: 'Bar 5', baz: 'Baz 5', yah: 'Yah long long long 5'},
- {id: 6, foo: 'Foo 6', bar: 'Bar 6', baz: 'Baz 6', yah: 'Yah long long long 6'},
- {id: 7, foo: 'Foo 7', bar: 'Bar 7', baz: 'Baz 7', yah: 'Yah long long long 7'},
- {id: 8, foo: 'Foo 8', bar: 'Bar 8', baz: 'Baz 8', yah: 'Yah long long long 8'},
- {id: 9, foo: 'Foo 9', bar: 'Bar 9', baz: 'Baz 9', yah: 'Yah long long long 9'},
- {id: 10, foo: 'Foo 10', bar: 'Bar 10', baz: 'Baz 10', yah: 'Yah long long long 10'}
- ];
-
- it('should render custom menus', async () => {
- let onAction = jest.fn();
- let {getByRole} = render(
-
-
- {(column) => (
-
-
- Filter
-
-
- Hide column
- Manage columns
-
- >
- }>{column.name}
- )}
+ it('renders', async () => {
+ const screen = await render(
+
+
+ Name
+ Type
+ Date Modified
-
- {item => (
-
- {(column) => {
- return | {item[column.id]} | ;
- }}
-
- )}
+
+
+ | Projects |
+ File folder |
+ 6/7/2025 |
+
+
+ | Pictures |
+ File folder |
+ 4/7/2025 |
+
+
+ | 2024 Annual Financial Report |
+ Text document |
+ 12/30/2024 |
+
+
+ | Job Posting |
+ Text Document |
+ 1/18/2025 |
+
);
-
- let tableTester = testUtilUser.createTester('Table', {root: getByRole('grid')});
- await tableTester.triggerColumnHeaderAction({column: 1, action: 0, interactionType: 'keyboard'});
- expect(onAction).toHaveBeenCalledTimes(1);
+ expect(screen.getByRole('grid')).toBeInTheDocument();
});
});
diff --git a/packages/@react-spectrum/s2/test/Tabs.test.tsx b/packages/@react-spectrum/s2/test/Tabs.test.tsx
new file mode 100644
index 00000000000..41bc02c0c76
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/Tabs.test.tsx
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+import {Tab, TabList, TabPanel, Tabs} from '../src';
+
+describe('Tabs', () => {
+ it('renders', async () => {
+ const screen = await render(
+
+
+ Home
+ Files
+ Search
+ Settings
+
+
+ Home content
+
+
+ Files content
+
+
+ Search content
+
+
+ Settings content
+
+
+ );
+ expect(screen.getByRole('tablist')).toBeInTheDocument();
+ const tabs = screen.container.querySelectorAll('[role="tab"]');
+ expect(tabs.length).toBe(4);
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/TagGroup.test.tsx b/packages/@react-spectrum/s2/test/TagGroup.test.tsx
index 9deff9e855d..9c5023ad1aa 100644
--- a/packages/@react-spectrum/s2/test/TagGroup.test.tsx
+++ b/packages/@react-spectrum/s2/test/TagGroup.test.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2025 Adobe. All rights reserved.
+ * Copyright 2026 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
@@ -10,66 +10,34 @@
* governing permissions and limitations under the License.
*/
-import {act, pointerMap, render} from '@react-spectrum/test-utils-internal';
-import React from 'react';
+import {describe, expect, it} from 'vitest';
+import {render} from './utils/render';
import {Tag, TagGroup} from '../src';
-import userEvent from '@testing-library/user-event';
-
-
-let TestTagGroup = ({tagGroupProps, itemProps}) => (
-
- Cat
- Dog
- Kangaroo
-
-);
-
-let renderTagGroup = (tagGroupProps = {}, itemProps = {}) => render( );
-
describe('TagGroup', () => {
- let user;
- beforeAll(() => {
- jest.useFakeTimers();
- user = userEvent.setup({delay: null, pointerMap});
- });
-
- afterEach(() => {
- jest.clearAllMocks();
- act(() => jest.runAllTimers());
- });
-
- afterAll(function () {
- jest.restoreAllMocks();
- });
-
- it('remove button should work', async () => {
- let onRemove = jest.fn();
- let {getAllByLabelText} = render(
-
- Chocolate
- Mint
- Strawberry
- Vanilla
+ it('renders', async () => {
+ const screen = await render(
+
+ Chocolate
+ Mint
+ Strawberry
+ Vanilla
+ Chocolate Chip Cookie Dough
+ Rocky Road
+ Butter Pecan
+ Neapolitan
+ Salted Caramel
+ Mint Chocolate Chip
+ Tonight Dough
+ Lemon Cookie
+ Cookies and Cream
+ Phish Food
+ Peanut Butter Cup
+ Coffee
+ Pistachio
+ Cherry
);
-
- let removeButtons = getAllByLabelText('Remove');
- expect(removeButtons).toHaveLength(4);
- await user.click(removeButtons[0]);
- act(() => {jest.runAllTimers();});
- expect(onRemove).toHaveBeenCalledTimes(1);
- expect(onRemove).toHaveBeenCalledWith(new Set(['chocolate']));
- });
-
- it('should aria label on tags', () => {
- let {getAllByRole} = renderTagGroup({label: 'TagGroup label'}, {'aria-label': 'Test'});
-
- for (let row of getAllByRole('row')) {
- expect(row).toHaveAttribute('aria-label', 'Test');
- }
+ expect(screen.container.firstChild).toBeInTheDocument();
});
});
diff --git a/packages/@react-spectrum/s2/test/TextArea.test.tsx b/packages/@react-spectrum/s2/test/TextArea.test.tsx
new file mode 100644
index 00000000000..92ce941916f
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/TextArea.test.tsx
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+import {TextArea} from '../src';
+
+describe('TextArea', () => {
+ it('renders', async () => {
+ const screen = await render(
+
+ );
+ expect(screen.getByRole('textbox')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/TextField.test.tsx b/packages/@react-spectrum/s2/test/TextField.test.tsx
index bad48cc0ccd..4c256736a58 100644
--- a/packages/@react-spectrum/s2/test/TextField.test.tsx
+++ b/packages/@react-spectrum/s2/test/TextField.test.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2025 Adobe. All rights reserved.
+ * Copyright 2026 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
@@ -10,23 +10,17 @@
* governing permissions and limitations under the License.
*/
-import {fireEvent, render} from '@react-spectrum/test-utils-internal';
-import {TextArea} from '../src';
+import {describe, expect, it} from 'vitest';
+import {render} from './utils/render';
+import {TextField} from '../src';
describe('TextField', () => {
- it('should focus textarea when tapping invalid icon', async () => {
- let {getByRole} = render(
-
+ it('renders', async () => {
+ const screen = await render(
+
);
-
- let textarea = getByRole('textbox');
- // svg doesn't have a role so grab it via queryselector
- let icon = getByRole('presentation').querySelector('svg')!;
- expect(icon).toBeInTheDocument();
- // user event with touch doesn't cause touchEnd to trigger so using fireEvent
- fireEvent.touchStart(icon);
- fireEvent.touchEnd(icon);
-
- expect(document.activeElement).toBe(textarea);
+ expect(screen.getByRole('textbox')).toBeInTheDocument();
});
});
diff --git a/packages/@react-spectrum/s2/test/TimeField.test.tsx b/packages/@react-spectrum/s2/test/TimeField.test.tsx
new file mode 100644
index 00000000000..813e44d6a15
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/TimeField.test.tsx
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+import {TimeField} from '../src';
+
+describe('TimeField', () => {
+ it('renders', async () => {
+ const screen = await render( );
+ expect(screen.getByRole('group')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/ToastContainer.test.tsx b/packages/@react-spectrum/s2/test/ToastContainer.test.tsx
new file mode 100644
index 00000000000..bca1133e27c
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/ToastContainer.test.tsx
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+import {ToastContainer} from '../src';
+
+describe('ToastContainer', () => {
+ it('renders', async () => {
+ const screen = await render( );
+ expect(screen.container).toBeDefined();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/ToggleButton.test.tsx b/packages/@react-spectrum/s2/test/ToggleButton.test.tsx
new file mode 100644
index 00000000000..c36ece8d177
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/ToggleButton.test.tsx
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+import {ToggleButton} from '../src';
+
+describe('ToggleButton', () => {
+ it('renders', async () => {
+ const screen = await render(Star );
+ expect(screen.getByRole('button')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/ToggleButtonGroup.test.tsx b/packages/@react-spectrum/s2/test/ToggleButtonGroup.test.tsx
index c0d87ca68b1..ef521708fda 100644
--- a/packages/@react-spectrum/s2/test/ToggleButtonGroup.test.tsx
+++ b/packages/@react-spectrum/s2/test/ToggleButtonGroup.test.tsx
@@ -10,24 +10,26 @@
* governing permissions and limitations under the License.
*/
-import {render} from '@react-spectrum/test-utils-internal';
+import {describe, expect, it} from 'vitest';
+import {render} from './utils/render';
import {Text, ToggleButton, ToggleButtonGroup} from '../src';
describe('ToggleButtonGroup', () => {
-
- it('can disable all buttons from the group', async () => {
- let {getAllByRole} = render(
-
- Bold
- Italic
- Underline
+ it('renders', async () => {
+ const screen = await render(
+
+
+ Bold
+
+
+ Italic
+
+
+ Underline
+
);
-
-
- let buttons = getAllByRole('radio');
- expect(buttons[0]).toBeDisabled();
- expect(buttons[1]).toBeDisabled();
- expect(buttons[2]).toBeDisabled();
+ const radios = screen.container.querySelectorAll('[role="radio"]');
+ expect(radios.length).toBe(3);
});
});
diff --git a/packages/@react-spectrum/s2/test/Tooltip.test.tsx b/packages/@react-spectrum/s2/test/Tooltip.test.tsx
new file mode 100644
index 00000000000..63374d63d23
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/Tooltip.test.tsx
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {ActionButton, Tooltip, TooltipTrigger} from '../src';
+import {describe, expect, it} from 'vitest';
+import React from 'react';
+import {render} from './utils/render';
+
+describe('Tooltip', () => {
+ it('renders', async () => {
+ const screen = await render(
+
+ Edit
+ Edit name
+
+ );
+ expect(screen.getByRole('button')).toBeInTheDocument();
+ });
+});
diff --git a/packages/@react-spectrum/s2/test/TreeView.test.tsx b/packages/@react-spectrum/s2/test/TreeView.test.tsx
index d68d29d7744..f5606f9f43e 100644
--- a/packages/@react-spectrum/s2/test/TreeView.test.tsx
+++ b/packages/@react-spectrum/s2/test/TreeView.test.tsx
@@ -1,496 +1,39 @@
-
-import {act, pointerMap, render} from '@react-spectrum/test-utils-internal';
-import {
- ActionMenu,
- MenuItem,
- Text,
- TreeView,
- TreeViewItem,
- TreeViewItemContent
-} from '../src';
-import {AriaTreeTests} from '../../../react-aria-components/test/AriaTree.test-util';
-import FileTxt from '../s2wf-icons/S2_Icon_FileText_20_N.svg';
-import Folder from '../s2wf-icons/S2_Icon_Folder_20_N.svg';
-import React from 'react';
-import userEvent from '@testing-library/user-event';
-
-AriaTreeTests({
- prefix: 'spectrum2-static',
- setup: () => {
- let offsetWidth, offsetHeight;
-
- beforeAll(function () {
- offsetWidth = jest.spyOn(window.HTMLElement.prototype, 'clientWidth', 'get').mockImplementation(() => 1000);
- offsetHeight = jest.spyOn(window.HTMLElement.prototype, 'clientHeight', 'get').mockImplementation(() => 1000);
- });
-
- afterAll(function () {
- offsetWidth.mockReset();
- offsetHeight.mockReset();
- });
- },
- renderers: {
- // todo - we don't support isDisabled on TreeViewItems?
- standard: () => render(
-
-
-
- Photos
-
-
-
-
-
- Projects
-
-
-
-
- Projects-1
-
-
-
-
- Projects-1A
-
-
-
-
-
-
- Projects-2
-
-
-
-
-
- Projects-3
-
-
-
-
-
-
- School
-
-
-
-
- Homework-1
-
-
-
-
- Homework-1A
-
-
-
-
-
-
- Homework-2
-
-
-
-
-
- Homework-3
-
-
-
-
-
- ),
- singleSelection: () => render(
-
-
-
- Photos
-
-
-
-
-
- Projects
-
-
-
-
- Projects-1
-
-
-
-
- Projects-1A
-
-
-
-
-
-
- Projects-2
-
-
-
-
-
- Projects-3
-
-
-
-
-
-
- School
-
-
-
-
- Homework-1
-
-
-
-
- Homework-1A
-
-
-
-
-
-
- Homework-2
-
-
-
-
-
- Homework-3
-
-
-
-
-
- ),
- allInteractionsDisabled: () => render(
-
-
-
- Photos
-
-
-
-
-
- Projects
-
-
-
-
- Projects-1
-
-
-
-
- Projects-1A
-
-
-
-
-
-
- Projects-2
-
-
-
-
-
- Projects-3
-
-
-
-
-
-
- School
-
-
-
-
- Homework-1
-
-
-
-
- Homework-1A
-
-
-
-
-
-
- Homework-2
-
-
-
-
-
- Homework-3
-
-
-
-
-
- )
- }
-});
-
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {describe, expect, it} from 'vitest';
+import {render} from './utils/render';
+import {TreeView, TreeViewItem, TreeViewItemContent} from '../src';
describe('TreeView', () => {
- let user;
- let offsetWidth, offsetHeight;
-
- beforeAll(function () {
- user = userEvent.setup({delay: null, pointerMap});
- offsetWidth = jest.spyOn(window.HTMLElement.prototype, 'clientWidth', 'get').mockImplementation(() => 400);
- offsetHeight = jest.spyOn(window.HTMLElement.prototype, 'clientHeight', 'get').mockImplementation(() => 200);
- jest.useFakeTimers();
- });
-
- afterEach(() => {
- jest.clearAllMocks();
- act(() => {jest.runAllTimers();});
- });
-
- afterAll(function () {
- offsetWidth.mockReset();
- offsetHeight.mockReset();
- });
-
- it('should not disable interactive items if the row is in disabledKeys and disabledBehavior is selection', async() => {
- let {getAllByLabelText, getByRole} = render(
-
-
-
- Projects
-
-
-
- Edit
-
-
- Delete
-
-
-
-
-
- Projects-1
-
-
-
- Edit
-
-
- Delete
-
-
-
-
-
-
- Projects-2
-
-
-
- Edit
-
-
- Delete
-
-
-
-
-
-
- );
-
- let buttons = getAllByLabelText('More actions');
- let button = buttons[1];
- expect(button).not.toHaveAttribute('data-disabled');
-
- await user.click(button);
- let menu = getByRole('menu');
- expect(menu).toBeInTheDocument();
- });
-
- it('should not disable interactive items if the row isDisabled and disabledBehavior is selection', async () => {
- let {getAllByLabelText, getByRole} = render(
-
-
-
- Projects
-
-
-
- Edit
-
-
- Delete
-
-
-
-
-
- Projects-1
-
-
-
- Edit
-
-
- Delete
-
-
-
-
-
-
- Projects-2
-
-
-
- Edit
-
-
- Delete
-
-
-
-
-
-
- );
-
- let buttons = getAllByLabelText('More actions');
- let button = buttons[1];
- expect(button).not.toHaveAttribute('data-disabled');
-
- await user.click(button);
- let menu = getByRole('menu');
- expect(menu).toBeInTheDocument();
- });
-
- it('should disable interactive items if the row is in disabledKeys and disabledBehavior is all', async () => {
- let {getAllByLabelText, queryByRole} = render(
-
-
-
- Projects
-
-
-
- Edit
-
-
- Delete
-
-
-
-
-
- Projects-1
-
-
-
- Edit
-
-
- Delete
-
-
-
-
-
-
- Projects-2
-
-
-
- Edit
-
-
- Delete
-
-
-
-
-
-
- );
-
- let buttons = getAllByLabelText('More actions');
- let button = buttons[1];
- expect(button).toHaveAttribute('data-disabled');
-
- await user.click(button);
- let menu = queryByRole('menu');
- expect(menu).not.toBeInTheDocument();
- });
-
- it('should disable interactive items if the row isDisabled and disabledBehavior is all', async () => {
- let {getAllByLabelText, queryByRole} = render(
-
-
-
- Projects
-
-
-
- Edit
-
-
- Delete
-
-
-
-
-
- Projects-1
-
-
-
- Edit
-
-
- Delete
-
-
-
+ it('renders', async () => {
+ const screen = await render(
+
+
+ Documents
+
+ Project A
+
+ Weekly Report
+
-
-
- Projects-2
-
-
-
- Edit
-
-
- Delete
-
-
-
+
+ README
);
-
- let buttons = getAllByLabelText('More actions');
- let button = buttons[1];
- expect(button).toHaveAttribute('data-disabled');
-
- await user.click(button);
- let menu = queryByRole('menu');
- expect(menu).not.toBeInTheDocument();
+ expect(screen.getByRole('treegrid')).toBeInTheDocument();
});
});
diff --git a/packages/@react-spectrum/s2/test/__mocks__/svg.tsx b/packages/@react-spectrum/s2/test/__mocks__/svg.tsx
new file mode 100644
index 00000000000..c799e6a5d0f
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/__mocks__/svg.tsx
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import React, {forwardRef, SVGProps} from 'react';
+
+const MockSvgIcon = forwardRef>(function MockSvgIcon(props, ref) {
+ return (
+
+
+
+ );
+});
+
+export default MockSvgIcon;
diff --git a/packages/@react-spectrum/s2/test/__mocks__/ui-icons.tsx b/packages/@react-spectrum/s2/test/__mocks__/ui-icons.tsx
new file mode 100644
index 00000000000..650112daff9
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/__mocks__/ui-icons.tsx
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import React, {ReactNode, SVGProps} from 'react';
+
+function MockIcon(props: SVGProps): ReactNode {
+ return ;
+}
+
+export default MockIcon;
diff --git a/packages/@react-spectrum/s2/test/setup.ts b/packages/@react-spectrum/s2/test/setup.ts
new file mode 100644
index 00000000000..04b189af1e9
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/setup.ts
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import 'vitest-browser-react';
+import '../src/page';
+import {beforeEach} from 'vitest';
+
+// Track console errors and warnings per test
+let consoleErrors: Array<{message: unknown, args: unknown[]}> = [];
+let consoleWarnings: Array<{message: unknown, args: unknown[]}> = [];
+
+// Reset console error/warning tracking before each test
+beforeEach(() => {
+ consoleErrors = [];
+ consoleWarnings = [];
+});
+
+// TODO: Fail test if there were any console errors
+// afterEach(() => {
+// if (consoleErrors.length > 0) {
+// const errorMessages = consoleErrors.map(e =>
+// typeof e.message === 'string' ? e.message : String(e.message)
+// ).join('\n');
+// throw new Error(
+// `Test failed due to console errors:\n${errorMessages}\n\n`
+// );
+// }
+// });
+
+// Track console errors
+const originalError = console.error;
+console.error = function (message: unknown, ...args: unknown[]) {
+ consoleErrors.push({message, args});
+ originalError.call(console, message, ...args);
+};
+
+// Track console warnings
+const originalWarn = console.warn;
+console.warn = function (message: unknown, ...args: unknown[]) {
+ consoleWarnings.push({message, args});
+ originalWarn.call(console, message, ...args);
+};
+
+// Mock process for browser environment
+if (typeof process === 'undefined') {
+ globalThis.process = {
+ env: {},
+ versions: {node: '0.0.0'} as NodeJS.ProcessVersions,
+ browser: true
+ } as unknown as NodeJS.Process;
+}
diff --git a/packages/@react-spectrum/s2/test/utils/index.ts b/packages/@react-spectrum/s2/test/utils/index.ts
new file mode 100644
index 00000000000..6d765fd03b2
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/utils/index.ts
@@ -0,0 +1,13 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+export * from './render';
diff --git a/packages/@react-spectrum/s2/test/utils/render.tsx b/packages/@react-spectrum/s2/test/utils/render.tsx
new file mode 100644
index 00000000000..ef69b891d06
--- /dev/null
+++ b/packages/@react-spectrum/s2/test/utils/render.tsx
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {Provider, type ProviderProps} from '../../src';
+import React, {ReactElement} from 'react';
+import {render as vitestBrowserRender} from 'vitest-browser-react';
+
+export interface S2RenderOptions {
+ /**
+ * Options to pass to the Provider.
+ */
+ providerOptions?: Omit
+}
+
+/**
+ * Custom render function that wraps components in the S2 Provider.
+ * Uses vitest-browser-react for browser mode testing.
+ */
+export async function render(
+ ui: ReactElement,
+ options: S2RenderOptions = {}
+) {
+ const {providerOptions = {}} = options;
+ const {locale = 'en-US', colorScheme = 'light', ...restProviderOptions} = providerOptions;
+
+ function Wrapper({children}: {children: React.ReactNode}) {
+ return (
+
+ {children}
+
+ );
+ }
+
+ return await vitestBrowserRender({ui} );
+}
diff --git a/packages/@react-spectrum/s2/vitest.config.ts b/packages/@react-spectrum/s2/vitest.config.ts
new file mode 100644
index 00000000000..3e0b319791b
--- /dev/null
+++ b/packages/@react-spectrum/s2/vitest.config.ts
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2025 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {defineConfig} from 'vitest/config';
+import fs from 'fs';
+import macros from 'unplugin-parcel-macros';
+import path from 'path';
+import {playwright} from '@vitest/browser-playwright';
+import type {Plugin} from 'vite';
+import react from '@vitejs/plugin-react';
+import svgr from 'vite-plugin-svgr';
+
+// Handles ../intl/*.json imports
+function intlJsonPlugin(): Plugin {
+ return {
+ name: 'intl-json-loader',
+ async resolveId(source, importer) {
+ if (source.includes('/intl/*.json') && importer) {
+ const dir = path.dirname(importer);
+ const intlDir = path.resolve(dir, source.replace('*.json', ''));
+ return `virtual:intl-messages:${intlDir}`;
+ }
+ return null;
+ },
+ async load(id) {
+ if (id.startsWith('virtual:intl-messages:')) {
+ const intlDir = id.replace('virtual:intl-messages:', '');
+ try {
+ const files = fs.readdirSync(intlDir).filter(f => f.endsWith('.json'));
+ const messages: Record> = {};
+
+ for (const file of files) {
+ const locale = path.basename(file, '.json');
+ const content = fs.readFileSync(path.join(intlDir, file), 'utf-8');
+ messages[locale] = JSON.parse(content);
+ }
+
+ return `export default ${JSON.stringify(messages)};`;
+ } catch {
+ return 'export default {};';
+ }
+ }
+ return null;
+ }
+ };
+}
+
+// Resolve @react-spectrum/s2/illustrations/* imports
+function illustrationResolverPlugin(): Plugin {
+ return {
+ name: 'illustration-resolver',
+ enforce: 'pre',
+ resolveId(source) {
+ if (source.startsWith('@react-spectrum/s2/illustrations/')) {
+ const illustrationPath = source.replace('@react-spectrum/s2/illustrations/', '');
+ const tsxPath = path.resolve(__dirname, 'spectrum-illustrations', illustrationPath + '.tsx');
+
+ if (fs.existsSync(tsxPath)) {
+ return tsxPath;
+ }
+
+ return null;
+ }
+ return null;
+ }
+ };
+}
+
+// Handle illustration: protocol
+function illustrationPlugin(): Plugin {
+ return {
+ name: 'illustration-loader',
+ enforce: 'pre',
+ resolveId(source, importer) {
+ if (source.startsWith('illustration:')) {
+ // Convert illustration:./path.svg to actual SVG path
+ const svgPath = source.replace('illustration:', '');
+ if (importer) {
+ const dir = path.dirname(importer);
+ const resolvedPath = path.resolve(dir, svgPath);
+ return resolvedPath;
+ }
+ }
+ return null;
+ }
+ };
+}
+
+// Build icon aliases to resolve @react-spectrum/s2/icons/* imports
+function buildIconAliases(): Record {
+ const aliases: Record = {};
+ const iconsDir = path.resolve(__dirname, 's2wf-icons');
+
+ if (fs.existsSync(iconsDir)) {
+ const iconFiles = fs.readdirSync(iconsDir).filter(f => f.startsWith('S2_Icon_') && f.endsWith('_20_N.svg'));
+
+ for (const iconFile of iconFiles) {
+ // Extract icon name: S2_Icon_Feedback_20_N.svg -> Feedback
+ const match = iconFile.match(/^S2_Icon_(.+)_20_N\.svg$/);
+ if (match) {
+ const iconName = match[1];
+ const iconPath = path.resolve(iconsDir, iconFile);
+ aliases[`@react-spectrum/s2/icons/${iconName}`] = iconPath;
+ }
+ }
+ }
+
+ return aliases;
+}
+
+export default defineConfig({
+ plugins: [
+ // @ts-expect-error
+ macros.vite(), // Must be first!
+ // @ts-expect-error
+ illustrationResolverPlugin(), // Resolve @react-spectrum/s2/illustrations/* imports
+ // @ts-expect-error
+ illustrationPlugin(), // Handle illustration: protocol
+ // @ts-expect-error
+ svgr({
+ // Process all SVG files as React components
+ include: [
+ '**/s2wf-icons/**/*.svg',
+ '**/ui-icons/**/*.svg',
+ '**/spectrum-illustrations/**/*.svg'
+ ],
+ exclude: ['**/node_modules/**'],
+ svgrOptions: {
+ ref: true,
+ typescript: false,
+ svgoConfig: {
+ plugins: [
+ {
+ name: 'preset-default',
+ params: {
+ overrides: {
+ removeViewBox: false
+ }
+ }
+ }
+ ]
+ }
+ }
+ }),
+ // @ts-expect-error
+ react(),
+ // @ts-expect-error
+ intlJsonPlugin()
+ ],
+ test: {
+ globals: true,
+ setupFiles: ['./test/setup.ts'],
+ include: ['test/**/*.test.{ts,tsx}'],
+ browser: {
+ provider: playwright(),
+ enabled: true,
+ instances: [
+ {browser: 'chromium'},
+ {browser: 'firefox'},
+ {browser: 'webkit'}
+ ]
+ },
+ coverage: {
+ provider: 'v8',
+ reporter: ['text', 'json', 'html'],
+ include: ['src/**/*.{ts,tsx}'],
+ exclude: ['src/**/*.d.ts', 'src/index.ts']
+ },
+ testTimeout: 20000
+ },
+ resolve: {
+ conditions: ['source', 'import', 'module', 'default'],
+ extensions: ['.mjs', '.js', '.mts', '.ts', '.jsx', '.tsx', '.json', '.svg'],
+ alias: {
+ ...buildIconAliases(),
+ '@react-spectrum/s2/illustrations': path.resolve(__dirname, 'spectrum-illustrations'),
+ '@react-spectrum/s2': path.resolve(__dirname, 'src')
+ }
+ },
+ optimizeDeps: {
+ exclude: ['@parcel/macros']
+ },
+ css: {
+ postcss: {
+ // Disable PostCSS processing for macro-generated CSS
+ // The macros plugin handles CSS generation, and PostCSS was causing parsing errors
+ // with nested selectors like &:before
+ // @ts-expect-error
+ config: false
+ }
+ }
+});
diff --git a/yarn.lock b/yarn.lock
index 6500c7bc175..a2ca413cf6b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -208,6 +208,19 @@ __metadata:
languageName: node
linkType: hard
+"@asamuzakjp/css-color@npm:^3.2.0":
+ version: 3.2.0
+ resolution: "@asamuzakjp/css-color@npm:3.2.0"
+ dependencies:
+ "@csstools/css-calc": "npm:^2.1.3"
+ "@csstools/css-color-parser": "npm:^3.0.9"
+ "@csstools/css-parser-algorithms": "npm:^3.0.4"
+ "@csstools/css-tokenizer": "npm:^3.0.3"
+ lru-cache: "npm:^10.4.3"
+ checksum: 10c0/a4bf1c831751b1fae46b437e37e8a38c0b5bd58d23230157ae210bd1e905fe509b89b7c243e63d1522d852668a6292ed730a160e21342772b4e5b7b8ea14c092
+ languageName: node
+ linkType: hard
+
"@babel/cli@npm:7.24.1":
version: 7.24.1
resolution: "@babel/cli@npm:7.24.1"
@@ -454,6 +467,13 @@ __metadata:
languageName: node
linkType: hard
+"@babel/helper-plugin-utils@npm:^7.27.1":
+ version: 7.28.6
+ resolution: "@babel/helper-plugin-utils@npm:7.28.6"
+ checksum: 10c0/3f5f8acc152fdbb69a84b8624145ff4f9b9f6e776cb989f9f968f8606eb7185c5c3cfcf3ba08534e37e1e0e1c118ac67080610333f56baa4f7376c99b5f1143d
+ languageName: node
+ linkType: hard
+
"@babel/helper-remap-async-to-generator@npm:^7.22.20":
version: 7.22.20
resolution: "@babel/helper-remap-async-to-generator@npm:7.22.20"
@@ -1400,6 +1420,28 @@ __metadata:
languageName: node
linkType: hard
+"@babel/plugin-transform-react-jsx-self@npm:^7.27.1":
+ version: 7.27.1
+ resolution: "@babel/plugin-transform-react-jsx-self@npm:7.27.1"
+ dependencies:
+ "@babel/helper-plugin-utils": "npm:^7.27.1"
+ peerDependencies:
+ "@babel/core": ^7.0.0-0
+ checksum: 10c0/00a4f917b70a608f9aca2fb39aabe04a60aa33165a7e0105fd44b3a8531630eb85bf5572e9f242f51e6ad2fa38c2e7e780902176c863556c58b5ba6f6e164031
+ languageName: node
+ linkType: hard
+
+"@babel/plugin-transform-react-jsx-source@npm:^7.27.1":
+ version: 7.27.1
+ resolution: "@babel/plugin-transform-react-jsx-source@npm:7.27.1"
+ dependencies:
+ "@babel/helper-plugin-utils": "npm:^7.27.1"
+ peerDependencies:
+ "@babel/core": ^7.0.0-0
+ checksum: 10c0/5e67b56c39c4d03e59e03ba80692b24c5a921472079b63af711b1d250fc37c1733a17069b63537f750f3e937ec44a42b1ee6a46cd23b1a0df5163b17f741f7f2
+ languageName: node
+ linkType: hard
+
"@babel/plugin-transform-react-jsx@npm:^7.22.5, @babel/plugin-transform-react-jsx@npm:^7.23.4":
version: 7.23.4
resolution: "@babel/plugin-transform-react-jsx@npm:7.23.4"
@@ -1828,6 +1870,36 @@ __metadata:
languageName: node
linkType: hard
+"@csstools/color-helpers@npm:^5.1.0":
+ version: 5.1.0
+ resolution: "@csstools/color-helpers@npm:5.1.0"
+ checksum: 10c0/b7f99d2e455cf1c9b41a67a5327d5d02888cd5c8802a68b1887dffef537d9d4bc66b3c10c1e62b40bbed638b6c1d60b85a232f904ed7b39809c4029cb36567db
+ languageName: node
+ linkType: hard
+
+"@csstools/css-calc@npm:^2.1.3, @csstools/css-calc@npm:^2.1.4":
+ version: 2.1.4
+ resolution: "@csstools/css-calc@npm:2.1.4"
+ peerDependencies:
+ "@csstools/css-parser-algorithms": ^3.0.5
+ "@csstools/css-tokenizer": ^3.0.4
+ checksum: 10c0/42ce5793e55ec4d772083808a11e9fb2dfe36db3ec168713069a276b4c3882205b3507c4680224c28a5d35fe0bc2d308c77f8f2c39c7c09aad8747708eb8ddd8
+ languageName: node
+ linkType: hard
+
+"@csstools/css-color-parser@npm:^3.0.9":
+ version: 3.1.0
+ resolution: "@csstools/css-color-parser@npm:3.1.0"
+ dependencies:
+ "@csstools/color-helpers": "npm:^5.1.0"
+ "@csstools/css-calc": "npm:^2.1.4"
+ peerDependencies:
+ "@csstools/css-parser-algorithms": ^3.0.5
+ "@csstools/css-tokenizer": ^3.0.4
+ checksum: 10c0/0e0c670ad54ec8ec4d9b07568b80defd83b9482191f5e8ca84ab546b7be6db5d7cc2ba7ac9fae54488b129a4be235d6183d3aab4416fec5e89351f73af4222c5
+ languageName: node
+ linkType: hard
+
"@csstools/css-parser-algorithms@npm:^2.1.1":
version: 2.2.0
resolution: "@csstools/css-parser-algorithms@npm:2.2.0"
@@ -1837,6 +1909,15 @@ __metadata:
languageName: node
linkType: hard
+"@csstools/css-parser-algorithms@npm:^3.0.4":
+ version: 3.0.5
+ resolution: "@csstools/css-parser-algorithms@npm:3.0.5"
+ peerDependencies:
+ "@csstools/css-tokenizer": ^3.0.4
+ checksum: 10c0/d9a1c888bd43849ae3437ca39251d5c95d2c8fd6b5ccdb7c45491dfd2c1cbdc3075645e80901d120e4d2c1993db9a5b2d83793b779dbbabcfb132adb142eb7f7
+ languageName: node
+ linkType: hard
+
"@csstools/css-tokenizer@npm:^2.1.1":
version: 2.1.1
resolution: "@csstools/css-tokenizer@npm:2.1.1"
@@ -1844,6 +1925,13 @@ __metadata:
languageName: node
linkType: hard
+"@csstools/css-tokenizer@npm:^3.0.3":
+ version: 3.0.4
+ resolution: "@csstools/css-tokenizer@npm:3.0.4"
+ checksum: 10c0/3b589f8e9942075a642213b389bab75a2d50d05d203727fcdac6827648a5572674caff07907eff3f9a2389d86a4ee47308fafe4f8588f4a77b7167c588d2559f
+ languageName: node
+ linkType: hard
+
"@cypress/request@npm:3.0.7":
version: 3.0.7
resolution: "@cypress/request@npm:3.0.7"
@@ -1890,6 +1978,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/aix-ppc64@npm:0.25.12":
+ version: 0.25.12
+ resolution: "@esbuild/aix-ppc64@npm:0.25.12"
+ conditions: os=aix & cpu=ppc64
+ languageName: node
+ linkType: hard
+
"@esbuild/aix-ppc64@npm:0.25.5":
version: 0.25.5
resolution: "@esbuild/aix-ppc64@npm:0.25.5"
@@ -1897,6 +1992,20 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/aix-ppc64@npm:0.27.2":
+ version: 0.27.2
+ resolution: "@esbuild/aix-ppc64@npm:0.27.2"
+ conditions: os=aix & cpu=ppc64
+ languageName: node
+ linkType: hard
+
+"@esbuild/android-arm64@npm:0.25.12":
+ version: 0.25.12
+ resolution: "@esbuild/android-arm64@npm:0.25.12"
+ conditions: os=android & cpu=arm64
+ languageName: node
+ linkType: hard
+
"@esbuild/android-arm64@npm:0.25.5":
version: 0.25.5
resolution: "@esbuild/android-arm64@npm:0.25.5"
@@ -1904,6 +2013,20 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/android-arm64@npm:0.27.2":
+ version: 0.27.2
+ resolution: "@esbuild/android-arm64@npm:0.27.2"
+ conditions: os=android & cpu=arm64
+ languageName: node
+ linkType: hard
+
+"@esbuild/android-arm@npm:0.25.12":
+ version: 0.25.12
+ resolution: "@esbuild/android-arm@npm:0.25.12"
+ conditions: os=android & cpu=arm
+ languageName: node
+ linkType: hard
+
"@esbuild/android-arm@npm:0.25.5":
version: 0.25.5
resolution: "@esbuild/android-arm@npm:0.25.5"
@@ -1911,6 +2034,20 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/android-arm@npm:0.27.2":
+ version: 0.27.2
+ resolution: "@esbuild/android-arm@npm:0.27.2"
+ conditions: os=android & cpu=arm
+ languageName: node
+ linkType: hard
+
+"@esbuild/android-x64@npm:0.25.12":
+ version: 0.25.12
+ resolution: "@esbuild/android-x64@npm:0.25.12"
+ conditions: os=android & cpu=x64
+ languageName: node
+ linkType: hard
+
"@esbuild/android-x64@npm:0.25.5":
version: 0.25.5
resolution: "@esbuild/android-x64@npm:0.25.5"
@@ -1918,6 +2055,20 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/android-x64@npm:0.27.2":
+ version: 0.27.2
+ resolution: "@esbuild/android-x64@npm:0.27.2"
+ conditions: os=android & cpu=x64
+ languageName: node
+ linkType: hard
+
+"@esbuild/darwin-arm64@npm:0.25.12":
+ version: 0.25.12
+ resolution: "@esbuild/darwin-arm64@npm:0.25.12"
+ conditions: os=darwin & cpu=arm64
+ languageName: node
+ linkType: hard
+
"@esbuild/darwin-arm64@npm:0.25.5":
version: 0.25.5
resolution: "@esbuild/darwin-arm64@npm:0.25.5"
@@ -1925,6 +2076,20 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/darwin-arm64@npm:0.27.2":
+ version: 0.27.2
+ resolution: "@esbuild/darwin-arm64@npm:0.27.2"
+ conditions: os=darwin & cpu=arm64
+ languageName: node
+ linkType: hard
+
+"@esbuild/darwin-x64@npm:0.25.12":
+ version: 0.25.12
+ resolution: "@esbuild/darwin-x64@npm:0.25.12"
+ conditions: os=darwin & cpu=x64
+ languageName: node
+ linkType: hard
+
"@esbuild/darwin-x64@npm:0.25.5":
version: 0.25.5
resolution: "@esbuild/darwin-x64@npm:0.25.5"
@@ -1932,6 +2097,20 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/darwin-x64@npm:0.27.2":
+ version: 0.27.2
+ resolution: "@esbuild/darwin-x64@npm:0.27.2"
+ conditions: os=darwin & cpu=x64
+ languageName: node
+ linkType: hard
+
+"@esbuild/freebsd-arm64@npm:0.25.12":
+ version: 0.25.12
+ resolution: "@esbuild/freebsd-arm64@npm:0.25.12"
+ conditions: os=freebsd & cpu=arm64
+ languageName: node
+ linkType: hard
+
"@esbuild/freebsd-arm64@npm:0.25.5":
version: 0.25.5
resolution: "@esbuild/freebsd-arm64@npm:0.25.5"
@@ -1939,6 +2118,20 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/freebsd-arm64@npm:0.27.2":
+ version: 0.27.2
+ resolution: "@esbuild/freebsd-arm64@npm:0.27.2"
+ conditions: os=freebsd & cpu=arm64
+ languageName: node
+ linkType: hard
+
+"@esbuild/freebsd-x64@npm:0.25.12":
+ version: 0.25.12
+ resolution: "@esbuild/freebsd-x64@npm:0.25.12"
+ conditions: os=freebsd & cpu=x64
+ languageName: node
+ linkType: hard
+
"@esbuild/freebsd-x64@npm:0.25.5":
version: 0.25.5
resolution: "@esbuild/freebsd-x64@npm:0.25.5"
@@ -1946,6 +2139,20 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/freebsd-x64@npm:0.27.2":
+ version: 0.27.2
+ resolution: "@esbuild/freebsd-x64@npm:0.27.2"
+ conditions: os=freebsd & cpu=x64
+ languageName: node
+ linkType: hard
+
+"@esbuild/linux-arm64@npm:0.25.12":
+ version: 0.25.12
+ resolution: "@esbuild/linux-arm64@npm:0.25.12"
+ conditions: os=linux & cpu=arm64
+ languageName: node
+ linkType: hard
+
"@esbuild/linux-arm64@npm:0.25.5":
version: 0.25.5
resolution: "@esbuild/linux-arm64@npm:0.25.5"
@@ -1953,6 +2160,20 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/linux-arm64@npm:0.27.2":
+ version: 0.27.2
+ resolution: "@esbuild/linux-arm64@npm:0.27.2"
+ conditions: os=linux & cpu=arm64
+ languageName: node
+ linkType: hard
+
+"@esbuild/linux-arm@npm:0.25.12":
+ version: 0.25.12
+ resolution: "@esbuild/linux-arm@npm:0.25.12"
+ conditions: os=linux & cpu=arm
+ languageName: node
+ linkType: hard
+
"@esbuild/linux-arm@npm:0.25.5":
version: 0.25.5
resolution: "@esbuild/linux-arm@npm:0.25.5"
@@ -1960,6 +2181,20 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/linux-arm@npm:0.27.2":
+ version: 0.27.2
+ resolution: "@esbuild/linux-arm@npm:0.27.2"
+ conditions: os=linux & cpu=arm
+ languageName: node
+ linkType: hard
+
+"@esbuild/linux-ia32@npm:0.25.12":
+ version: 0.25.12
+ resolution: "@esbuild/linux-ia32@npm:0.25.12"
+ conditions: os=linux & cpu=ia32
+ languageName: node
+ linkType: hard
+
"@esbuild/linux-ia32@npm:0.25.5":
version: 0.25.5
resolution: "@esbuild/linux-ia32@npm:0.25.5"
@@ -1967,6 +2202,20 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/linux-ia32@npm:0.27.2":
+ version: 0.27.2
+ resolution: "@esbuild/linux-ia32@npm:0.27.2"
+ conditions: os=linux & cpu=ia32
+ languageName: node
+ linkType: hard
+
+"@esbuild/linux-loong64@npm:0.25.12":
+ version: 0.25.12
+ resolution: "@esbuild/linux-loong64@npm:0.25.12"
+ conditions: os=linux & cpu=loong64
+ languageName: node
+ linkType: hard
+
"@esbuild/linux-loong64@npm:0.25.5":
version: 0.25.5
resolution: "@esbuild/linux-loong64@npm:0.25.5"
@@ -1974,6 +2223,20 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/linux-loong64@npm:0.27.2":
+ version: 0.27.2
+ resolution: "@esbuild/linux-loong64@npm:0.27.2"
+ conditions: os=linux & cpu=loong64
+ languageName: node
+ linkType: hard
+
+"@esbuild/linux-mips64el@npm:0.25.12":
+ version: 0.25.12
+ resolution: "@esbuild/linux-mips64el@npm:0.25.12"
+ conditions: os=linux & cpu=mips64el
+ languageName: node
+ linkType: hard
+
"@esbuild/linux-mips64el@npm:0.25.5":
version: 0.25.5
resolution: "@esbuild/linux-mips64el@npm:0.25.5"
@@ -1981,6 +2244,20 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/linux-mips64el@npm:0.27.2":
+ version: 0.27.2
+ resolution: "@esbuild/linux-mips64el@npm:0.27.2"
+ conditions: os=linux & cpu=mips64el
+ languageName: node
+ linkType: hard
+
+"@esbuild/linux-ppc64@npm:0.25.12":
+ version: 0.25.12
+ resolution: "@esbuild/linux-ppc64@npm:0.25.12"
+ conditions: os=linux & cpu=ppc64
+ languageName: node
+ linkType: hard
+
"@esbuild/linux-ppc64@npm:0.25.5":
version: 0.25.5
resolution: "@esbuild/linux-ppc64@npm:0.25.5"
@@ -1988,6 +2265,20 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/linux-ppc64@npm:0.27.2":
+ version: 0.27.2
+ resolution: "@esbuild/linux-ppc64@npm:0.27.2"
+ conditions: os=linux & cpu=ppc64
+ languageName: node
+ linkType: hard
+
+"@esbuild/linux-riscv64@npm:0.25.12":
+ version: 0.25.12
+ resolution: "@esbuild/linux-riscv64@npm:0.25.12"
+ conditions: os=linux & cpu=riscv64
+ languageName: node
+ linkType: hard
+
"@esbuild/linux-riscv64@npm:0.25.5":
version: 0.25.5
resolution: "@esbuild/linux-riscv64@npm:0.25.5"
@@ -1995,6 +2286,20 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/linux-riscv64@npm:0.27.2":
+ version: 0.27.2
+ resolution: "@esbuild/linux-riscv64@npm:0.27.2"
+ conditions: os=linux & cpu=riscv64
+ languageName: node
+ linkType: hard
+
+"@esbuild/linux-s390x@npm:0.25.12":
+ version: 0.25.12
+ resolution: "@esbuild/linux-s390x@npm:0.25.12"
+ conditions: os=linux & cpu=s390x
+ languageName: node
+ linkType: hard
+
"@esbuild/linux-s390x@npm:0.25.5":
version: 0.25.5
resolution: "@esbuild/linux-s390x@npm:0.25.5"
@@ -2002,6 +2307,20 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/linux-s390x@npm:0.27.2":
+ version: 0.27.2
+ resolution: "@esbuild/linux-s390x@npm:0.27.2"
+ conditions: os=linux & cpu=s390x
+ languageName: node
+ linkType: hard
+
+"@esbuild/linux-x64@npm:0.25.12":
+ version: 0.25.12
+ resolution: "@esbuild/linux-x64@npm:0.25.12"
+ conditions: os=linux & cpu=x64
+ languageName: node
+ linkType: hard
+
"@esbuild/linux-x64@npm:0.25.5":
version: 0.25.5
resolution: "@esbuild/linux-x64@npm:0.25.5"
@@ -2009,6 +2328,20 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/linux-x64@npm:0.27.2":
+ version: 0.27.2
+ resolution: "@esbuild/linux-x64@npm:0.27.2"
+ conditions: os=linux & cpu=x64
+ languageName: node
+ linkType: hard
+
+"@esbuild/netbsd-arm64@npm:0.25.12":
+ version: 0.25.12
+ resolution: "@esbuild/netbsd-arm64@npm:0.25.12"
+ conditions: os=netbsd & cpu=arm64
+ languageName: node
+ linkType: hard
+
"@esbuild/netbsd-arm64@npm:0.25.5":
version: 0.25.5
resolution: "@esbuild/netbsd-arm64@npm:0.25.5"
@@ -2016,6 +2349,20 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/netbsd-arm64@npm:0.27.2":
+ version: 0.27.2
+ resolution: "@esbuild/netbsd-arm64@npm:0.27.2"
+ conditions: os=netbsd & cpu=arm64
+ languageName: node
+ linkType: hard
+
+"@esbuild/netbsd-x64@npm:0.25.12":
+ version: 0.25.12
+ resolution: "@esbuild/netbsd-x64@npm:0.25.12"
+ conditions: os=netbsd & cpu=x64
+ languageName: node
+ linkType: hard
+
"@esbuild/netbsd-x64@npm:0.25.5":
version: 0.25.5
resolution: "@esbuild/netbsd-x64@npm:0.25.5"
@@ -2023,6 +2370,20 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/netbsd-x64@npm:0.27.2":
+ version: 0.27.2
+ resolution: "@esbuild/netbsd-x64@npm:0.27.2"
+ conditions: os=netbsd & cpu=x64
+ languageName: node
+ linkType: hard
+
+"@esbuild/openbsd-arm64@npm:0.25.12":
+ version: 0.25.12
+ resolution: "@esbuild/openbsd-arm64@npm:0.25.12"
+ conditions: os=openbsd & cpu=arm64
+ languageName: node
+ linkType: hard
+
"@esbuild/openbsd-arm64@npm:0.25.5":
version: 0.25.5
resolution: "@esbuild/openbsd-arm64@npm:0.25.5"
@@ -2030,6 +2391,20 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/openbsd-arm64@npm:0.27.2":
+ version: 0.27.2
+ resolution: "@esbuild/openbsd-arm64@npm:0.27.2"
+ conditions: os=openbsd & cpu=arm64
+ languageName: node
+ linkType: hard
+
+"@esbuild/openbsd-x64@npm:0.25.12":
+ version: 0.25.12
+ resolution: "@esbuild/openbsd-x64@npm:0.25.12"
+ conditions: os=openbsd & cpu=x64
+ languageName: node
+ linkType: hard
+
"@esbuild/openbsd-x64@npm:0.25.5":
version: 0.25.5
resolution: "@esbuild/openbsd-x64@npm:0.25.5"
@@ -2037,6 +2412,34 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/openbsd-x64@npm:0.27.2":
+ version: 0.27.2
+ resolution: "@esbuild/openbsd-x64@npm:0.27.2"
+ conditions: os=openbsd & cpu=x64
+ languageName: node
+ linkType: hard
+
+"@esbuild/openharmony-arm64@npm:0.25.12":
+ version: 0.25.12
+ resolution: "@esbuild/openharmony-arm64@npm:0.25.12"
+ conditions: os=openharmony & cpu=arm64
+ languageName: node
+ linkType: hard
+
+"@esbuild/openharmony-arm64@npm:0.27.2":
+ version: 0.27.2
+ resolution: "@esbuild/openharmony-arm64@npm:0.27.2"
+ conditions: os=openharmony & cpu=arm64
+ languageName: node
+ linkType: hard
+
+"@esbuild/sunos-x64@npm:0.25.12":
+ version: 0.25.12
+ resolution: "@esbuild/sunos-x64@npm:0.25.12"
+ conditions: os=sunos & cpu=x64
+ languageName: node
+ linkType: hard
+
"@esbuild/sunos-x64@npm:0.25.5":
version: 0.25.5
resolution: "@esbuild/sunos-x64@npm:0.25.5"
@@ -2044,6 +2447,20 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/sunos-x64@npm:0.27.2":
+ version: 0.27.2
+ resolution: "@esbuild/sunos-x64@npm:0.27.2"
+ conditions: os=sunos & cpu=x64
+ languageName: node
+ linkType: hard
+
+"@esbuild/win32-arm64@npm:0.25.12":
+ version: 0.25.12
+ resolution: "@esbuild/win32-arm64@npm:0.25.12"
+ conditions: os=win32 & cpu=arm64
+ languageName: node
+ linkType: hard
+
"@esbuild/win32-arm64@npm:0.25.5":
version: 0.25.5
resolution: "@esbuild/win32-arm64@npm:0.25.5"
@@ -2051,6 +2468,20 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/win32-arm64@npm:0.27.2":
+ version: 0.27.2
+ resolution: "@esbuild/win32-arm64@npm:0.27.2"
+ conditions: os=win32 & cpu=arm64
+ languageName: node
+ linkType: hard
+
+"@esbuild/win32-ia32@npm:0.25.12":
+ version: 0.25.12
+ resolution: "@esbuild/win32-ia32@npm:0.25.12"
+ conditions: os=win32 & cpu=ia32
+ languageName: node
+ linkType: hard
+
"@esbuild/win32-ia32@npm:0.25.5":
version: 0.25.5
resolution: "@esbuild/win32-ia32@npm:0.25.5"
@@ -2058,6 +2489,20 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/win32-ia32@npm:0.27.2":
+ version: 0.27.2
+ resolution: "@esbuild/win32-ia32@npm:0.27.2"
+ conditions: os=win32 & cpu=ia32
+ languageName: node
+ linkType: hard
+
+"@esbuild/win32-x64@npm:0.25.12":
+ version: 0.25.12
+ resolution: "@esbuild/win32-x64@npm:0.25.12"
+ conditions: os=win32 & cpu=x64
+ languageName: node
+ linkType: hard
+
"@esbuild/win32-x64@npm:0.25.5":
version: 0.25.5
resolution: "@esbuild/win32-x64@npm:0.25.5"
@@ -2065,6 +2510,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/win32-x64@npm:0.27.2":
+ version: 0.27.2
+ resolution: "@esbuild/win32-x64@npm:0.27.2"
+ conditions: os=win32 & cpu=x64
+ languageName: node
+ linkType: hard
+
"@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0":
version: 4.4.0
resolution: "@eslint-community/eslint-utils@npm:4.4.0"
@@ -2995,6 +3447,13 @@ __metadata:
languageName: node
linkType: hard
+"@jridgewell/sourcemap-codec@npm:^1.5.5":
+ version: 1.5.5
+ resolution: "@jridgewell/sourcemap-codec@npm:1.5.5"
+ checksum: 10c0/f9e538f302b63c0ebc06eecb1dd9918dd4289ed36147a0ddce35d6ea4d7ebbda243cda7b2213b6a5e1d8087a298d5cf630fb2bd39329cdecb82017023f6081a0
+ languageName: node
+ linkType: hard
+
"@jridgewell/trace-mapping@npm:0.3.9":
version: 0.3.9
resolution: "@jridgewell/trace-mapping@npm:0.3.9"
@@ -5317,7 +5776,14 @@ __metadata:
languageName: node
linkType: hard
-"@react-aria/actiongroup@npm:^3.7.22, @react-aria/actiongroup@workspace:packages/@react-aria/actiongroup":
+"@polka/url@npm:^1.0.0-next.24":
+ version: 1.0.0-next.29
+ resolution: "@polka/url@npm:1.0.0-next.29"
+ checksum: 10c0/0d58e081844095cb029d3c19a659bfefd09d5d51a2f791bc61eba7ea826f13d6ee204a8a448c2f5a855c17df07b37517373ff916dd05801063c0568ae9937684
+ languageName: node
+ linkType: hard
+
+"@react-aria/actiongroup@npm:^3.7.22, @react-aria/actiongroup@workspace:packages/@react-aria/actiongroup":
version: 0.0.0-use.local
resolution: "@react-aria/actiongroup@workspace:packages/@react-aria/actiongroup"
dependencies:
@@ -7487,13 +7953,25 @@ __metadata:
"@react-types/textfield": "npm:^3.12.6"
"@storybook/jest": "npm:^0.2.3"
"@testing-library/dom": "npm:^10.1.0"
+ "@testing-library/jest-dom": "npm:^6.6.3"
"@testing-library/react": "npm:^16.0.0"
"@testing-library/user-event": "npm:^14.0.0"
+ "@vitejs/plugin-react": "npm:^4.3.4"
+ "@vitest/browser": "npm:^4.0.0"
+ "@vitest/browser-playwright": "npm:^4.0.0"
+ "@vitest/ui": "npm:^4.0.0"
csstype: "npm:^3.0.2"
- jest: "npm:^29.5.0"
+ glob: "npm:^10.3.0"
+ jsdom: "npm:^25.0.0"
+ playwright: "npm:^1.45.3"
react-aria: "npm:^3.45.0"
react-aria-components: "npm:^1.14.0"
react-stately: "npm:^3.43.0"
+ unplugin-parcel-macros: "npm:^0.1.2-alpha.1"
+ vite: "npm:^6.0.0"
+ vite-plugin-svgr: "npm:^4.3.0"
+ vitest: "npm:^4.0.0"
+ vitest-browser-react: "npm:^2.0.2"
peerDependencies:
react: ^19.0.0-rc.1
react-dom: ^19.0.0-rc.1
@@ -8896,6 +9374,204 @@ __metadata:
languageName: unknown
linkType: soft
+"@rolldown/pluginutils@npm:1.0.0-beta.27":
+ version: 1.0.0-beta.27
+ resolution: "@rolldown/pluginutils@npm:1.0.0-beta.27"
+ checksum: 10c0/9658f235b345201d4f6bfb1f32da9754ca164f892d1cb68154fe5f53c1df42bd675ecd409836dff46884a7847d6c00bdc38af870f7c81e05bba5c2645eb4ab9c
+ languageName: node
+ linkType: hard
+
+"@rollup/pluginutils@npm:^5.2.0":
+ version: 5.3.0
+ resolution: "@rollup/pluginutils@npm:5.3.0"
+ dependencies:
+ "@types/estree": "npm:^1.0.0"
+ estree-walker: "npm:^2.0.2"
+ picomatch: "npm:^4.0.2"
+ peerDependencies:
+ rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
+ peerDependenciesMeta:
+ rollup:
+ optional: true
+ checksum: 10c0/001834bf62d7cf5bac424d2617c113f7f7d3b2bf3c1778cbcccb72cdc957b68989f8e7747c782c2b911f1dde8257f56f8ac1e779e29e74e638e3f1e2cac2bcd0
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-android-arm-eabi@npm:4.55.2":
+ version: 4.55.2
+ resolution: "@rollup/rollup-android-arm-eabi@npm:4.55.2"
+ conditions: os=android & cpu=arm
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-android-arm64@npm:4.55.2":
+ version: 4.55.2
+ resolution: "@rollup/rollup-android-arm64@npm:4.55.2"
+ conditions: os=android & cpu=arm64
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-darwin-arm64@npm:4.55.2":
+ version: 4.55.2
+ resolution: "@rollup/rollup-darwin-arm64@npm:4.55.2"
+ conditions: os=darwin & cpu=arm64
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-darwin-x64@npm:4.55.2":
+ version: 4.55.2
+ resolution: "@rollup/rollup-darwin-x64@npm:4.55.2"
+ conditions: os=darwin & cpu=x64
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-freebsd-arm64@npm:4.55.2":
+ version: 4.55.2
+ resolution: "@rollup/rollup-freebsd-arm64@npm:4.55.2"
+ conditions: os=freebsd & cpu=arm64
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-freebsd-x64@npm:4.55.2":
+ version: 4.55.2
+ resolution: "@rollup/rollup-freebsd-x64@npm:4.55.2"
+ conditions: os=freebsd & cpu=x64
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-linux-arm-gnueabihf@npm:4.55.2":
+ version: 4.55.2
+ resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.55.2"
+ conditions: os=linux & cpu=arm & libc=glibc
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-linux-arm-musleabihf@npm:4.55.2":
+ version: 4.55.2
+ resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.55.2"
+ conditions: os=linux & cpu=arm & libc=musl
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-linux-arm64-gnu@npm:4.55.2":
+ version: 4.55.2
+ resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.55.2"
+ conditions: os=linux & cpu=arm64 & libc=glibc
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-linux-arm64-musl@npm:4.55.2":
+ version: 4.55.2
+ resolution: "@rollup/rollup-linux-arm64-musl@npm:4.55.2"
+ conditions: os=linux & cpu=arm64 & libc=musl
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-linux-loong64-gnu@npm:4.55.2":
+ version: 4.55.2
+ resolution: "@rollup/rollup-linux-loong64-gnu@npm:4.55.2"
+ conditions: os=linux & cpu=loong64 & libc=glibc
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-linux-loong64-musl@npm:4.55.2":
+ version: 4.55.2
+ resolution: "@rollup/rollup-linux-loong64-musl@npm:4.55.2"
+ conditions: os=linux & cpu=loong64 & libc=musl
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-linux-ppc64-gnu@npm:4.55.2":
+ version: 4.55.2
+ resolution: "@rollup/rollup-linux-ppc64-gnu@npm:4.55.2"
+ conditions: os=linux & cpu=ppc64 & libc=glibc
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-linux-ppc64-musl@npm:4.55.2":
+ version: 4.55.2
+ resolution: "@rollup/rollup-linux-ppc64-musl@npm:4.55.2"
+ conditions: os=linux & cpu=ppc64 & libc=musl
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-linux-riscv64-gnu@npm:4.55.2":
+ version: 4.55.2
+ resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.55.2"
+ conditions: os=linux & cpu=riscv64 & libc=glibc
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-linux-riscv64-musl@npm:4.55.2":
+ version: 4.55.2
+ resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.55.2"
+ conditions: os=linux & cpu=riscv64 & libc=musl
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-linux-s390x-gnu@npm:4.55.2":
+ version: 4.55.2
+ resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.55.2"
+ conditions: os=linux & cpu=s390x & libc=glibc
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-linux-x64-gnu@npm:4.55.2":
+ version: 4.55.2
+ resolution: "@rollup/rollup-linux-x64-gnu@npm:4.55.2"
+ conditions: os=linux & cpu=x64 & libc=glibc
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-linux-x64-musl@npm:4.55.2":
+ version: 4.55.2
+ resolution: "@rollup/rollup-linux-x64-musl@npm:4.55.2"
+ conditions: os=linux & cpu=x64 & libc=musl
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-openbsd-x64@npm:4.55.2":
+ version: 4.55.2
+ resolution: "@rollup/rollup-openbsd-x64@npm:4.55.2"
+ conditions: os=openbsd & cpu=x64
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-openharmony-arm64@npm:4.55.2":
+ version: 4.55.2
+ resolution: "@rollup/rollup-openharmony-arm64@npm:4.55.2"
+ conditions: os=openharmony & cpu=arm64
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-win32-arm64-msvc@npm:4.55.2":
+ version: 4.55.2
+ resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.55.2"
+ conditions: os=win32 & cpu=arm64
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-win32-ia32-msvc@npm:4.55.2":
+ version: 4.55.2
+ resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.55.2"
+ conditions: os=win32 & cpu=ia32
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-win32-x64-gnu@npm:4.55.2":
+ version: 4.55.2
+ resolution: "@rollup/rollup-win32-x64-gnu@npm:4.55.2"
+ conditions: os=win32 & cpu=x64
+ languageName: node
+ linkType: hard
+
+"@rollup/rollup-win32-x64-msvc@npm:4.55.2":
+ version: 4.55.2
+ resolution: "@rollup/rollup-win32-x64-msvc@npm:4.55.2"
+ conditions: os=win32 & cpu=x64
+ languageName: node
+ linkType: hard
+
"@rtsao/scc@npm:^1.1.0":
version: 1.1.0
resolution: "@rtsao/scc@npm:1.1.0"
@@ -9081,6 +9757,13 @@ __metadata:
languageName: unknown
linkType: soft
+"@standard-schema/spec@npm:^1.0.0":
+ version: 1.1.0
+ resolution: "@standard-schema/spec@npm:1.1.0"
+ checksum: 10c0/d90f55acde4b2deb983529c87e8025fa693de1a5e8b49ecc6eb84d1fd96328add0e03d7d551442156c7432fd78165b2c26ff561b970a9a881f046abb78d6a526
+ languageName: node
+ linkType: hard
+
"@storybook/addon-a11y@npm:8.6.14":
version: 8.6.14
resolution: "@storybook/addon-a11y@npm:8.6.14"
@@ -9805,6 +10488,13 @@ __metadata:
languageName: node
linkType: hard
+"@swc/core-darwin-arm64@npm:1.15.10":
+ version: 1.15.10
+ resolution: "@swc/core-darwin-arm64@npm:1.15.10"
+ conditions: os=darwin & cpu=arm64
+ languageName: node
+ linkType: hard
+
"@swc/core-darwin-x64@npm:1.12.1":
version: 1.12.1
resolution: "@swc/core-darwin-x64@npm:1.12.1"
@@ -9812,6 +10502,13 @@ __metadata:
languageName: node
linkType: hard
+"@swc/core-darwin-x64@npm:1.15.10":
+ version: 1.15.10
+ resolution: "@swc/core-darwin-x64@npm:1.15.10"
+ conditions: os=darwin & cpu=x64
+ languageName: node
+ linkType: hard
+
"@swc/core-linux-arm-gnueabihf@npm:1.12.1":
version: 1.12.1
resolution: "@swc/core-linux-arm-gnueabihf@npm:1.12.1"
@@ -9819,6 +10516,13 @@ __metadata:
languageName: node
linkType: hard
+"@swc/core-linux-arm-gnueabihf@npm:1.15.10":
+ version: 1.15.10
+ resolution: "@swc/core-linux-arm-gnueabihf@npm:1.15.10"
+ conditions: os=linux & cpu=arm
+ languageName: node
+ linkType: hard
+
"@swc/core-linux-arm64-gnu@npm:1.12.1":
version: 1.12.1
resolution: "@swc/core-linux-arm64-gnu@npm:1.12.1"
@@ -9826,6 +10530,13 @@ __metadata:
languageName: node
linkType: hard
+"@swc/core-linux-arm64-gnu@npm:1.15.10":
+ version: 1.15.10
+ resolution: "@swc/core-linux-arm64-gnu@npm:1.15.10"
+ conditions: os=linux & cpu=arm64 & libc=glibc
+ languageName: node
+ linkType: hard
+
"@swc/core-linux-arm64-musl@npm:1.12.1":
version: 1.12.1
resolution: "@swc/core-linux-arm64-musl@npm:1.12.1"
@@ -9833,6 +10544,13 @@ __metadata:
languageName: node
linkType: hard
+"@swc/core-linux-arm64-musl@npm:1.15.10":
+ version: 1.15.10
+ resolution: "@swc/core-linux-arm64-musl@npm:1.15.10"
+ conditions: os=linux & cpu=arm64 & libc=musl
+ languageName: node
+ linkType: hard
+
"@swc/core-linux-x64-gnu@npm:1.12.1":
version: 1.12.1
resolution: "@swc/core-linux-x64-gnu@npm:1.12.1"
@@ -9840,6 +10558,13 @@ __metadata:
languageName: node
linkType: hard
+"@swc/core-linux-x64-gnu@npm:1.15.10":
+ version: 1.15.10
+ resolution: "@swc/core-linux-x64-gnu@npm:1.15.10"
+ conditions: os=linux & cpu=x64 & libc=glibc
+ languageName: node
+ linkType: hard
+
"@swc/core-linux-x64-musl@npm:1.12.1":
version: 1.12.1
resolution: "@swc/core-linux-x64-musl@npm:1.12.1"
@@ -9847,6 +10572,13 @@ __metadata:
languageName: node
linkType: hard
+"@swc/core-linux-x64-musl@npm:1.15.10":
+ version: 1.15.10
+ resolution: "@swc/core-linux-x64-musl@npm:1.15.10"
+ conditions: os=linux & cpu=x64 & libc=musl
+ languageName: node
+ linkType: hard
+
"@swc/core-win32-arm64-msvc@npm:1.12.1":
version: 1.12.1
resolution: "@swc/core-win32-arm64-msvc@npm:1.12.1"
@@ -9854,6 +10586,13 @@ __metadata:
languageName: node
linkType: hard
+"@swc/core-win32-arm64-msvc@npm:1.15.10":
+ version: 1.15.10
+ resolution: "@swc/core-win32-arm64-msvc@npm:1.15.10"
+ conditions: os=win32 & cpu=arm64
+ languageName: node
+ linkType: hard
+
"@swc/core-win32-ia32-msvc@npm:1.12.1":
version: 1.12.1
resolution: "@swc/core-win32-ia32-msvc@npm:1.12.1"
@@ -9861,6 +10600,13 @@ __metadata:
languageName: node
linkType: hard
+"@swc/core-win32-ia32-msvc@npm:1.15.10":
+ version: 1.15.10
+ resolution: "@swc/core-win32-ia32-msvc@npm:1.15.10"
+ conditions: os=win32 & cpu=ia32
+ languageName: node
+ linkType: hard
+
"@swc/core-win32-x64-msvc@npm:1.12.1":
version: 1.12.1
resolution: "@swc/core-win32-x64-msvc@npm:1.12.1"
@@ -9868,6 +10614,13 @@ __metadata:
languageName: node
linkType: hard
+"@swc/core-win32-x64-msvc@npm:1.15.10":
+ version: 1.15.10
+ resolution: "@swc/core-win32-x64-msvc@npm:1.15.10"
+ conditions: os=win32 & cpu=x64
+ languageName: node
+ linkType: hard
+
"@swc/core@npm:^1.11.24, @swc/core@npm:^1.3.36, @swc/core@npm:^1.5.22":
version: 1.12.1
resolution: "@swc/core@npm:1.12.1"
@@ -9914,6 +10667,52 @@ __metadata:
languageName: node
linkType: hard
+"@swc/core@npm:^1.15.3":
+ version: 1.15.10
+ resolution: "@swc/core@npm:1.15.10"
+ dependencies:
+ "@swc/core-darwin-arm64": "npm:1.15.10"
+ "@swc/core-darwin-x64": "npm:1.15.10"
+ "@swc/core-linux-arm-gnueabihf": "npm:1.15.10"
+ "@swc/core-linux-arm64-gnu": "npm:1.15.10"
+ "@swc/core-linux-arm64-musl": "npm:1.15.10"
+ "@swc/core-linux-x64-gnu": "npm:1.15.10"
+ "@swc/core-linux-x64-musl": "npm:1.15.10"
+ "@swc/core-win32-arm64-msvc": "npm:1.15.10"
+ "@swc/core-win32-ia32-msvc": "npm:1.15.10"
+ "@swc/core-win32-x64-msvc": "npm:1.15.10"
+ "@swc/counter": "npm:^0.1.3"
+ "@swc/types": "npm:^0.1.25"
+ peerDependencies:
+ "@swc/helpers": ">=0.5.17"
+ dependenciesMeta:
+ "@swc/core-darwin-arm64":
+ optional: true
+ "@swc/core-darwin-x64":
+ optional: true
+ "@swc/core-linux-arm-gnueabihf":
+ optional: true
+ "@swc/core-linux-arm64-gnu":
+ optional: true
+ "@swc/core-linux-arm64-musl":
+ optional: true
+ "@swc/core-linux-x64-gnu":
+ optional: true
+ "@swc/core-linux-x64-musl":
+ optional: true
+ "@swc/core-win32-arm64-msvc":
+ optional: true
+ "@swc/core-win32-ia32-msvc":
+ optional: true
+ "@swc/core-win32-x64-msvc":
+ optional: true
+ peerDependenciesMeta:
+ "@swc/helpers":
+ optional: true
+ checksum: 10c0/4802276838dfbdd8fda8429731eec560ad394b65be2ca8a458f2c8fd6475cd879386c599bd05daea7b3de0fd7adf4877cd93dc07992397f45e12a211154193c6
+ languageName: node
+ linkType: hard
+
"@swc/counter@npm:^0.1.3":
version: 0.1.3
resolution: "@swc/counter@npm:0.1.3"
@@ -9952,6 +10751,15 @@ __metadata:
languageName: node
linkType: hard
+"@swc/types@npm:^0.1.25":
+ version: 0.1.25
+ resolution: "@swc/types@npm:0.1.25"
+ dependencies:
+ "@swc/counter": "npm:^0.1.3"
+ checksum: 10c0/847a5b20b131281f89d640a7ed4887fb65724807d53d334b230e84b98c21097aa10cd28a074f9ed287a6ce109e443dd4bafbe7dcfb62333d7806c4ea3e7f8aca
+ languageName: node
+ linkType: hard
+
"@tailwindcss/node@npm:4.0.17":
version: 4.0.17
resolution: "@tailwindcss/node@npm:4.0.17"
@@ -10112,6 +10920,22 @@ __metadata:
languageName: node
linkType: hard
+"@testing-library/dom@npm:^10.4.1":
+ version: 10.4.1
+ resolution: "@testing-library/dom@npm:10.4.1"
+ dependencies:
+ "@babel/code-frame": "npm:^7.10.4"
+ "@babel/runtime": "npm:^7.12.5"
+ "@types/aria-query": "npm:^5.0.1"
+ aria-query: "npm:5.3.0"
+ dom-accessibility-api: "npm:^0.5.9"
+ lz-string: "npm:^1.5.0"
+ picocolors: "npm:1.1.1"
+ pretty-format: "npm:^27.0.2"
+ checksum: 10c0/19ce048012d395ad0468b0dbcc4d0911f6f9e39464d7a8464a587b29707eed5482000dad728f5acc4ed314d2f4d54f34982999a114d2404f36d048278db815b1
+ languageName: node
+ linkType: hard
+
"@testing-library/jest-dom@npm:6.5.0":
version: 6.5.0
resolution: "@testing-library/jest-dom@npm:6.5.0"
@@ -10159,6 +10983,20 @@ __metadata:
languageName: node
linkType: hard
+"@testing-library/jest-dom@npm:^6.6.3":
+ version: 6.9.1
+ resolution: "@testing-library/jest-dom@npm:6.9.1"
+ dependencies:
+ "@adobe/css-tools": "npm:^4.4.0"
+ aria-query: "npm:^5.0.0"
+ css.escape: "npm:^1.5.1"
+ dom-accessibility-api: "npm:^0.6.3"
+ picocolors: "npm:^1.1.1"
+ redent: "npm:^3.0.0"
+ checksum: 10c0/4291ebd2f0f38d14cefac142c56c337941775a5807e2a3d6f1a14c2fbd6be76a18e498ed189e95bedc97d9e8cf1738049bc76c85b5bc5e23fae7c9e10f7b3a12
+ languageName: node
+ linkType: hard
+
"@testing-library/react@npm:^16.0.0":
version: 16.2.0
resolution: "@testing-library/react@npm:16.2.0"
@@ -10266,7 +11104,7 @@ __metadata:
languageName: node
linkType: hard
-"@types/babel__core@npm:^7.1.14":
+"@types/babel__core@npm:^7.1.14, @types/babel__core@npm:^7.20.5":
version: 7.20.5
resolution: "@types/babel__core@npm:7.20.5"
dependencies:
@@ -10307,6 +11145,16 @@ __metadata:
languageName: node
linkType: hard
+"@types/chai@npm:^5.2.2":
+ version: 5.2.3
+ resolution: "@types/chai@npm:5.2.3"
+ dependencies:
+ "@types/deep-eql": "npm:*"
+ assertion-error: "npm:^2.0.1"
+ checksum: 10c0/e0ef1de3b6f8045a5e473e867c8565788c444271409d155588504840ad1a53611011f85072188c2833941189400228c1745d78323dac13fcede9c2b28bacfb2f
+ languageName: node
+ linkType: hard
+
"@types/color-name@npm:^1.1.1":
version: 1.1.1
resolution: "@types/color-name@npm:1.1.1"
@@ -10323,6 +11171,13 @@ __metadata:
languageName: node
linkType: hard
+"@types/deep-eql@npm:*":
+ version: 4.0.2
+ resolution: "@types/deep-eql@npm:4.0.2"
+ checksum: 10c0/bf3f811843117900d7084b9d0c852da9a044d12eb40e6de73b552598a6843c21291a8a381b0532644574beecd5e3491c5ff3a0365ab86b15d59862c025384844
+ languageName: node
+ linkType: hard
+
"@types/estree-jsx@npm:^0.0.1":
version: 0.0.1
resolution: "@types/estree-jsx@npm:0.0.1"
@@ -10339,6 +11194,13 @@ __metadata:
languageName: node
linkType: hard
+"@types/estree@npm:1.0.8, @types/estree@npm:^1.0.0":
+ version: 1.0.8
+ resolution: "@types/estree@npm:1.0.8"
+ checksum: 10c0/39d34d1afaa338ab9763f37ad6066e3f349444f9052b9676a7cc0252ef9485a41c6d81c9c4e0d26e9077993354edf25efc853f3224dd4b447175ef62bdcc86a5
+ languageName: node
+ linkType: hard
+
"@types/estree@npm:^0.0.46":
version: 0.0.46
resolution: "@types/estree@npm:0.0.46"
@@ -10353,13 +11215,6 @@ __metadata:
languageName: node
linkType: hard
-"@types/estree@npm:^1.0.0":
- version: 1.0.8
- resolution: "@types/estree@npm:1.0.8"
- checksum: 10c0/39d34d1afaa338ab9763f37ad6066e3f349444f9052b9676a7cc0252ef9485a41c6d81c9c4e0d26e9077993354edf25efc853f3224dd4b447175ef62bdcc86a5
- languageName: node
- linkType: hard
-
"@types/extend@npm:^3.0.0":
version: 3.0.1
resolution: "@types/extend@npm:3.0.1"
@@ -11210,6 +12065,70 @@ __metadata:
languageName: node
linkType: hard
+"@vitejs/plugin-react@npm:^4.3.4":
+ version: 4.7.0
+ resolution: "@vitejs/plugin-react@npm:4.7.0"
+ dependencies:
+ "@babel/core": "npm:^7.28.0"
+ "@babel/plugin-transform-react-jsx-self": "npm:^7.27.1"
+ "@babel/plugin-transform-react-jsx-source": "npm:^7.27.1"
+ "@rolldown/pluginutils": "npm:1.0.0-beta.27"
+ "@types/babel__core": "npm:^7.20.5"
+ react-refresh: "npm:^0.17.0"
+ peerDependencies:
+ vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0
+ checksum: 10c0/692f23960972879485d647713663ec299c478222c96567d60285acf7c7dc5c178e71abfe9d2eefddef1eeb01514dacbc2ed68aad84628debf9c7116134734253
+ languageName: node
+ linkType: hard
+
+"@vitest/browser-playwright@npm:^4.0.0, @vitest/browser-playwright@npm:^4.0.17":
+ version: 4.0.17
+ resolution: "@vitest/browser-playwright@npm:4.0.17"
+ dependencies:
+ "@vitest/browser": "npm:4.0.17"
+ "@vitest/mocker": "npm:4.0.17"
+ tinyrainbow: "npm:^3.0.3"
+ peerDependencies:
+ playwright: "*"
+ vitest: 4.0.17
+ peerDependenciesMeta:
+ playwright:
+ optional: false
+ checksum: 10c0/c05da663d06d5c5f164ea14cd46cf6fa6a4d5e4b1d2bb1319be330a40ea48573783f0ee908f15b4ebe1ff9f254283e58949cb2c11b53db65307fa2f4f5551f45
+ languageName: node
+ linkType: hard
+
+"@vitest/browser-preview@npm:^4.0.17":
+ version: 4.0.17
+ resolution: "@vitest/browser-preview@npm:4.0.17"
+ dependencies:
+ "@testing-library/dom": "npm:^10.4.1"
+ "@testing-library/user-event": "npm:^14.6.1"
+ "@vitest/browser": "npm:4.0.17"
+ peerDependencies:
+ vitest: 4.0.17
+ checksum: 10c0/223e6ca1d32f57e2461c534b00e9d1b68e57cfa75e49ed93f4b5daf15c2b2a03f27702e7aec04b4daa72081068db7fcb50279730a5902c6046aedd3524a0ed1f
+ languageName: node
+ linkType: hard
+
+"@vitest/browser@npm:4.0.17, @vitest/browser@npm:^4.0.0":
+ version: 4.0.17
+ resolution: "@vitest/browser@npm:4.0.17"
+ dependencies:
+ "@vitest/mocker": "npm:4.0.17"
+ "@vitest/utils": "npm:4.0.17"
+ magic-string: "npm:^0.30.21"
+ pixelmatch: "npm:7.1.0"
+ pngjs: "npm:^7.0.0"
+ sirv: "npm:^3.0.2"
+ tinyrainbow: "npm:^3.0.3"
+ ws: "npm:^8.18.3"
+ peerDependencies:
+ vitest: 4.0.17
+ checksum: 10c0/f2abda1073403ad00d16b8eba4646e618ef6c12e9882849817c4a9443f19f6f5ea73352736d77519aa468db235d055415780f16b2ca1ba77eaf44da271a9a17f
+ languageName: node
+ linkType: hard
+
"@vitest/expect@npm:2.0.5":
version: 2.0.5
resolution: "@vitest/expect@npm:2.0.5"
@@ -11222,6 +12141,39 @@ __metadata:
languageName: node
linkType: hard
+"@vitest/expect@npm:4.0.17":
+ version: 4.0.17
+ resolution: "@vitest/expect@npm:4.0.17"
+ dependencies:
+ "@standard-schema/spec": "npm:^1.0.0"
+ "@types/chai": "npm:^5.2.2"
+ "@vitest/spy": "npm:4.0.17"
+ "@vitest/utils": "npm:4.0.17"
+ chai: "npm:^6.2.1"
+ tinyrainbow: "npm:^3.0.3"
+ checksum: 10c0/cdaa6827aa3a9473d51fd0944bcd698a94507929fa3c98b00bbdb74342319ec04279f01108d7d2dd7cbcd0d8062f65a3f21bb3615c0d5223e61adcc036c8b370
+ languageName: node
+ linkType: hard
+
+"@vitest/mocker@npm:4.0.17":
+ version: 4.0.17
+ resolution: "@vitest/mocker@npm:4.0.17"
+ dependencies:
+ "@vitest/spy": "npm:4.0.17"
+ estree-walker: "npm:^3.0.3"
+ magic-string: "npm:^0.30.21"
+ peerDependencies:
+ msw: ^2.4.9
+ vite: ^6.0.0 || ^7.0.0-0
+ peerDependenciesMeta:
+ msw:
+ optional: true
+ vite:
+ optional: true
+ checksum: 10c0/54e657fa5b79764926b15aac993528bfe7083f6731209253617b1f27d328aa3297fcbf96b67e84d1a5632553231f795585f2396f563837cf117a574c87f5cef7
+ languageName: node
+ linkType: hard
+
"@vitest/pretty-format@npm:2.0.5":
version: 2.0.5
resolution: "@vitest/pretty-format@npm:2.0.5"
@@ -11240,6 +12192,36 @@ __metadata:
languageName: node
linkType: hard
+"@vitest/pretty-format@npm:4.0.17":
+ version: 4.0.17
+ resolution: "@vitest/pretty-format@npm:4.0.17"
+ dependencies:
+ tinyrainbow: "npm:^3.0.3"
+ checksum: 10c0/10a2dd7e2daf7ee006107d380bbd28b66b09a7014d31087daab0dea7dee0d12868cfcf6b3372729268502fd9065162345b68b9b9c5d225f5c6c2fd2c664a2a86
+ languageName: node
+ linkType: hard
+
+"@vitest/runner@npm:4.0.17":
+ version: 4.0.17
+ resolution: "@vitest/runner@npm:4.0.17"
+ dependencies:
+ "@vitest/utils": "npm:4.0.17"
+ pathe: "npm:^2.0.3"
+ checksum: 10c0/f4ccc236d1ed5ba2186d5f36ff0306d4ac7b711a40d7316ad6fd71c0f7229482b19969a8737e87670f3d4efb08f2138ff5b47a744fd7ae8db6c03cf991293a04
+ languageName: node
+ linkType: hard
+
+"@vitest/snapshot@npm:4.0.17":
+ version: 4.0.17
+ resolution: "@vitest/snapshot@npm:4.0.17"
+ dependencies:
+ "@vitest/pretty-format": "npm:4.0.17"
+ magic-string: "npm:^0.30.21"
+ pathe: "npm:^2.0.3"
+ checksum: 10c0/31a047a097b13eff6c0f5393ea3e7203771ae9a22afe6465cd9023fd2ed516ddccd84523d48504a032c9d04a86a12e3f1235e08bb2ffc7d7a125e372c41ef53d
+ languageName: node
+ linkType: hard
+
"@vitest/spy@npm:2.0.5":
version: 2.0.5
resolution: "@vitest/spy@npm:2.0.5"
@@ -11249,6 +12231,30 @@ __metadata:
languageName: node
linkType: hard
+"@vitest/spy@npm:4.0.17":
+ version: 4.0.17
+ resolution: "@vitest/spy@npm:4.0.17"
+ checksum: 10c0/c290731ba3392f11eaba8fc7fa08063a3a4d14af6baeec210b260ccd5a46613196fb4a8ff3ac8bf91a9606aef90eee9b6364bda130ce71abff368e35dfe2b265
+ languageName: node
+ linkType: hard
+
+"@vitest/ui@npm:^4.0.0":
+ version: 4.0.17
+ resolution: "@vitest/ui@npm:4.0.17"
+ dependencies:
+ "@vitest/utils": "npm:4.0.17"
+ fflate: "npm:^0.8.2"
+ flatted: "npm:^3.3.3"
+ pathe: "npm:^2.0.3"
+ sirv: "npm:^3.0.2"
+ tinyglobby: "npm:^0.2.15"
+ tinyrainbow: "npm:^3.0.3"
+ peerDependencies:
+ vitest: 4.0.17
+ checksum: 10c0/30479cc76defbb86bbc4af33483d21c769e5bef44ff93b291eb6bc1d4a715303dfbea91b651c2d639a29b2d2c596835bf45d09a3cf116c53525e5409eac952b6
+ languageName: node
+ linkType: hard
+
"@vitest/utils@npm:2.0.5":
version: 2.0.5
resolution: "@vitest/utils@npm:2.0.5"
@@ -11261,6 +12267,16 @@ __metadata:
languageName: node
linkType: hard
+"@vitest/utils@npm:4.0.17":
+ version: 4.0.17
+ resolution: "@vitest/utils@npm:4.0.17"
+ dependencies:
+ "@vitest/pretty-format": "npm:4.0.17"
+ tinyrainbow: "npm:^3.0.3"
+ checksum: 10c0/1e2e4d7d7709ec022f603a1e12015523a2290f326c0bbe0c6bd5481ec396d4efc6bf8c738d601915d88e74267e9841df1e05157edced10f5048865204aeb86ff
+ languageName: node
+ linkType: hard
+
"@vitest/utils@npm:^2.1.1":
version: 2.1.9
resolution: "@vitest/utils@npm:2.1.9"
@@ -11434,6 +12450,13 @@ __metadata:
languageName: node
linkType: hard
+"agent-base@npm:^7.1.2":
+ version: 7.1.4
+ resolution: "agent-base@npm:7.1.4"
+ checksum: 10c0/c2c9ab7599692d594b6a161559ada307b7a624fa4c7b03e3afdb5a5e31cd0e53269115b620fcab024c5ac6a6f37fa5eb2e004f076ad30f5f7e6b8b671f7b35fe
+ languageName: node
+ linkType: hard
+
"agent-base@npm:~4.2.1":
version: 4.2.1
resolution: "agent-base@npm:4.2.1"
@@ -13004,6 +14027,13 @@ __metadata:
languageName: node
linkType: hard
+"chai@npm:^6.2.1":
+ version: 6.2.2
+ resolution: "chai@npm:6.2.2"
+ checksum: 10c0/e6c69e5f0c11dffe6ea13d0290936ebb68fcc1ad688b8e952e131df6a6d5797d5e860bc55cef1aca2e950c3e1f96daf79e9d5a70fb7dbaab4e46355e2635ed53
+ languageName: node
+ linkType: hard
+
"chalk@npm:^1.1.3":
version: 1.1.3
resolution: "chalk@npm:1.1.3"
@@ -14214,6 +15244,16 @@ __metadata:
languageName: node
linkType: hard
+"cssstyle@npm:^4.1.0":
+ version: 4.6.0
+ resolution: "cssstyle@npm:4.6.0"
+ dependencies:
+ "@asamuzakjp/css-color": "npm:^3.2.0"
+ rrweb-cssom: "npm:^0.8.0"
+ checksum: 10c0/71add1b0ffafa1bedbef6855db6189b9523d3320e015a0bf3fbd504760efb9a81e1f1a225228d5fa892ee58e56d06994ca372e7f4e461cda7c4c9985fe075f65
+ languageName: node
+ linkType: hard
+
"csstype@npm:^3.0.2":
version: 3.0.3
resolution: "csstype@npm:3.0.3"
@@ -14283,6 +15323,16 @@ __metadata:
languageName: node
linkType: hard
+"data-urls@npm:^5.0.0":
+ version: 5.0.0
+ resolution: "data-urls@npm:5.0.0"
+ dependencies:
+ whatwg-mimetype: "npm:^4.0.0"
+ whatwg-url: "npm:^14.0.0"
+ checksum: 10c0/1b894d7d41c861f3a4ed2ae9b1c3f0909d4575ada02e36d3d3bc584bdd84278e20709070c79c3b3bff7ac98598cb191eb3e86a89a79ea4ee1ef360e1694f92ad
+ languageName: node
+ linkType: hard
+
"data-view-buffer@npm:^1.0.1":
version: 1.0.1
resolution: "data-view-buffer@npm:1.0.1"
@@ -14443,6 +15493,13 @@ __metadata:
languageName: node
linkType: hard
+"decimal.js@npm:^10.4.3":
+ version: 10.6.0
+ resolution: "decimal.js@npm:10.6.0"
+ checksum: 10c0/07d69fbcc54167a340d2d97de95f546f9ff1f69d2b45a02fd7a5292412df3cd9eb7e23065e532a318f5474a2e1bccf8392fdf0443ef467f97f3bf8cb0477e5aa
+ languageName: node
+ linkType: hard
+
"decode-named-character-reference@npm:^1.0.0":
version: 1.0.1
resolution: "decode-named-character-reference@npm:1.0.1"
@@ -15137,6 +16194,13 @@ __metadata:
languageName: node
linkType: hard
+"entities@npm:^6.0.0":
+ version: 6.0.1
+ resolution: "entities@npm:6.0.1"
+ checksum: 10c0/ed836ddac5acb34341094eb495185d527bd70e8632b6c0d59548cbfa23defdbae70b96f9a405c82904efa421230b5b3fd2283752447d737beffd3f3e6ee74414
+ languageName: node
+ linkType: hard
+
"env-paths@npm:^1.0.0":
version: 1.0.0
resolution: "env-paths@npm:1.0.0"
@@ -15297,6 +16361,13 @@ __metadata:
languageName: node
linkType: hard
+"es-module-lexer@npm:^1.7.0":
+ version: 1.7.0
+ resolution: "es-module-lexer@npm:1.7.0"
+ checksum: 10c0/4c935affcbfeba7fb4533e1da10fa8568043df1e3574b869385980de9e2d475ddc36769891936dbb07036edb3c3786a8b78ccf44964cd130dedc1f2c984b6c7b
+ languageName: node
+ linkType: hard
+
"es-object-atoms@npm:^1.0.0, es-object-atoms@npm:^1.1.1":
version: 1.1.1
resolution: "es-object-atoms@npm:1.1.1"
@@ -15458,6 +16529,184 @@ __metadata:
languageName: node
linkType: hard
+"esbuild@npm:^0.25.0":
+ version: 0.25.12
+ resolution: "esbuild@npm:0.25.12"
+ dependencies:
+ "@esbuild/aix-ppc64": "npm:0.25.12"
+ "@esbuild/android-arm": "npm:0.25.12"
+ "@esbuild/android-arm64": "npm:0.25.12"
+ "@esbuild/android-x64": "npm:0.25.12"
+ "@esbuild/darwin-arm64": "npm:0.25.12"
+ "@esbuild/darwin-x64": "npm:0.25.12"
+ "@esbuild/freebsd-arm64": "npm:0.25.12"
+ "@esbuild/freebsd-x64": "npm:0.25.12"
+ "@esbuild/linux-arm": "npm:0.25.12"
+ "@esbuild/linux-arm64": "npm:0.25.12"
+ "@esbuild/linux-ia32": "npm:0.25.12"
+ "@esbuild/linux-loong64": "npm:0.25.12"
+ "@esbuild/linux-mips64el": "npm:0.25.12"
+ "@esbuild/linux-ppc64": "npm:0.25.12"
+ "@esbuild/linux-riscv64": "npm:0.25.12"
+ "@esbuild/linux-s390x": "npm:0.25.12"
+ "@esbuild/linux-x64": "npm:0.25.12"
+ "@esbuild/netbsd-arm64": "npm:0.25.12"
+ "@esbuild/netbsd-x64": "npm:0.25.12"
+ "@esbuild/openbsd-arm64": "npm:0.25.12"
+ "@esbuild/openbsd-x64": "npm:0.25.12"
+ "@esbuild/openharmony-arm64": "npm:0.25.12"
+ "@esbuild/sunos-x64": "npm:0.25.12"
+ "@esbuild/win32-arm64": "npm:0.25.12"
+ "@esbuild/win32-ia32": "npm:0.25.12"
+ "@esbuild/win32-x64": "npm:0.25.12"
+ dependenciesMeta:
+ "@esbuild/aix-ppc64":
+ optional: true
+ "@esbuild/android-arm":
+ optional: true
+ "@esbuild/android-arm64":
+ optional: true
+ "@esbuild/android-x64":
+ optional: true
+ "@esbuild/darwin-arm64":
+ optional: true
+ "@esbuild/darwin-x64":
+ optional: true
+ "@esbuild/freebsd-arm64":
+ optional: true
+ "@esbuild/freebsd-x64":
+ optional: true
+ "@esbuild/linux-arm":
+ optional: true
+ "@esbuild/linux-arm64":
+ optional: true
+ "@esbuild/linux-ia32":
+ optional: true
+ "@esbuild/linux-loong64":
+ optional: true
+ "@esbuild/linux-mips64el":
+ optional: true
+ "@esbuild/linux-ppc64":
+ optional: true
+ "@esbuild/linux-riscv64":
+ optional: true
+ "@esbuild/linux-s390x":
+ optional: true
+ "@esbuild/linux-x64":
+ optional: true
+ "@esbuild/netbsd-arm64":
+ optional: true
+ "@esbuild/netbsd-x64":
+ optional: true
+ "@esbuild/openbsd-arm64":
+ optional: true
+ "@esbuild/openbsd-x64":
+ optional: true
+ "@esbuild/openharmony-arm64":
+ optional: true
+ "@esbuild/sunos-x64":
+ optional: true
+ "@esbuild/win32-arm64":
+ optional: true
+ "@esbuild/win32-ia32":
+ optional: true
+ "@esbuild/win32-x64":
+ optional: true
+ bin:
+ esbuild: bin/esbuild
+ checksum: 10c0/c205357531423220a9de8e1e6c6514242bc9b1666e762cd67ccdf8fdfdc3f1d0bd76f8d9383958b97ad4c953efdb7b6e8c1f9ca5951cd2b7c5235e8755b34a6b
+ languageName: node
+ linkType: hard
+
+"esbuild@npm:^0.27.0":
+ version: 0.27.2
+ resolution: "esbuild@npm:0.27.2"
+ dependencies:
+ "@esbuild/aix-ppc64": "npm:0.27.2"
+ "@esbuild/android-arm": "npm:0.27.2"
+ "@esbuild/android-arm64": "npm:0.27.2"
+ "@esbuild/android-x64": "npm:0.27.2"
+ "@esbuild/darwin-arm64": "npm:0.27.2"
+ "@esbuild/darwin-x64": "npm:0.27.2"
+ "@esbuild/freebsd-arm64": "npm:0.27.2"
+ "@esbuild/freebsd-x64": "npm:0.27.2"
+ "@esbuild/linux-arm": "npm:0.27.2"
+ "@esbuild/linux-arm64": "npm:0.27.2"
+ "@esbuild/linux-ia32": "npm:0.27.2"
+ "@esbuild/linux-loong64": "npm:0.27.2"
+ "@esbuild/linux-mips64el": "npm:0.27.2"
+ "@esbuild/linux-ppc64": "npm:0.27.2"
+ "@esbuild/linux-riscv64": "npm:0.27.2"
+ "@esbuild/linux-s390x": "npm:0.27.2"
+ "@esbuild/linux-x64": "npm:0.27.2"
+ "@esbuild/netbsd-arm64": "npm:0.27.2"
+ "@esbuild/netbsd-x64": "npm:0.27.2"
+ "@esbuild/openbsd-arm64": "npm:0.27.2"
+ "@esbuild/openbsd-x64": "npm:0.27.2"
+ "@esbuild/openharmony-arm64": "npm:0.27.2"
+ "@esbuild/sunos-x64": "npm:0.27.2"
+ "@esbuild/win32-arm64": "npm:0.27.2"
+ "@esbuild/win32-ia32": "npm:0.27.2"
+ "@esbuild/win32-x64": "npm:0.27.2"
+ dependenciesMeta:
+ "@esbuild/aix-ppc64":
+ optional: true
+ "@esbuild/android-arm":
+ optional: true
+ "@esbuild/android-arm64":
+ optional: true
+ "@esbuild/android-x64":
+ optional: true
+ "@esbuild/darwin-arm64":
+ optional: true
+ "@esbuild/darwin-x64":
+ optional: true
+ "@esbuild/freebsd-arm64":
+ optional: true
+ "@esbuild/freebsd-x64":
+ optional: true
+ "@esbuild/linux-arm":
+ optional: true
+ "@esbuild/linux-arm64":
+ optional: true
+ "@esbuild/linux-ia32":
+ optional: true
+ "@esbuild/linux-loong64":
+ optional: true
+ "@esbuild/linux-mips64el":
+ optional: true
+ "@esbuild/linux-ppc64":
+ optional: true
+ "@esbuild/linux-riscv64":
+ optional: true
+ "@esbuild/linux-s390x":
+ optional: true
+ "@esbuild/linux-x64":
+ optional: true
+ "@esbuild/netbsd-arm64":
+ optional: true
+ "@esbuild/netbsd-x64":
+ optional: true
+ "@esbuild/openbsd-arm64":
+ optional: true
+ "@esbuild/openbsd-x64":
+ optional: true
+ "@esbuild/openharmony-arm64":
+ optional: true
+ "@esbuild/sunos-x64":
+ optional: true
+ "@esbuild/win32-arm64":
+ optional: true
+ "@esbuild/win32-ia32":
+ optional: true
+ "@esbuild/win32-x64":
+ optional: true
+ bin:
+ esbuild: bin/esbuild
+ checksum: 10c0/cf83f626f55500f521d5fe7f4bc5871bec240d3deb2a01fbd379edc43b3664d1167428738a5aad8794b35d1cca985c44c375b1cd38a2ca613c77ced2c83aafcd
+ languageName: node
+ linkType: hard
+
"escalade@npm:^3.1.1, escalade@npm:^3.2.0":
version: 3.2.0
resolution: "escalade@npm:3.2.0"
@@ -15872,6 +17121,13 @@ __metadata:
languageName: node
linkType: hard
+"estree-walker@npm:^2.0.2":
+ version: 2.0.2
+ resolution: "estree-walker@npm:2.0.2"
+ checksum: 10c0/53a6c54e2019b8c914dc395890153ffdc2322781acf4bd7d1a32d7aedc1710807bdcd866ac133903d5629ec601fbb50abe8c2e5553c7f5a0afdd9b6af6c945af
+ languageName: node
+ linkType: hard
+
"estree-walker@npm:^3.0.0, estree-walker@npm:^3.0.3":
version: 3.0.3
resolution: "estree-walker@npm:3.0.3"
@@ -16033,6 +17289,13 @@ __metadata:
languageName: node
linkType: hard
+"expect-type@npm:^1.2.2":
+ version: 1.3.0
+ resolution: "expect-type@npm:1.3.0"
+ checksum: 10c0/8412b3fe4f392c420ab41dae220b09700e4e47c639a29ba7ba2e83cc6cffd2b4926f7ac9e47d7e277e8f4f02acda76fd6931cb81fd2b382fa9477ef9ada953fd
+ languageName: node
+ linkType: hard
+
"expect@npm:^29.7.0":
version: 29.7.0
resolution: "expect@npm:29.7.0"
@@ -16340,6 +17603,18 @@ __metadata:
languageName: node
linkType: hard
+"fdir@npm:^6.4.4, fdir@npm:^6.5.0":
+ version: 6.5.0
+ resolution: "fdir@npm:6.5.0"
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+ checksum: 10c0/e345083c4306b3aed6cb8ec551e26c36bab5c511e99ea4576a16750ddc8d3240e63826cc624f5ae17ad4dc82e68a253213b60d556c11bfad064b7607847ed07f
+ languageName: node
+ linkType: hard
+
"fflate@npm:^0.7.3":
version: 0.7.4
resolution: "fflate@npm:0.7.4"
@@ -16347,6 +17622,13 @@ __metadata:
languageName: node
linkType: hard
+"fflate@npm:^0.8.2":
+ version: 0.8.2
+ resolution: "fflate@npm:0.8.2"
+ checksum: 10c0/03448d630c0a583abea594835a9fdb2aaf7d67787055a761515bf4ed862913cfd693b4c4ffd5c3f3b355a70cf1e19033e9ae5aedcca103188aaff91b8bd6e293
+ languageName: node
+ linkType: hard
+
"figgy-pudding@npm:^3.4.1, figgy-pudding@npm:^3.5.1":
version: 3.5.1
resolution: "figgy-pudding@npm:3.5.1"
@@ -16583,6 +17865,13 @@ __metadata:
languageName: node
linkType: hard
+"flatted@npm:^3.3.3":
+ version: 3.3.3
+ resolution: "flatted@npm:3.3.3"
+ checksum: 10c0/e957a1c6b0254aa15b8cce8533e24165abd98fadc98575db082b786b5da1b7d72062b81bfdcd1da2f4d46b6ed93bec2434e62333e9b4261d79ef2e75a10dd538
+ languageName: node
+ linkType: hard
+
"flow-parser@npm:0.*":
version: 0.235.1
resolution: "flow-parser@npm:0.235.1"
@@ -16884,6 +18173,16 @@ __metadata:
languageName: node
linkType: hard
+"fsevents@npm:~2.3.3":
+ version: 2.3.3
+ resolution: "fsevents@npm:2.3.3"
+ dependencies:
+ node-gyp: "npm:latest"
+ checksum: 10c0/a1f0c44595123ed717febbc478aa952e47adfc28e2092be66b8ab1635147254ca6cfe1df792a8997f22716d4cbafc73309899ff7bfac2ac3ad8cf2e4ecc3ec60
+ conditions: os=darwin
+ languageName: node
+ linkType: hard
+
"fsevents@patch:fsevents@npm%3A2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A^2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin":
version: 2.3.2
resolution: "fsevents@patch:fsevents@npm%3A2.3.2#optional!builtin::version=2.3.2&hash=df0bf1"
@@ -16893,6 +18192,15 @@ __metadata:
languageName: node
linkType: hard
+"fsevents@patch:fsevents@npm%3A~2.3.3#optional!builtin":
+ version: 2.3.3
+ resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1"
+ dependencies:
+ node-gyp: "npm:latest"
+ conditions: os=darwin
+ languageName: node
+ linkType: hard
+
"function-bind@npm:^1.1.2":
version: 1.1.2
resolution: "function-bind@npm:1.1.2"
@@ -17230,6 +18538,22 @@ __metadata:
languageName: node
linkType: hard
+"glob@npm:^10.3.0":
+ version: 10.5.0
+ resolution: "glob@npm:10.5.0"
+ dependencies:
+ foreground-child: "npm:^3.1.0"
+ jackspeak: "npm:^3.1.2"
+ minimatch: "npm:^9.0.4"
+ minipass: "npm:^7.1.2"
+ package-json-from-dist: "npm:^1.0.0"
+ path-scurry: "npm:^1.11.1"
+ bin:
+ glob: dist/esm/bin.mjs
+ checksum: 10c0/100705eddbde6323e7b35e1d1ac28bcb58322095bd8e63a7d0bef1a2cdafe0d0f7922a981b2b48369a4f8c1b077be5c171804534c3509dfe950dde15fbe6d828
+ languageName: node
+ linkType: hard
+
"glob@npm:^11.0.0, glob@npm:^11.0.3":
version: 11.0.3
resolution: "glob@npm:11.0.3"
@@ -17919,6 +19243,15 @@ __metadata:
languageName: node
linkType: hard
+"html-encoding-sniffer@npm:^4.0.0":
+ version: 4.0.0
+ resolution: "html-encoding-sniffer@npm:4.0.0"
+ dependencies:
+ whatwg-encoding: "npm:^3.1.1"
+ checksum: 10c0/523398055dc61ac9b34718a719cb4aa691e4166f29187e211e1607de63dc25ac7af52ca7c9aead0c4b3c0415ffecb17326396e1202e2e86ff4bca4c0ee4c6140
+ languageName: node
+ linkType: hard
+
"html-escaper@npm:^2.0.0":
version: 2.0.2
resolution: "html-escaper@npm:2.0.2"
@@ -17995,7 +19328,7 @@ __metadata:
languageName: node
linkType: hard
-"http-proxy-agent@npm:^7.0.0":
+"http-proxy-agent@npm:^7.0.0, http-proxy-agent@npm:^7.0.2":
version: 7.0.2
resolution: "http-proxy-agent@npm:7.0.2"
dependencies:
@@ -18100,6 +19433,16 @@ __metadata:
languageName: node
linkType: hard
+"https-proxy-agent@npm:^7.0.5":
+ version: 7.0.6
+ resolution: "https-proxy-agent@npm:7.0.6"
+ dependencies:
+ agent-base: "npm:^7.1.2"
+ debug: "npm:4"
+ checksum: 10c0/f729219bc735edb621fa30e6e84e60ee5d00802b8247aac0d7b79b0bd6d4b3294737a337b93b86a0bd9e68099d031858a39260c976dc14cdbba238ba1f8779ac
+ languageName: node
+ linkType: hard
+
"human-signals@npm:^2.1.0":
version: 2.1.0
resolution: "human-signals@npm:2.1.0"
@@ -20086,6 +21429,40 @@ __metadata:
languageName: node
linkType: hard
+"jsdom@npm:^25.0.0":
+ version: 25.0.1
+ resolution: "jsdom@npm:25.0.1"
+ dependencies:
+ cssstyle: "npm:^4.1.0"
+ data-urls: "npm:^5.0.0"
+ decimal.js: "npm:^10.4.3"
+ form-data: "npm:^4.0.0"
+ html-encoding-sniffer: "npm:^4.0.0"
+ http-proxy-agent: "npm:^7.0.2"
+ https-proxy-agent: "npm:^7.0.5"
+ is-potential-custom-element-name: "npm:^1.0.1"
+ nwsapi: "npm:^2.2.12"
+ parse5: "npm:^7.1.2"
+ rrweb-cssom: "npm:^0.7.1"
+ saxes: "npm:^6.0.0"
+ symbol-tree: "npm:^3.2.4"
+ tough-cookie: "npm:^5.0.0"
+ w3c-xmlserializer: "npm:^5.0.0"
+ webidl-conversions: "npm:^7.0.0"
+ whatwg-encoding: "npm:^3.1.1"
+ whatwg-mimetype: "npm:^4.0.0"
+ whatwg-url: "npm:^14.0.0"
+ ws: "npm:^8.18.0"
+ xml-name-validator: "npm:^5.0.0"
+ peerDependencies:
+ canvas: ^2.11.2
+ peerDependenciesMeta:
+ canvas:
+ optional: true
+ checksum: 10c0/6bda32a6dfe4e37a30568bf51136bdb3ba9c0b72aadd6356280404275a34c9e097c8c25b5eb3c742e602623741e172da977ff456684befd77c9042ed9bf8c2b4
+ languageName: node
+ linkType: hard
+
"jsesc@npm:^2.5.1":
version: 2.5.2
resolution: "jsesc@npm:2.5.2"
@@ -20941,6 +22318,13 @@ __metadata:
languageName: node
linkType: hard
+"lru-cache@npm:^10.4.3":
+ version: 10.4.3
+ resolution: "lru-cache@npm:10.4.3"
+ checksum: 10c0/ebd04fbca961e6c1d6c0af3799adcc966a1babe798f685bb84e6599266599cd95d94630b10262f5424539bc4640107e8a33aa28585374abf561d30d16f4b39fb
+ languageName: node
+ linkType: hard
+
"lru-cache@npm:^11.0.0":
version: 11.2.2
resolution: "lru-cache@npm:11.2.2"
@@ -20992,6 +22376,15 @@ __metadata:
languageName: node
linkType: hard
+"magic-string@npm:^0.30.21":
+ version: 0.30.21
+ resolution: "magic-string@npm:0.30.21"
+ dependencies:
+ "@jridgewell/sourcemap-codec": "npm:^1.5.5"
+ checksum: 10c0/299378e38f9a270069fc62358522ddfb44e94244baa0d6a8980ab2a9b2490a1d03b236b447eee309e17eb3bddfa482c61259d47960eb018a904f0ded52780c4a
+ languageName: node
+ linkType: hard
+
"make-dir@npm:^1.0.0":
version: 1.3.0
resolution: "make-dir@npm:1.3.0"
@@ -22273,6 +23666,13 @@ __metadata:
languageName: node
linkType: hard
+"mrmime@npm:^2.0.0":
+ version: 2.0.1
+ resolution: "mrmime@npm:2.0.1"
+ checksum: 10c0/af05afd95af202fdd620422f976ad67dc18e6ee29beb03dd1ce950ea6ef664de378e44197246df4c7cdd73d47f2e7143a6e26e473084b9e4aa2095c0ad1e1761
+ languageName: node
+ linkType: hard
+
"ms@npm:2.0.0":
version: 2.0.0
resolution: "ms@npm:2.0.0"
@@ -22427,6 +23827,13 @@ __metadata:
languageName: node
linkType: hard
+"napi-wasm@npm:^1.0.1":
+ version: 1.1.3
+ resolution: "napi-wasm@npm:1.1.3"
+ checksum: 10c0/7c365ab9dc59e6f20d7b7886279ecc03ffc7c3d502ed66d32652e3681c3a56c372f00f29b110aefd9b074a6bab6a997e9b602968c18622e2586818f417e41a5d
+ languageName: node
+ linkType: hard
+
"natural-compare@npm:^1.4.0":
version: 1.4.0
resolution: "natural-compare@npm:1.4.0"
@@ -22868,6 +24275,13 @@ __metadata:
languageName: node
linkType: hard
+"nwsapi@npm:^2.2.12":
+ version: 2.2.23
+ resolution: "nwsapi@npm:2.2.23"
+ checksum: 10c0/e44bfc9246baf659581206ed716d291a1905185247795fb8a302cb09315c943a31023b4ac4d026a5eaf32b2def51d77b3d0f9ebf4f3d35f70e105fcb6447c76e
+ languageName: node
+ linkType: hard
+
"nwsapi@npm:^2.2.2":
version: 2.2.4
resolution: "nwsapi@npm:2.2.4"
@@ -23065,6 +24479,13 @@ __metadata:
languageName: node
linkType: hard
+"obug@npm:^2.1.1":
+ version: 2.1.1
+ resolution: "obug@npm:2.1.1"
+ checksum: 10c0/59dccd7de72a047e08f8649e94c1015ec72f94eefb6ddb57fb4812c4b425a813bc7e7cd30c9aca20db3c59abc3c85cc7a62bb656a968741d770f4e8e02bc2e78
+ languageName: node
+ linkType: hard
+
"octokit-pagination-methods@npm:^1.1.0":
version: 1.1.0
resolution: "octokit-pagination-methods@npm:1.1.0"
@@ -23698,6 +25119,15 @@ __metadata:
languageName: node
linkType: hard
+"parse5@npm:^7.1.2":
+ version: 7.3.0
+ resolution: "parse5@npm:7.3.0"
+ dependencies:
+ entities: "npm:^6.0.0"
+ checksum: 10c0/7fd2e4e247e85241d6f2a464d0085eed599a26d7b0a5233790c49f53473232eb85350e8133344d9b3fd58b89339e7ad7270fe1f89d28abe50674ec97b87f80b5
+ languageName: node
+ linkType: hard
+
"parseurl@npm:^1.3.3, parseurl@npm:~1.3.3":
version: 1.3.3
resolution: "parseurl@npm:1.3.3"
@@ -23884,6 +25314,13 @@ __metadata:
languageName: node
linkType: hard
+"pathe@npm:^2.0.3":
+ version: 2.0.3
+ resolution: "pathe@npm:2.0.3"
+ checksum: 10c0/c118dc5a8b5c4166011b2b70608762e260085180bb9e33e80a50dcdb1e78c010b1624f4280c492c92b05fc276715a4c357d1f9edc570f8f1b3d90b6839ebaca1
+ languageName: node
+ linkType: hard
+
"pathval@npm:^2.0.0":
version: 2.0.0
resolution: "pathval@npm:2.0.0"
@@ -23919,6 +25356,13 @@ __metadata:
languageName: node
linkType: hard
+"picocolors@npm:1.1.1, picocolors@npm:^1.1.1":
+ version: 1.1.1
+ resolution: "picocolors@npm:1.1.1"
+ checksum: 10c0/e2e3e8170ab9d7c7421969adaa7e1b31434f789afb9b3f115f6b96d91945041ac3ceb02e9ec6fe6510ff036bcc0bf91e69a1772edc0b707e12b19c0f2d6bcf58
+ languageName: node
+ linkType: hard
+
"picocolors@npm:^0.2.1":
version: 0.2.1
resolution: "picocolors@npm:0.2.1"
@@ -23933,13 +25377,6 @@ __metadata:
languageName: node
linkType: hard
-"picocolors@npm:^1.1.1":
- version: 1.1.1
- resolution: "picocolors@npm:1.1.1"
- checksum: 10c0/e2e3e8170ab9d7c7421969adaa7e1b31434f789afb9b3f115f6b96d91945041ac3ceb02e9ec6fe6510ff036bcc0bf91e69a1772edc0b707e12b19c0f2d6bcf58
- languageName: node
- linkType: hard
-
"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.2.3, picomatch@npm:^2.3.1":
version: 2.3.1
resolution: "picomatch@npm:2.3.1"
@@ -23947,6 +25384,13 @@ __metadata:
languageName: node
linkType: hard
+"picomatch@npm:^4.0.2, picomatch@npm:^4.0.3":
+ version: 4.0.3
+ resolution: "picomatch@npm:4.0.3"
+ checksum: 10c0/9582c951e95eebee5434f59e426cddd228a7b97a0161a375aed4be244bd3fe8e3a31b846808ea14ef2c8a2527a6eeab7b3946a67d5979e81694654f939473ae2
+ languageName: node
+ linkType: hard
+
"pify@npm:^2.0.0, pify@npm:^2.3.0":
version: 2.3.0
resolution: "pify@npm:2.3.0"
@@ -24038,6 +25482,17 @@ __metadata:
languageName: node
linkType: hard
+"pixelmatch@npm:7.1.0":
+ version: 7.1.0
+ resolution: "pixelmatch@npm:7.1.0"
+ dependencies:
+ pngjs: "npm:^7.0.0"
+ bin:
+ pixelmatch: bin/pixelmatch
+ checksum: 10c0/ff069f92edaa841ac9b58b0ab74e1afa1f3b5e770eea0218c96bac1da4e752f5f6b79a0f9c4ba6b02afb955d39b8c78bcc3cc884f8122b67a1f2efbbccbe1a73
+ languageName: node
+ linkType: hard
+
"pkce-challenge@npm:^5.0.0":
version: 5.0.0
resolution: "pkce-challenge@npm:5.0.0"
@@ -24118,6 +25573,13 @@ __metadata:
languageName: node
linkType: hard
+"pngjs@npm:^7.0.0":
+ version: 7.0.0
+ resolution: "pngjs@npm:7.0.0"
+ checksum: 10c0/0d4c7a0fd476a9c33df7d0a2a73e1d56537628a668841f6995c2bca070cf30819f9254a64363266bc14ef2fee47659dd3b4f2b18eec7ab65143015139f497b38
+ languageName: node
+ linkType: hard
+
"polished@npm:^4.2.2":
version: 4.2.2
resolution: "polished@npm:4.2.2"
@@ -24458,6 +25920,13 @@ __metadata:
languageName: node
linkType: hard
+"punycode@npm:^2.3.1":
+ version: 2.3.1
+ resolution: "punycode@npm:2.3.1"
+ checksum: 10c0/14f76a8206bc3464f794fb2e3d3cc665ae416c01893ad7a02b23766eb07159144ee612ad67af5e84fa4479ccfe67678c4feb126b0485651b302babf66f04f9e9
+ languageName: node
+ linkType: hard
+
"pure-rand@npm:^5.0.0":
version: 5.0.0
resolution: "pure-rand@npm:5.0.0"
@@ -24841,6 +26310,8 @@ __metadata:
"@types/react": "npm:^19.0.0"
"@types/react-dom": "npm:^19.0.0"
"@typescript/native-preview": "npm:^7.0.0-dev.20251223.1"
+ "@vitest/browser-playwright": "npm:^4.0.17"
+ "@vitest/browser-preview": "npm:^4.0.17"
"@yarnpkg/types": "npm:^4.0.0"
autoprefixer: "npm:^9.6.0"
axe-playwright: "npm:^1.1.11"
@@ -24907,6 +26378,9 @@ __metadata:
typescript: "npm:^5.8.2"
typescript-eslint: "npm:^8.38.0"
verdaccio: "npm:^6.0.0"
+ vite-plugin-svgr: "npm:^4.5.0"
+ vitest: "npm:^4.0.17"
+ vitest-browser-react: "npm:^2.0.2"
walk-object: "npm:^4.0.0"
xml: "npm:^1.0.1"
languageName: unknown
@@ -25795,6 +27269,96 @@ __metadata:
languageName: node
linkType: hard
+"rollup@npm:^4.34.9, rollup@npm:^4.43.0":
+ version: 4.55.2
+ resolution: "rollup@npm:4.55.2"
+ dependencies:
+ "@rollup/rollup-android-arm-eabi": "npm:4.55.2"
+ "@rollup/rollup-android-arm64": "npm:4.55.2"
+ "@rollup/rollup-darwin-arm64": "npm:4.55.2"
+ "@rollup/rollup-darwin-x64": "npm:4.55.2"
+ "@rollup/rollup-freebsd-arm64": "npm:4.55.2"
+ "@rollup/rollup-freebsd-x64": "npm:4.55.2"
+ "@rollup/rollup-linux-arm-gnueabihf": "npm:4.55.2"
+ "@rollup/rollup-linux-arm-musleabihf": "npm:4.55.2"
+ "@rollup/rollup-linux-arm64-gnu": "npm:4.55.2"
+ "@rollup/rollup-linux-arm64-musl": "npm:4.55.2"
+ "@rollup/rollup-linux-loong64-gnu": "npm:4.55.2"
+ "@rollup/rollup-linux-loong64-musl": "npm:4.55.2"
+ "@rollup/rollup-linux-ppc64-gnu": "npm:4.55.2"
+ "@rollup/rollup-linux-ppc64-musl": "npm:4.55.2"
+ "@rollup/rollup-linux-riscv64-gnu": "npm:4.55.2"
+ "@rollup/rollup-linux-riscv64-musl": "npm:4.55.2"
+ "@rollup/rollup-linux-s390x-gnu": "npm:4.55.2"
+ "@rollup/rollup-linux-x64-gnu": "npm:4.55.2"
+ "@rollup/rollup-linux-x64-musl": "npm:4.55.2"
+ "@rollup/rollup-openbsd-x64": "npm:4.55.2"
+ "@rollup/rollup-openharmony-arm64": "npm:4.55.2"
+ "@rollup/rollup-win32-arm64-msvc": "npm:4.55.2"
+ "@rollup/rollup-win32-ia32-msvc": "npm:4.55.2"
+ "@rollup/rollup-win32-x64-gnu": "npm:4.55.2"
+ "@rollup/rollup-win32-x64-msvc": "npm:4.55.2"
+ "@types/estree": "npm:1.0.8"
+ fsevents: "npm:~2.3.2"
+ dependenciesMeta:
+ "@rollup/rollup-android-arm-eabi":
+ optional: true
+ "@rollup/rollup-android-arm64":
+ optional: true
+ "@rollup/rollup-darwin-arm64":
+ optional: true
+ "@rollup/rollup-darwin-x64":
+ optional: true
+ "@rollup/rollup-freebsd-arm64":
+ optional: true
+ "@rollup/rollup-freebsd-x64":
+ optional: true
+ "@rollup/rollup-linux-arm-gnueabihf":
+ optional: true
+ "@rollup/rollup-linux-arm-musleabihf":
+ optional: true
+ "@rollup/rollup-linux-arm64-gnu":
+ optional: true
+ "@rollup/rollup-linux-arm64-musl":
+ optional: true
+ "@rollup/rollup-linux-loong64-gnu":
+ optional: true
+ "@rollup/rollup-linux-loong64-musl":
+ optional: true
+ "@rollup/rollup-linux-ppc64-gnu":
+ optional: true
+ "@rollup/rollup-linux-ppc64-musl":
+ optional: true
+ "@rollup/rollup-linux-riscv64-gnu":
+ optional: true
+ "@rollup/rollup-linux-riscv64-musl":
+ optional: true
+ "@rollup/rollup-linux-s390x-gnu":
+ optional: true
+ "@rollup/rollup-linux-x64-gnu":
+ optional: true
+ "@rollup/rollup-linux-x64-musl":
+ optional: true
+ "@rollup/rollup-openbsd-x64":
+ optional: true
+ "@rollup/rollup-openharmony-arm64":
+ optional: true
+ "@rollup/rollup-win32-arm64-msvc":
+ optional: true
+ "@rollup/rollup-win32-ia32-msvc":
+ optional: true
+ "@rollup/rollup-win32-x64-gnu":
+ optional: true
+ "@rollup/rollup-win32-x64-msvc":
+ optional: true
+ fsevents:
+ optional: true
+ bin:
+ rollup: dist/bin/rollup
+ checksum: 10c0/0b18e107147e905491f96e993cb97609e05f518cf6a388b521edbf8c4d4789bbf8476d960c1671be370e770aeaa4479399fe4483ccf001c3bd593cbc2fd26483
+ languageName: node
+ linkType: hard
+
"route-manifest@npm:^1.0.0":
version: 1.0.0
resolution: "route-manifest@npm:1.0.0"
@@ -25817,6 +27381,20 @@ __metadata:
languageName: node
linkType: hard
+"rrweb-cssom@npm:^0.7.1":
+ version: 0.7.1
+ resolution: "rrweb-cssom@npm:0.7.1"
+ checksum: 10c0/127b8ca6c8aac45e2755abbae6138d4a813b1bedc2caabf79466ae83ab3cfc84b5bfab513b7033f0aa4561c7753edf787d0dd01163ceacdee2e8eb1b6bf7237e
+ languageName: node
+ linkType: hard
+
+"rrweb-cssom@npm:^0.8.0":
+ version: 0.8.0
+ resolution: "rrweb-cssom@npm:0.8.0"
+ checksum: 10c0/56f2bfd56733adb92c0b56e274c43f864b8dd48784d6fe946ef5ff8d438234015e59ad837fc2ad54714b6421384141c1add4eb569e72054e350d1f8a50b8ac7b
+ languageName: node
+ linkType: hard
+
"rsc-html-stream@npm:^0.0.6":
version: 0.0.6
resolution: "rsc-html-stream@npm:0.0.6"
@@ -26330,6 +27908,13 @@ __metadata:
languageName: node
linkType: hard
+"siginfo@npm:^2.0.0":
+ version: 2.0.0
+ resolution: "siginfo@npm:2.0.0"
+ checksum: 10c0/3def8f8e516fbb34cb6ae415b07ccc5d9c018d85b4b8611e3dc6f8be6d1899f693a4382913c9ed51a06babb5201639d76453ab297d1c54a456544acf5c892e34
+ languageName: node
+ linkType: hard
+
"signal-exit@npm:^3.0.0, signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3, signal-exit@npm:^3.0.7":
version: 3.0.7
resolution: "signal-exit@npm:3.0.7"
@@ -26353,6 +27938,17 @@ __metadata:
languageName: node
linkType: hard
+"sirv@npm:^3.0.2":
+ version: 3.0.2
+ resolution: "sirv@npm:3.0.2"
+ dependencies:
+ "@polka/url": "npm:^1.0.0-next.24"
+ mrmime: "npm:^2.0.0"
+ totalist: "npm:^3.0.0"
+ checksum: 10c0/5930e4397afdb14fbae13751c3be983af4bda5c9aadec832607dc2af15a7162f7d518c71b30e83ae3644b9a24cea041543cc969e5fe2b80af6ce8ea3174b2d04
+ languageName: node
+ linkType: hard
+
"sisteransi@npm:^1.0.5":
version: 1.0.5
resolution: "sisteransi@npm:1.0.5"
@@ -26803,6 +28399,13 @@ __metadata:
languageName: node
linkType: hard
+"stackback@npm:0.0.2":
+ version: 0.0.2
+ resolution: "stackback@npm:0.0.2"
+ checksum: 10c0/89a1416668f950236dd5ac9f9a6b2588e1b9b62b1b6ad8dff1bfc5d1a15dbf0aafc9b52d2226d00c28dffff212da464eaeebfc6b7578b9d180cef3e3782c5983
+ languageName: node
+ linkType: hard
+
"static-extend@npm:^0.1.1":
version: 0.1.2
resolution: "static-extend@npm:0.1.2"
@@ -26827,6 +28430,13 @@ __metadata:
languageName: node
linkType: hard
+"std-env@npm:^3.10.0":
+ version: 3.10.0
+ resolution: "std-env@npm:3.10.0"
+ checksum: 10c0/1814927a45004d36dde6707eaf17552a546769bc79a6421be2c16ce77d238158dfe5de30910b78ec30d95135cc1c59ea73ee22d2ca170f8b9753f84da34c427f
+ languageName: node
+ linkType: hard
+
"steno@npm:^0.4.1":
version: 0.4.4
resolution: "steno@npm:0.4.4"
@@ -27696,6 +29306,30 @@ __metadata:
languageName: node
linkType: hard
+"tinybench@npm:^2.9.0":
+ version: 2.9.0
+ resolution: "tinybench@npm:2.9.0"
+ checksum: 10c0/c3500b0f60d2eb8db65250afe750b66d51623057ee88720b7f064894a6cb7eb93360ca824a60a31ab16dab30c7b1f06efe0795b352e37914a9d4bad86386a20c
+ languageName: node
+ linkType: hard
+
+"tinyexec@npm:^1.0.2":
+ version: 1.0.2
+ resolution: "tinyexec@npm:1.0.2"
+ checksum: 10c0/1261a8e34c9b539a9aae3b7f0bb5372045ff28ee1eba035a2a059e532198fe1a182ec61ac60fa0b4a4129f0c4c4b1d2d57355b5cb9aa2d17ac9454ecace502ee
+ languageName: node
+ linkType: hard
+
+"tinyglobby@npm:^0.2.13, tinyglobby@npm:^0.2.15":
+ version: 0.2.15
+ resolution: "tinyglobby@npm:0.2.15"
+ dependencies:
+ fdir: "npm:^6.5.0"
+ picomatch: "npm:^4.0.3"
+ checksum: 10c0/869c31490d0d88eedb8305d178d4c75e7463e820df5a9b9d388291daf93e8b1eb5de1dad1c1e139767e4269fe75f3b10d5009b2cc14db96ff98986920a186844
+ languageName: node
+ linkType: hard
+
"tinyrainbow@npm:^1.2.0":
version: 1.2.0
resolution: "tinyrainbow@npm:1.2.0"
@@ -27703,6 +29337,13 @@ __metadata:
languageName: node
linkType: hard
+"tinyrainbow@npm:^3.0.3":
+ version: 3.0.3
+ resolution: "tinyrainbow@npm:3.0.3"
+ checksum: 10c0/1e799d35cd23cabe02e22550985a3051dc88814a979be02dc632a159c393a998628eacfc558e4c746b3006606d54b00bcdea0c39301133956d10a27aa27e988c
+ languageName: node
+ linkType: hard
+
"tinyspy@npm:^3.0.0":
version: 3.0.2
resolution: "tinyspy@npm:3.0.2"
@@ -27821,6 +29462,13 @@ __metadata:
languageName: node
linkType: hard
+"totalist@npm:^3.0.0":
+ version: 3.0.1
+ resolution: "totalist@npm:3.0.1"
+ checksum: 10c0/4bb1fadb69c3edbef91c73ebef9d25b33bbf69afe1e37ce544d5f7d13854cda15e47132f3e0dc4cafe300ddb8578c77c50a65004d8b6e97e77934a69aa924863
+ languageName: node
+ linkType: hard
+
"tough-cookie@npm:^4.1.2":
version: 4.1.2
resolution: "tough-cookie@npm:4.1.2"
@@ -27870,6 +29518,15 @@ __metadata:
languageName: node
linkType: hard
+"tr46@npm:^5.1.0":
+ version: 5.1.1
+ resolution: "tr46@npm:5.1.1"
+ dependencies:
+ punycode: "npm:^2.3.1"
+ checksum: 10c0/ae270e194d52ec67ebd695c1a42876e0f19b96e4aca2ab464ab1d9d17dc3acd3e18764f5034c93897db73421563be27c70c98359c4501136a497e46deda5d5ec
+ languageName: node
+ linkType: hard
+
"tr46@npm:~0.0.3":
version: 0.0.3
resolution: "tr46@npm:0.0.3"
@@ -28662,6 +30319,89 @@ __metadata:
languageName: node
linkType: hard
+"unplugin-parcel-macros-darwin-arm64@npm:0.1.2-alpha.1":
+ version: 0.1.2-alpha.1
+ resolution: "unplugin-parcel-macros-darwin-arm64@npm:0.1.2-alpha.1"
+ conditions: os=darwin & cpu=arm64
+ languageName: node
+ linkType: hard
+
+"unplugin-parcel-macros-darwin-x64@npm:0.1.2-alpha.1":
+ version: 0.1.2-alpha.1
+ resolution: "unplugin-parcel-macros-darwin-x64@npm:0.1.2-alpha.1"
+ conditions: os=darwin & cpu=x64
+ languageName: node
+ linkType: hard
+
+"unplugin-parcel-macros-linux-arm64-gnu@npm:0.1.2-alpha.1":
+ version: 0.1.2-alpha.1
+ resolution: "unplugin-parcel-macros-linux-arm64-gnu@npm:0.1.2-alpha.1"
+ conditions: os=linux & cpu=arm64 & libc=glibc
+ languageName: node
+ linkType: hard
+
+"unplugin-parcel-macros-linux-arm64-musl@npm:0.1.2-alpha.1":
+ version: 0.1.2-alpha.1
+ resolution: "unplugin-parcel-macros-linux-arm64-musl@npm:0.1.2-alpha.1"
+ conditions: os=linux & cpu=arm64 & libc=musl
+ languageName: node
+ linkType: hard
+
+"unplugin-parcel-macros-linux-x64-gnu@npm:0.1.2-alpha.1":
+ version: 0.1.2-alpha.1
+ resolution: "unplugin-parcel-macros-linux-x64-gnu@npm:0.1.2-alpha.1"
+ conditions: os=linux & cpu=x64 & libc=glibc
+ languageName: node
+ linkType: hard
+
+"unplugin-parcel-macros-linux-x64-musl@npm:0.1.2-alpha.1":
+ version: 0.1.2-alpha.1
+ resolution: "unplugin-parcel-macros-linux-x64-musl@npm:0.1.2-alpha.1"
+ conditions: os=linux & cpu=x64 & libc=musl
+ languageName: node
+ linkType: hard
+
+"unplugin-parcel-macros-win32-x64-msvc@npm:0.1.2-alpha.1":
+ version: 0.1.2-alpha.1
+ resolution: "unplugin-parcel-macros-win32-x64-msvc@npm:0.1.2-alpha.1"
+ conditions: os=win32 & cpu=x64
+ languageName: node
+ linkType: hard
+
+"unplugin-parcel-macros@npm:^0.1.2-alpha.1":
+ version: 0.1.2-alpha.1
+ resolution: "unplugin-parcel-macros@npm:0.1.2-alpha.1"
+ dependencies:
+ "@parcel/source-map": "npm:^2.1.1"
+ "@swc/core": "npm:^1.15.3"
+ napi-wasm: "npm:^1.0.1"
+ unplugin: "npm:^1.9.0"
+ unplugin-parcel-macros-darwin-arm64: "npm:0.1.2-alpha.1"
+ unplugin-parcel-macros-darwin-x64: "npm:0.1.2-alpha.1"
+ unplugin-parcel-macros-linux-arm64-gnu: "npm:0.1.2-alpha.1"
+ unplugin-parcel-macros-linux-arm64-musl: "npm:0.1.2-alpha.1"
+ unplugin-parcel-macros-linux-x64-gnu: "npm:0.1.2-alpha.1"
+ unplugin-parcel-macros-linux-x64-musl: "npm:0.1.2-alpha.1"
+ unplugin-parcel-macros-win32-x64-msvc: "npm:0.1.2-alpha.1"
+ dependenciesMeta:
+ unplugin-parcel-macros-darwin-arm64:
+ optional: true
+ unplugin-parcel-macros-darwin-x64:
+ optional: true
+ unplugin-parcel-macros-linux-arm64-gnu:
+ optional: true
+ unplugin-parcel-macros-linux-arm64-musl:
+ optional: true
+ unplugin-parcel-macros-linux-x64-gnu:
+ optional: true
+ unplugin-parcel-macros-linux-x64-musl:
+ optional: true
+ unplugin-parcel-macros-win32-x64-msvc:
+ optional: true
+ checksum: 10c0/29dd3fcc83004da8dc70563527661c6576f9c9a7ff44ad25fad0e1c751989c519c25f74195d71c0bf0acb74cf16e811a1b8c1e73220f3c9054428b5c53e4bd00
+ languageName: node
+ linkType: hard
+
"unplugin@npm:^1.3.1, unplugin@npm:^1.4.0":
version: 1.12.0
resolution: "unplugin@npm:1.12.0"
@@ -28674,6 +30414,16 @@ __metadata:
languageName: node
linkType: hard
+"unplugin@npm:^1.9.0":
+ version: 1.16.1
+ resolution: "unplugin@npm:1.16.1"
+ dependencies:
+ acorn: "npm:^8.14.0"
+ webpack-virtual-modules: "npm:^0.6.2"
+ checksum: 10c0/dd5f8c5727d0135847da73cf03fb199107f1acf458167034886fda3405737dab871ad3926431b4f70e1e82cdac482ac1383cea4019d782a68515c8e3e611b6cc
+ languageName: node
+ linkType: hard
+
"unquote@npm:^1.1.0":
version: 1.1.1
resolution: "unquote@npm:1.1.1"
@@ -29159,6 +30909,206 @@ __metadata:
languageName: node
linkType: hard
+"vite-plugin-svgr@npm:^4.3.0, vite-plugin-svgr@npm:^4.5.0":
+ version: 4.5.0
+ resolution: "vite-plugin-svgr@npm:4.5.0"
+ dependencies:
+ "@rollup/pluginutils": "npm:^5.2.0"
+ "@svgr/core": "npm:^8.1.0"
+ "@svgr/plugin-jsx": "npm:^8.1.0"
+ peerDependencies:
+ vite: ">=2.6.0"
+ checksum: 10c0/3e1959fec626bb4f5a8ec13ff15bc40ffbc1c0ff38149bebe3f37dc2d67ed1f276f129ff7983e06946cf712e19996affd9d6868aa7d20d8921d1fe4449109b55
+ languageName: node
+ linkType: hard
+
+"vite@npm:^6.0.0":
+ version: 6.4.1
+ resolution: "vite@npm:6.4.1"
+ dependencies:
+ esbuild: "npm:^0.25.0"
+ fdir: "npm:^6.4.4"
+ fsevents: "npm:~2.3.3"
+ picomatch: "npm:^4.0.2"
+ postcss: "npm:^8.5.3"
+ rollup: "npm:^4.34.9"
+ tinyglobby: "npm:^0.2.13"
+ peerDependencies:
+ "@types/node": ^18.0.0 || ^20.0.0 || >=22.0.0
+ jiti: ">=1.21.0"
+ less: "*"
+ lightningcss: ^1.21.0
+ sass: "*"
+ sass-embedded: "*"
+ stylus: "*"
+ sugarss: "*"
+ terser: ^5.16.0
+ tsx: ^4.8.1
+ yaml: ^2.4.2
+ dependenciesMeta:
+ fsevents:
+ optional: true
+ peerDependenciesMeta:
+ "@types/node":
+ optional: true
+ jiti:
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ tsx:
+ optional: true
+ yaml:
+ optional: true
+ bin:
+ vite: bin/vite.js
+ checksum: 10c0/77bb4c5b10f2a185e7859cc9a81c789021bc18009b02900347d1583b453b58e4b19ff07a5e5a5b522b68fc88728460bb45a63b104d969e8c6a6152aea3b849f7
+ languageName: node
+ linkType: hard
+
+"vite@npm:^6.0.0 || ^7.0.0":
+ version: 7.3.1
+ resolution: "vite@npm:7.3.1"
+ dependencies:
+ esbuild: "npm:^0.27.0"
+ fdir: "npm:^6.5.0"
+ fsevents: "npm:~2.3.3"
+ picomatch: "npm:^4.0.3"
+ postcss: "npm:^8.5.6"
+ rollup: "npm:^4.43.0"
+ tinyglobby: "npm:^0.2.15"
+ peerDependencies:
+ "@types/node": ^20.19.0 || >=22.12.0
+ jiti: ">=1.21.0"
+ less: ^4.0.0
+ lightningcss: ^1.21.0
+ sass: ^1.70.0
+ sass-embedded: ^1.70.0
+ stylus: ">=0.54.8"
+ sugarss: ^5.0.0
+ terser: ^5.16.0
+ tsx: ^4.8.1
+ yaml: ^2.4.2
+ dependenciesMeta:
+ fsevents:
+ optional: true
+ peerDependenciesMeta:
+ "@types/node":
+ optional: true
+ jiti:
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ tsx:
+ optional: true
+ yaml:
+ optional: true
+ bin:
+ vite: bin/vite.js
+ checksum: 10c0/5c7548f5f43a23533e53324304db4ad85f1896b1bfd3ee32ae9b866bac2933782c77b350eb2b52a02c625c8ad1ddd4c000df077419410650c982cd97fde8d014
+ languageName: node
+ linkType: hard
+
+"vitest-browser-react@npm:^2.0.2":
+ version: 2.0.2
+ resolution: "vitest-browser-react@npm:2.0.2"
+ peerDependencies:
+ "@types/react": ^18.0.0 || ^19.0.0
+ "@types/react-dom": ^18.0.0 || ^19.0.0
+ react: ^18.0.0 || ^19.0.0
+ react-dom: ^18.0.0 || ^19.0.0
+ vitest: ^4.0.0
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ "@types/react-dom":
+ optional: true
+ checksum: 10c0/95941efb09fa26533974029c70f5bc295b35de0ddef36e8852ce2e97221e10c660d4251af837f875a264a5b08ced965145a2d90a67d11dc595e91b16adbf3808
+ languageName: node
+ linkType: hard
+
+"vitest@npm:^4.0.0, vitest@npm:^4.0.17":
+ version: 4.0.17
+ resolution: "vitest@npm:4.0.17"
+ dependencies:
+ "@vitest/expect": "npm:4.0.17"
+ "@vitest/mocker": "npm:4.0.17"
+ "@vitest/pretty-format": "npm:4.0.17"
+ "@vitest/runner": "npm:4.0.17"
+ "@vitest/snapshot": "npm:4.0.17"
+ "@vitest/spy": "npm:4.0.17"
+ "@vitest/utils": "npm:4.0.17"
+ es-module-lexer: "npm:^1.7.0"
+ expect-type: "npm:^1.2.2"
+ magic-string: "npm:^0.30.21"
+ obug: "npm:^2.1.1"
+ pathe: "npm:^2.0.3"
+ picomatch: "npm:^4.0.3"
+ std-env: "npm:^3.10.0"
+ tinybench: "npm:^2.9.0"
+ tinyexec: "npm:^1.0.2"
+ tinyglobby: "npm:^0.2.15"
+ tinyrainbow: "npm:^3.0.3"
+ vite: "npm:^6.0.0 || ^7.0.0"
+ why-is-node-running: "npm:^2.3.0"
+ peerDependencies:
+ "@edge-runtime/vm": "*"
+ "@opentelemetry/api": ^1.9.0
+ "@types/node": ^20.0.0 || ^22.0.0 || >=24.0.0
+ "@vitest/browser-playwright": 4.0.17
+ "@vitest/browser-preview": 4.0.17
+ "@vitest/browser-webdriverio": 4.0.17
+ "@vitest/ui": 4.0.17
+ happy-dom: "*"
+ jsdom: "*"
+ peerDependenciesMeta:
+ "@edge-runtime/vm":
+ optional: true
+ "@opentelemetry/api":
+ optional: true
+ "@types/node":
+ optional: true
+ "@vitest/browser-playwright":
+ optional: true
+ "@vitest/browser-preview":
+ optional: true
+ "@vitest/browser-webdriverio":
+ optional: true
+ "@vitest/ui":
+ optional: true
+ happy-dom:
+ optional: true
+ jsdom:
+ optional: true
+ bin:
+ vitest: vitest.mjs
+ checksum: 10c0/e1648bbfe2d01e23ceb6856863344035d2a1c139f39e8b15859e6ea8dc510ac3ba425df7c45486492d85ca516472aa892540dfd11ab6ad0613be98fd56d40716
+ languageName: node
+ linkType: hard
+
"w3c-xmlserializer@npm:^4.0.0":
version: 4.0.0
resolution: "w3c-xmlserializer@npm:4.0.0"
@@ -29168,6 +31118,15 @@ __metadata:
languageName: node
linkType: hard
+"w3c-xmlserializer@npm:^5.0.0":
+ version: 5.0.0
+ resolution: "w3c-xmlserializer@npm:5.0.0"
+ dependencies:
+ xml-name-validator: "npm:^5.0.0"
+ checksum: 10c0/8712774c1aeb62dec22928bf1cdfd11426c2c9383a1a63f2bcae18db87ca574165a0fbe96b312b73652149167ac6c7f4cf5409f2eb101d9c805efe0e4bae798b
+ languageName: node
+ linkType: hard
+
"wait-on@npm:^7.0.0":
version: 7.2.0
resolution: "wait-on@npm:7.2.0"
@@ -29281,6 +31240,15 @@ __metadata:
languageName: node
linkType: hard
+"whatwg-encoding@npm:^3.1.1":
+ version: 3.1.1
+ resolution: "whatwg-encoding@npm:3.1.1"
+ dependencies:
+ iconv-lite: "npm:0.6.3"
+ checksum: 10c0/273b5f441c2f7fda3368a496c3009edbaa5e43b71b09728f90425e7f487e5cef9eb2b846a31bd760dd8077739c26faf6b5ca43a5f24033172b003b72cf61a93e
+ languageName: node
+ linkType: hard
+
"whatwg-mimetype@npm:^3.0.0":
version: 3.0.0
resolution: "whatwg-mimetype@npm:3.0.0"
@@ -29288,6 +31256,13 @@ __metadata:
languageName: node
linkType: hard
+"whatwg-mimetype@npm:^4.0.0":
+ version: 4.0.0
+ resolution: "whatwg-mimetype@npm:4.0.0"
+ checksum: 10c0/a773cdc8126b514d790bdae7052e8bf242970cebd84af62fb2f35a33411e78e981f6c0ab9ed1fe6ec5071b09d5340ac9178e05b52d35a9c4bcf558ba1b1551df
+ languageName: node
+ linkType: hard
+
"whatwg-url@npm:^11.0.0":
version: 11.0.0
resolution: "whatwg-url@npm:11.0.0"
@@ -29298,6 +31273,16 @@ __metadata:
languageName: node
linkType: hard
+"whatwg-url@npm:^14.0.0":
+ version: 14.2.0
+ resolution: "whatwg-url@npm:14.2.0"
+ dependencies:
+ tr46: "npm:^5.1.0"
+ webidl-conversions: "npm:^7.0.0"
+ checksum: 10c0/f746fc2f4c906607d09537de1227b13f9494c171141e5427ed7d2c0dd0b6a48b43d8e71abaae57d368d0c06b673fd8ec63550b32ad5ed64990c7b0266c2b4272
+ languageName: node
+ linkType: hard
+
"whatwg-url@npm:^5.0.0":
version: 5.0.0
resolution: "whatwg-url@npm:5.0.0"
@@ -29417,6 +31402,18 @@ __metadata:
languageName: node
linkType: hard
+"why-is-node-running@npm:^2.3.0":
+ version: 2.3.0
+ resolution: "why-is-node-running@npm:2.3.0"
+ dependencies:
+ siginfo: "npm:^2.0.0"
+ stackback: "npm:0.0.2"
+ bin:
+ why-is-node-running: cli.js
+ checksum: 10c0/1cde0b01b827d2cf4cb11db962f3958b9175d5d9e7ac7361d1a7b0e2dc6069a263e69118bd974c4f6d0a890ef4eedfe34cf3d5167ec14203dbc9a18620537054
+ languageName: node
+ linkType: hard
+
"wide-align@npm:^1.1.0":
version: 1.1.5
resolution: "wide-align@npm:1.1.5"
@@ -29604,6 +31601,21 @@ __metadata:
languageName: node
linkType: hard
+"ws@npm:^8.18.0, ws@npm:^8.18.3":
+ version: 8.19.0
+ resolution: "ws@npm:8.19.0"
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: ">=5.0.2"
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+ checksum: 10c0/4741d9b9bc3f9c791880882414f96e36b8b254e34d4b503279d6400d9a4b87a033834856dbdd94ee4b637944df17ea8afc4bce0ff4a1560d2166be8855da5b04
+ languageName: node
+ linkType: hard
+
"xdg-basedir@npm:^3.0.0":
version: 3.0.0
resolution: "xdg-basedir@npm:3.0.0"
@@ -29618,6 +31630,13 @@ __metadata:
languageName: node
linkType: hard
+"xml-name-validator@npm:^5.0.0":
+ version: 5.0.0
+ resolution: "xml-name-validator@npm:5.0.0"
+ checksum: 10c0/3fcf44e7b73fb18be917fdd4ccffff3639373c7cb83f8fc35df6001fecba7942f1dbead29d91ebb8315e2f2ff786b508f0c9dc0215b6353f9983c6b7d62cb1f5
+ languageName: node
+ linkType: hard
+
"xml@npm:^1.0.1":
version: 1.0.1
resolution: "xml@npm:1.0.1"