UI testing (#565)

* store demo data in React Query

* Playwright for UI screenshots

* use path based routing on web

* Fix Typescript error
This commit is contained in:
Oscar Beaumont 2023-02-27 13:15:47 +08:00 committed by GitHub
parent 7192ead2c2
commit faeb0473cf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 384 additions and 32 deletions

2
.gitignore vendored
View file

@ -63,6 +63,8 @@ examples/*/*.lock
/target
prisma*.rs
playwright-report
/sdserver_data
.spacedrive
dev.db-journal

View file

@ -18,6 +18,7 @@
"@sd/client": "workspace:*",
"@sd/interface": "workspace:*",
"@sd/ui": "workspace:*",
"@tanstack/react-query": "^4.24.4",
"@tauri-apps/api": "1.2.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"

View file

@ -1,13 +1,13 @@
import { loggerLink } from '@rspc/client';
import { tauriLink } from '@rspc/tauri';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { dialog, invoke, os, shell } from '@tauri-apps/api';
import { listen } from '@tauri-apps/api/event';
import { convertFileSrc } from '@tauri-apps/api/tauri';
import { useEffect } from 'react';
import { getDebugState, hooks, queryClient } from '@sd/client';
import { getDebugState, hooks } from '@sd/client';
import SpacedriveInterface, { OperatingSystem, Platform, PlatformProvider } from '@sd/interface';
import { KeybindEvent } from '@sd/interface';
import { ErrorPage } from '@sd/interface';
import { KeybindEvent, ErrorPage } from '@sd/interface';
import '@sd/ui/style';
const client = hooks.createClient({
@ -65,6 +65,8 @@ const platform: Platform = {
openPath: (path) => shell.open(path)
};
const queryClient = new QueryClient();
export default function App() {
useEffect(() => {
// This tells Tauri to show the current window because it's finished loading
@ -86,9 +88,12 @@ export default function App() {
}
return (
// @ts-expect-error: Just a version mismatch
<hooks.Provider client={client} queryClient={queryClient}>
<PlatformProvider platform={platform}>
<SpacedriveInterface />
<QueryClientProvider client={queryClient}>
<SpacedriveInterface router="memory" />
</QueryClientProvider>
</PlatformProvider>
</hooks.Provider>
);

View file

@ -1,6 +1,7 @@
import { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
import { DefaultTheme, NavigationContainer, Theme } from '@react-navigation/native';
import { loggerLink } from '@rspc/client';
import { QueryClient } from '@tanstack/react-query';
import dayjs from 'dayjs';
import advancedFormat from 'dayjs/plugin/advancedFormat';
import duration from 'dayjs/plugin/duration';
@ -17,7 +18,6 @@ import {
ClientContextProvider,
LibraryContextProvider,
getDebugState,
queryClient,
rspc,
useClientContext,
useInvalidateQuery
@ -95,12 +95,15 @@ const client = rspc.createClient({
]
});
const queryClient = new QueryClient();
export default function App() {
useEffect(() => {
SplashScreen.hideAsync();
}, []);
return (
// @ts-expect-error: Version mismatch
<rspc.Provider client={client} queryClient={queryClient}>
<AppContainer />
</rspc.Provider>

View file

@ -1,5 +1,6 @@
import { useQueryClient } from '@tanstack/react-query';
import { useState } from 'react';
import { queryClient, useBridgeMutation } from '@sd/client';
import { useBridgeMutation } from '@sd/client';
import { Input } from '~/components/form/Input';
import Dialog from '~/components/layout/Dialog';
import { currentLibraryStore } from '~/utils/nav';
@ -12,6 +13,7 @@ type Props = {
// TODO: Move to a Modal component
const CreateLibraryDialog = ({ children, onSubmit, disableBackdropClose }: Props) => {
const queryClient = useQueryClient();
const [libName, setLibName] = useState('');
const [isOpen, setIsOpen] = useState(false);

View file

@ -1,7 +1,8 @@
import { Heart } from 'phosphor-react-native';
import { useState } from 'react';
import { Pressable, PressableProps } from 'react-native';
import { Object as SDObject, queryClient, useLibraryMutation } from '@sd/client';
import { Object as SDObject, useLibraryMutation } from '@sd/client';
import { useQueryClient } from '@tanstack/react-query';
type Props = {
data: SDObject;
@ -9,6 +10,7 @@ type Props = {
};
const FavoriteButton = (props: Props) => {
const queryClient = useQueryClient();
const [favorite, setFavorite] = useState(props.data.favorite);
const { mutate: toggleFavorite, isLoading } = useLibraryMutation('files.setFavorite', {

View file

@ -1,6 +1,7 @@
import { useQueryClient } from '@tanstack/react-query';
import { useBridgeMutation } from '@sd/client';
import { useRef } from 'react';
import { queryClient, useBridgeMutation } from '@sd/client';
import { ConfirmModal, ModalRef } from '~/components/layout/Modal';
import { ModalRef, ConfirmModal } from '~/components/layout/Modal';
type Props = {
libraryUuid: string;
@ -9,6 +10,7 @@ type Props = {
};
const DeleteLibraryModal = ({ trigger, onSubmit, libraryUuid }: Props) => {
const queryClient = useQueryClient();
const modalRef = useRef<ModalRef>(null);
const { mutate: deleteLibrary, isLoading: deleteLibLoading } = useBridgeMutation(

View file

@ -1,15 +1,17 @@
import { forwardRef, useEffect, useState } from 'react';
import { Pressable, Text, View } from 'react-native';
import ColorPicker from 'react-native-wheel-color-picker';
import { queryClient, useLibraryMutation } from '@sd/client';
import { useLibraryMutation } from '@sd/client';
import { FadeInAnimation } from '~/components/animation/layout';
import { Input } from '~/components/form/Input';
import { Modal, ModalRef } from '~/components/layout/Modal';
import { Button } from '~/components/primitive/Button';
import useForwardedRef from '~/hooks/useForwardedRef';
import { tw, twStyle } from '~/lib/tailwind';
import { useQueryClient } from '@tanstack/react-query';
const CreateTagModal = forwardRef<ModalRef, unknown>((_, ref) => {
const queryClient = useQueryClient();
const modalRef = useForwardedRef(ref);
const [tagName, setTagName] = useState('');

View file

@ -1,6 +1,6 @@
import { forwardRef, useEffect, useState } from 'react';
import { ColorValue, Pressable, Text, View } from 'react-native';
import { Tag, queryClient, useLibraryMutation } from '@sd/client';
import { Pressable, Text, View } from 'react-native';
import { Tag, useLibraryMutation } from '@sd/client';
import { FadeInAnimation } from '~/components/animation/layout';
import ColorPicker from '~/components/form/ColorPicker';
import { Input } from '~/components/form/Input';
@ -8,6 +8,7 @@ import { Modal, ModalRef } from '~/components/layout/Modal';
import { Button } from '~/components/primitive/Button';
import useForwardedRef from '~/hooks/useForwardedRef';
import { tw, twStyle } from '~/lib/tailwind';
import { useQueryClient } from '@tanstack/react-query';
type Props = {
tag: Tag;
@ -15,6 +16,7 @@ type Props = {
};
const UpdateTagModal = forwardRef<ModalRef, Props>((props, ref) => {
const queryClient = useQueryClient();
const modalRef = useForwardedRef(ref);
const [tagName, setTagName] = useState(props.tag.name!);

View file

@ -6,6 +6,7 @@
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"test": "VITE_SD_DEMO_MODE=true playwright test",
"typecheck": "tsc -b"
},
"dependencies": {
@ -18,6 +19,7 @@
"react-dom": "^18.2.0"
},
"devDependencies": {
"@playwright/test": "^1.30.0",
"@sd/config": "workspace:*",
"@sd/ui": "workspace:*",
"@types/react": "^18.0.21",

View file

@ -0,0 +1,60 @@
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
timeout: 30 * 1000,
expect: {
timeout: 5000
},
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
webServer: {
command: 'pnpm build && pnpm preview',
port: 4173
},
use: {
actionTimeout: 0,
trace: 'on-first-retry'
},
/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] }
}
// {
// name: 'firefox',
// use: { ...devices['Desktop Firefox'] }
// },
// {
// name: 'webkit',
// use: { ...devices['Desktop Safari'] }
// }
/* Test against mobile viewports. */
// {
// name: 'Mobile Chrome',
// use: { ...devices['Pixel 5'] },
// },
// {
// name: 'Mobile Safari',
// use: { ...devices['iPhone 12'] },
// },
/* Test against branded browsers. */
// {
// name: 'Microsoft Edge',
// use: { channel: 'msedge' },
// },
// {
// name: 'Google Chrome',
// use: { channel: 'chrome' },
// },
]
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View file

@ -1,7 +1,9 @@
import { createWSClient, loggerLink, wsLink } from '@rspc/client';
import { QueryClient, QueryClientProvider, hydrate } from '@tanstack/react-query';
import { useEffect } from 'react';
import { getDebugState, hooks, queryClient } from '@sd/client';
import { getDebugState, hooks } from '@sd/client';
import SpacedriveInterface, { Platform, PlatformProvider } from '@sd/interface';
import demoData from './demoData.json';
globalThis.isDev = import.meta.env.DEV;
@ -34,17 +36,38 @@ const platform: Platform = {
locationLocalId
)}/${encodeURIComponent(filePathId)}`,
openLink: (url) => window.open(url, '_blank')?.focus(),
demoMode: true
demoMode: import.meta.env.VITE_SD_DEMO_MODE === 'true'
};
const queryClient = new QueryClient({
defaultOptions: {
queries: import.meta.env.VITE_SD_DEMO_MODE
? {
refetchOnWindowFocus: false,
staleTime: Infinity,
cacheTime: Infinity,
networkMode: 'offlineFirst',
enabled: false
}
: undefined
// TODO: Mutations can't be globally disable which is annoying!
}
});
function App() {
useEffect(() => window.parent.postMessage('spacedrive-hello', '*'), []);
if (import.meta.env.VITE_SD_DEMO_MODE === 'true') {
hydrate(queryClient, demoData);
}
return (
<div className="App">
<hooks.Provider client={client} queryClient={queryClient}>
<PlatformProvider platform={platform}>
<SpacedriveInterface />
<QueryClientProvider client={queryClient}>
<SpacedriveInterface router="browser" />
</QueryClientProvider>
</PlatformProvider>
</hooks.Provider>
</div>

182
apps/web/src/demoData.json Normal file
View file

@ -0,0 +1,182 @@
{
"mutations": [],
"queries": [
{
"state": {
"data": [
{
"uuid": "dc1c41b9-65c6-4a9d-a418-79e628b3ac59",
"config": { "version": "0.1.0", "name": "My Library", "description": "" }
}
],
"dataUpdateCount": 2,
"dataUpdatedAt": 1675912735380,
"error": null,
"errorUpdateCount": 0,
"errorUpdatedAt": 0,
"fetchFailureCount": 0,
"fetchFailureReason": null,
"fetchMeta": {},
"isInvalidated": false,
"status": "success",
"fetchStatus": "fetching"
},
"queryKey": ["library.list"],
"queryHash": "[\"library.list\"]"
},
{
"state": {
"data": "macOS",
"dataUpdateCount": 0,
"dataUpdatedAt": 1675912734876,
"error": null,
"errorUpdateCount": 0,
"errorUpdatedAt": 0,
"fetchFailureCount": 0,
"fetchFailureReason": null,
"fetchMeta": null,
"isInvalidated": false,
"status": "success",
"fetchStatus": "idle"
},
"queryKey": ["_tauri", "platform"],
"queryHash": "[\"_tauri\",\"platform\"]"
},
{
"state": {
"data": [],
"dataUpdateCount": 1,
"dataUpdatedAt": 1675912735051,
"error": null,
"errorUpdateCount": 0,
"errorUpdatedAt": 0,
"fetchFailureCount": 0,
"fetchFailureReason": null,
"fetchMeta": {},
"isInvalidated": false,
"status": "success",
"fetchStatus": "fetching"
},
"queryKey": [
"locations.list",
{ "library_id": "dc1c41b9-65c6-4a9d-a418-79e628b3ac59", "arg": null }
],
"queryHash": "[\"locations.list\",{\"arg\":null,\"library_id\":\"dc1c41b9-65c6-4a9d-a418-79e628b3ac59\"}]"
},
{
"state": {
"data": [],
"dataUpdateCount": 1,
"dataUpdatedAt": 1675912735057,
"error": null,
"errorUpdateCount": 0,
"errorUpdatedAt": 0,
"fetchFailureCount": 0,
"fetchFailureReason": null,
"fetchMeta": {},
"isInvalidated": false,
"status": "success",
"fetchStatus": "fetching"
},
"queryKey": [
"tags.list",
{ "library_id": "dc1c41b9-65c6-4a9d-a418-79e628b3ac59", "arg": null }
],
"queryHash": "[\"tags.list\",{\"arg\":null,\"library_id\":\"dc1c41b9-65c6-4a9d-a418-79e628b3ac59\"}]"
},
{
"state": {
"data": false,
"dataUpdateCount": 1,
"dataUpdatedAt": 1675912735057,
"error": null,
"errorUpdateCount": 0,
"errorUpdatedAt": 0,
"fetchFailureCount": 0,
"fetchFailureReason": null,
"fetchMeta": {},
"isInvalidated": false,
"status": "success",
"fetchStatus": "fetching"
},
"queryKey": [
"jobs.isRunning",
{ "library_id": "dc1c41b9-65c6-4a9d-a418-79e628b3ac59", "arg": null }
],
"queryHash": "[\"jobs.isRunning\",{\"arg\":null,\"library_id\":\"dc1c41b9-65c6-4a9d-a418-79e628b3ac59\"}]"
},
{
"state": {
"data": { "version": "0.1.0", "commit": "07401ac0" },
"dataUpdateCount": 1,
"dataUpdatedAt": 1675912735057,
"error": null,
"errorUpdateCount": 0,
"errorUpdatedAt": 0,
"fetchFailureCount": 0,
"fetchFailureReason": null,
"fetchMeta": {},
"isInvalidated": false,
"status": "success",
"fetchStatus": "fetching"
},
"queryKey": ["buildInfo"],
"queryHash": "[\"buildInfo\"]"
},
{
"state": {
"data": {
"version": "0.1.0",
"id": "afa03a65-861e-489e-918b-dac0252fe40c",
"name": "Oscars-MBP-2.lan",
"p2p_port": null,
"data_path": "/Users/oscar/Desktop/sd-ui-testing/sdserver_data"
},
"dataUpdateCount": 1,
"dataUpdatedAt": 1675912735057,
"error": null,
"errorUpdateCount": 0,
"errorUpdatedAt": 0,
"fetchFailureCount": 0,
"fetchFailureReason": null,
"fetchMeta": {},
"isInvalidated": false,
"status": "success",
"fetchStatus": "fetching"
},
"queryKey": ["nodeState"],
"queryHash": "[\"nodeState\"]"
},
{
"state": {
"data": {
"id": 1,
"date_captured": "2023-02-09T03:18:55.378+00:00",
"total_object_count": 0,
"library_db_size": "128",
"total_bytes_used": "0",
"total_bytes_capacity": "994662584320",
"total_unique_bytes": "0",
"total_bytes_free": "170520260608",
"preview_media_bytes": "0"
},
"dataUpdateCount": 1,
"dataUpdatedAt": 1675912735381,
"error": null,
"errorUpdateCount": 0,
"errorUpdatedAt": 0,
"fetchFailureCount": 0,
"fetchFailureReason": null,
"fetchMeta": {},
"isInvalidated": false,
"status": "success",
"fetchStatus": "fetching"
},
"queryKey": [
"library.getStatistics",
{ "library_id": "dc1c41b9-65c6-4a9d-a418-79e628b3ac59", "arg": null }
],
"queryHash": "[\"library.getStatistics\",{\"arg\":null,\"library_id\":\"dc1c41b9-65c6-4a9d-a418-79e628b3ac59\"}]"
}
]
}

View file

@ -2,6 +2,7 @@
import React, { Suspense } from 'react';
import ReactDOM from 'react-dom/client';
import '@sd/ui/style';
import '~/patches';
// THIS MUST GO BEFORE importing the App
import '~/patches';
import App from './App';

View file

@ -0,0 +1,13 @@
import { test } from '@playwright/test';
test('dark screenshot', async ({ page }) => {
await page.emulateMedia({ colorScheme: 'dark' });
await page.goto('/');
await page.screenshot({ path: 'screenshots/overview-dark.png', fullPage: true });
});
test('light screenshot', async ({ page }) => {
await page.emulateMedia({ colorScheme: 'light' });
await page.goto('/');
await page.screenshot({ path: 'screenshots/overview-light.png', fullPage: true });
});

View file

@ -4,7 +4,7 @@
"rootDir": "src",
"declarationDir": "dist"
},
"include": ["src"],
"include": ["src", "src/demoData.json"],
"references": [
{
"path": "../../packages/interface"

View file

@ -1,6 +1,5 @@
import { ProcedureDef } from '@rspc/client';
import { internal_createReactHooksFactory } from '@rspc/react';
import { QueryClient } from '@tanstack/react-query';
import { LibraryArgs, Procedures } from './core';
import { currentLibraryCache } from './hooks';
import { normiCustomHooks } from './normi';
@ -70,7 +69,6 @@ const libraryHooks = hooks.createHooks<
}
});
export const queryClient = new QueryClient();
export const rspc = hooks.createHooks<Procedures>();
export const useBridgeQuery = nonLibraryHooks.useQuery;

View file

@ -4,15 +4,15 @@ import {
init
} from '@sentry/browser';
import '@fontsource/inter/variable.css';
import { QueryClientProvider, defaultContext } from '@tanstack/react-query';
import { defaultContext, QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import dayjs from 'dayjs';
import advancedFormat from 'dayjs/plugin/advancedFormat';
import duration from 'dayjs/plugin/duration';
import relativeTime from 'dayjs/plugin/relativeTime';
import { ErrorBoundary } from 'react-error-boundary';
import { MemoryRouter } from 'react-router-dom';
import { queryClient, useDebugState } from '@sd/client';
import { BrowserRouter, MemoryRouter } from 'react-router-dom';
import { useDebugState } from '@sd/client';
import { Dialogs } from '@sd/ui';
import { AppRouter } from './AppRouter';
import { ErrorFallback } from './ErrorFallback';
@ -29,16 +29,15 @@ init({
integrations: [new HttpContextIntegration(), new DedupeIntegration()]
});
export default function SpacedriveInterface() {
export default function SpacedriveInterface({ router }: { router: 'memory' | 'browser' }) {
const Router = router === 'memory' ? MemoryRouter : BrowserRouter;
return (
<ErrorBoundary FallbackComponent={ErrorFallback}>
<QueryClientProvider client={queryClient} contextSharing={true}>
<Devtools />
<MemoryRouter>
<AppRouter />
</MemoryRouter>
<Dialogs />
</QueryClientProvider>
<Devtools />
<Router>
<AppRouter />
</Router>
<Dialogs />
</ErrorBoundary>
);
}

View file

@ -1,3 +1,4 @@
import { useQueryClient } from '@tanstack/react-query';
import clsx from 'clsx';
import {
ArchiveBox,
@ -229,6 +230,7 @@ function IsRunningJob() {
}
function DebugPanel() {
const queryClient = useQueryClient();
const buildInfo = useBridgeQuery(['buildInfo']);
const nodeState = useBridgeQuery(['nodeState']);
const debugState = useDebugState();
@ -289,6 +291,18 @@ function DebugPanel() {
<SelectOption value="enabled">Enabled</SelectOption>
</Select>
</InputContainer>
<div className="w-full p-2 flex items-center justify-center">
<Button
variant="accent"
onClick={() => {
import('@tanstack/react-query').then((v) => {
console.log(JSON.stringify(v.dehydrate(queryClient, { dehydrateQueries: true })));
});
}}
>
Dump RQ Cache to Console
</Button>
</div>
{/* {platform.showDevtools && (
<InputContainer

View file

@ -1,3 +1,4 @@
import { useQueryClient } from '@tanstack/react-query';
import byteSize from 'byte-size';
import clsx from 'clsx';
import {
@ -19,6 +20,7 @@ import useCounter from '~/hooks/useCounter';
import { useLibraryId } from '~/util';
import { usePlatform } from '~/util/Platform';
import { ScreenContainer } from './_Layout';
import { useEffect } from 'react';
interface StatItemProps {
title: string;

View file

@ -45,6 +45,7 @@ importers:
'@sd/config': workspace:*
'@sd/interface': workspace:*
'@sd/ui': workspace:*
'@tanstack/react-query': ^4.24.4
'@tauri-apps/api': 1.2.0
'@tauri-apps/cli': 1.2.3
'@types/babel-core': ^6.25.7
@ -65,6 +66,7 @@ importers:
'@sd/client': link:../../packages/client
'@sd/interface': link:../../packages/interface
'@sd/ui': link:../../packages/ui
'@tanstack/react-query': 4.24.4_biqbaboplfbrettd7655fr4n2y
'@tauri-apps/api': 1.2.0
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
@ -292,6 +294,7 @@ importers:
apps/web:
specifiers:
'@fontsource/inter': ^4.5.13
'@playwright/test': ^1.30.0
'@rspc/client': ^0.0.0-main-7c0a67c1
'@sd/client': workspace:*
'@sd/config': workspace:*
@ -320,6 +323,7 @@ importers:
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
devDependencies:
'@playwright/test': 1.30.0
'@sd/config': link:../../packages/config
'@sd/ui': link:../../packages/ui
'@types/react': 18.0.27
@ -4415,6 +4419,15 @@ packages:
mkdirp: 1.0.4
rimraf: 3.0.2
/@playwright/test/1.30.0:
resolution: {integrity: sha512-SVxkQw1xvn/Wk/EvBnqWIq6NLo1AppwbYOjNLmyU0R1RoQ3rLEBtmjTnElcnz8VEtn11fptj1ECxK0tgURhajw==}
engines: {node: '>=14'}
hasBin: true
dependencies:
'@types/node': 18.11.18
playwright-core: 1.30.0
dev: true
/@pmmmwh/react-refresh-webpack-plugin/0.5.10_ohj47mxwagpoxvu7nhhwxzphqm:
resolution: {integrity: sha512-j0Ya0hCFZPd4x40qLzbhGsh9TMtdb+CJQiso+WxLOPNasohq9cc5SNUcwsZaRH6++Xh91Xkm/xHCkuIiIu0LUA==}
engines: {node: '>= 10.13'}
@ -7639,6 +7652,24 @@ packages:
react-dom: 18.2.0_react@18.2.0
use-sync-external-store: 1.2.0_react@18.2.0
/@tanstack/react-query/4.24.4_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-RpaS/3T/a3pHuZJbIAzAYRu+1nkp+/enr9hfRXDS/mojwx567UiMksoqW4wUFWlwIvWTXyhot2nbIipTKEg55Q==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
react-native: '*'
peerDependenciesMeta:
react-dom:
optional: true
react-native:
optional: true
dependencies:
'@tanstack/query-core': 4.24.4
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
use-sync-external-store: 1.2.0_react@18.2.0
dev: false
/@tanstack/react-query/4.24.4_tj3nonr5gneraukzjkxpsiy7yu:
resolution: {integrity: sha512-RpaS/3T/a3pHuZJbIAzAYRu+1nkp+/enr9hfRXDS/mojwx567UiMksoqW4wUFWlwIvWTXyhot2nbIipTKEg55Q==}
peerDependencies:
@ -8394,7 +8425,7 @@ packages:
'@babel/plugin-transform-react-jsx-source': 7.19.6_@babel+core@7.20.12
magic-string: 0.26.7
react-refresh: 0.14.0
vite: 4.0.4_@types+node@18.11.18
vite: 4.0.4_sass@1.57.1
transitivePeerDependencies:
- supports-color
@ -16686,6 +16717,12 @@ packages:
dependencies:
find-up: 3.0.0
/playwright-core/1.30.0:
resolution: {integrity: sha512-7AnRmTCf+GVYhHbLJsGUtskWTE33SwMZkybJ0v6rqR1boxq2x36U7p1vDRV7HO2IwTZgmycracLxPEJI49wu4g==}
engines: {node: '>=14'}
hasBin: true
dev: true
/plist/3.0.6:
resolution: {integrity: sha512-WiIVYyrp8TD4w8yCvyeIr+lkmrGRd5u0VbRnU+tP/aRLxP/YadJUYOMZJ/6hIa3oUyVCsycXvtNRgd5XBJIbiA==}
engines: {node: '>=6'}
@ -21023,7 +21060,7 @@ packages:
dependencies:
'@rollup/pluginutils': 5.0.2
'@svgr/core': 6.5.1
vite: 4.0.4_@types+node@18.11.18
vite: 4.0.4_sass@1.57.1
transitivePeerDependencies:
- rollup
- supports-color
@ -21119,6 +21156,7 @@ packages:
rollup: 3.10.0
optionalDependencies:
fsevents: 2.3.2
dev: true
/vite/4.0.4_ovmyjmuuyckt3r3gpaexj2onji:
resolution: {integrity: sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==}
@ -21186,7 +21224,6 @@ packages:
sass: 1.57.1
optionalDependencies:
fsevents: 2.3.2
dev: true
/vlq/1.0.1:
resolution: {integrity: sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==}