فهرست منبع

add tests for remark-attachment-refs

Futa Arai 8 ماه پیش
والد
کامیت
ac374ad5eb

+ 13 - 3
packages/remark-attachment-refs/package.json

@@ -41,7 +41,7 @@
     "lint:styles": "stylelint \"src/**/*.scss\" \"src/**/*.css\"",
     "lint:styles": "stylelint \"src/**/*.scss\" \"src/**/*.css\"",
     "lint:typecheck": "vue-tsc --noEmit",
     "lint:typecheck": "vue-tsc --noEmit",
     "lint": "run-p lint:*",
     "lint": "run-p lint:*",
-    "test": ""
+    "test": "vitest"
   },
   },
   "dependencies": {
   "dependencies": {
     "@growi/core": "workspace:^",
     "@growi/core": "workspace:^",
@@ -49,22 +49,32 @@
     "@growi/ui": "workspace:^",
     "@growi/ui": "workspace:^",
     "axios": "^1.10.0",
     "axios": "^1.10.0",
     "bunyan": "^1.8.15",
     "bunyan": "^1.8.15",
-    "hast-util-select": "^6.0.2",
     "express": "^4.20.0",
     "express": "^4.20.0",
+    "hast-util-select": "^6.0.2",
     "mongoose": "^6.13.6",
     "mongoose": "^6.13.6",
     "swr": "^2.3.2",
     "swr": "^2.3.2",
     "universal-bunyan": "^0.9.2",
     "universal-bunyan": "^0.9.2",
     "xss": "^1.0.15"
     "xss": "^1.0.15"
   },
   },
   "devDependencies": {
   "devDependencies": {
+    "@testing-library/dom": "^10.4.0",
+    "@testing-library/react": "^16.0.1",
+    "@testing-library/react-hooks": "^8.0.1",
     "@types/bunyan": "^1.8.11",
     "@types/bunyan": "^1.8.11",
     "@types/hast": "^3.0.4",
     "@types/hast": "^3.0.4",
+    "@types/react": "^18.2.14",
+    "@types/react-dom": "^18.2.6",
+    "@types/supertest": "^6.0.2",
     "csstype": "^3.0.2",
     "csstype": "^3.0.2",
+    "happy-dom": "^15.7.4",
     "hast-util-sanitize": "^5.0.1",
     "hast-util-sanitize": "^5.0.1",
     "hast-util-select": "^6.0.2",
     "hast-util-select": "^6.0.2",
     "npm-run-all": "^4.1.5",
     "npm-run-all": "^4.1.5",
+    "supertest": "^7.0.0",
     "unified": "^11.0.0",
     "unified": "^11.0.0",
-    "unist-util-visit": "^5.0.0"
+    "unist-util-visit": "^5.0.0",
+    "vite-tsconfig-paths": "^5.0.1",
+    "vitest": "^2.1.1"
   },
   },
   "peerDependencies": {
   "peerDependencies": {
     "react": "^18.2.0",
     "react": "^18.2.0",

+ 297 - 0
packages/remark-attachment-refs/test/refs-integration.test.ts

@@ -0,0 +1,297 @@
+import { createServer, type Server } from 'node:http';
+import { renderHook, waitFor } from '@testing-library/react';
+import axios from 'axios';
+import express from 'express';
+import {
+  afterAll,
+  beforeAll,
+  beforeEach,
+  describe,
+  expect,
+  it,
+  vi,
+} from 'vitest';
+
+import { useSWRxRef, useSWRxRefs } from '../src/client/stores/refs';
+import { routesFactory } from '../src/server/routes/refs';
+
+// Test server setup
+let testServer: Server;
+let baseURL: string;
+
+// Mock data for testing
+const mockUser = {
+  _id: 'user123',
+  username: 'testuser',
+  email: 'test@example.com',
+};
+
+const mockPage = {
+  _id: 'page123',
+  path: '/test/page',
+};
+
+const mockAttachment = {
+  _id: 'attachment123',
+  originalName: 'test-file.pdf',
+  fileName: 'test-file.pdf',
+  fileFormat: 'pdf',
+  filePath: '/uploads/test-file.pdf',
+  filePathProxied: '/api/attachments/attachment123',
+  downloadPathProxied: '/api/attachments/attachment123/download',
+  fileSize: 1024,
+  attachmentType: 'file',
+  page: 'page123',
+  creator: 'user123',
+  createdAt: '2023-01-01T00:00:00.000Z', // Use string format to match JSON serialization
+};
+
+const mockAttachments = [
+  {
+    _id: 'attachment1',
+    originalName: 'file1.pdf',
+    fileName: 'file1.pdf',
+    filePath: '/uploads/file1.pdf',
+    creator: mockUser._id,
+    page: mockPage._id, // This should match the page ID returned by the page query
+    createdAt: '2023-01-01T00:00:00.000Z', // JSON serialized format
+    fileFormat: 'pdf',
+    fileSize: 1024,
+    attachmentType: 'file',
+    filePathProxied: '/api/attachments/attachment1',
+    downloadPathProxied: '/api/attachments/attachment1/download',
+  },
+  {
+    _id: 'attachment2',
+    originalName: 'file2.jpg',
+    fileName: 'file2.jpg',
+    filePath: '/uploads/file2.jpg',
+    creator: mockUser._id,
+    page: mockPage._id, // This should match the page ID returned by the page query
+    createdAt: '2023-01-01T00:00:00.000Z', // JSON serialized format
+    fileFormat: 'image',
+    fileSize: 2048,
+    attachmentType: 'image',
+    filePathProxied: '/api/attachments/attachment2',
+    downloadPathProxied: '/api/attachments/attachment2/download',
+  },
+];
+
+// Mock the Growi dependencies that the routes need
+const mockCrowi = {
+  require: vi.fn((path) => {
+    if (path === '../middlewares/login-required') {
+      return () => (_req, _res, next) => {
+        // Mock authentication - add user to request
+        _req.user = mockUser;
+        next();
+      };
+    }
+    return vi.fn();
+  }),
+  accessTokenParser: (_req, _res, next) => next(),
+};
+
+// Mock mongoose models
+const mockPageModel = {
+  findByPathAndViewer: vi.fn(),
+  isAccessiblePageByViewer: vi.fn().mockResolvedValue(true),
+  find: vi.fn().mockImplementation(() => {
+    // Return a mock query that has chaining methods
+    const mockQuery = {
+      select: vi.fn().mockReturnThis(),
+      exec: vi.fn().mockResolvedValue([
+        { id: mockPage._id }, // Return our mock page ID
+      ]),
+    };
+    return mockQuery;
+  }),
+  addConditionToFilteringByViewerForList: vi.fn(),
+  PageQueryBuilder: vi.fn().mockImplementation((query) => ({
+    query: query || mockPageModel.find(), // Use the actual query or fall back to find()
+    addConditionToListWithDescendants: vi.fn().mockReturnThis(),
+    addConditionToExcludeTrashed: vi.fn().mockReturnThis(),
+  })),
+};
+
+const mockAttachmentModel = {
+  findOne: vi.fn(),
+  find: vi.fn().mockImplementation(() => {
+    // Return a mock query that supports chaining
+    const mockQuery = {
+      and: vi.fn().mockReturnThis(),
+      populate: vi.fn().mockReturnThis(),
+      exec: vi.fn().mockResolvedValue(mockAttachments), // Return our mock attachments
+    };
+    return mockQuery;
+  }),
+  and: vi.fn().mockReturnThis(),
+  populate: vi.fn().mockReturnThis(),
+  exec: vi.fn().mockResolvedValue(mockAttachments),
+};
+
+// Mock mongoose
+vi.mock('mongoose', () => ({
+  default: {
+    model: vi.fn((modelName) => {
+      if (modelName === 'Page') return mockPageModel;
+      if (modelName === 'Attachment') return mockAttachmentModel;
+      return {};
+    }),
+  },
+  model: vi.fn((modelName) => {
+    if (modelName === 'Attachment') return mockAttachmentModel;
+    return {};
+  }),
+  Types: {
+    ObjectId: class MockObjectId {
+      private id: string;
+
+      constructor(id: string) {
+        this.id = id;
+      }
+
+      static isValid(_id: unknown): boolean {
+        return true; // Accept any ID as valid for testing
+      }
+
+      toString(): string {
+        return this.id;
+      }
+    },
+  },
+}));
+
+// Mock the serializer
+vi.mock('@growi/core/dist/models/serializers', () => ({
+  serializeAttachmentSecurely: vi.fn((attachment) => attachment),
+}));
+
+describe('refs hooks - Integration Tests with Actual Routes', () => {
+  beforeAll(async () => {
+    // Create a real Express app with the actual routes
+    const app = express();
+    app.use(express.json());
+    app.use(express.urlencoded({ extended: true }));
+
+    // Create the routes using the real routesFactory with our mocks
+    const router = routesFactory(mockCrowi);
+    app.use('/_api/attachment-refs', router);
+
+    // Start the test server on a dynamic port
+    testServer = createServer(app);
+
+    await new Promise<void>((resolve) => {
+      testServer.listen(0, () => {
+        const address = testServer.address();
+        if (address && typeof address === 'object') {
+          const port = address.port;
+          baseURL = `http://localhost:${port}`;
+          console.log(`Test server started on ${baseURL}`);
+        }
+        resolve();
+      });
+    });
+  });
+
+  afterAll(async () => {
+    // Clean up the test server
+    if (testServer) {
+      await new Promise<void>((resolve) => {
+        testServer.close(() => resolve());
+      });
+    }
+  });
+
+  beforeEach(() => {
+    // Reset all mocks before each test
+    vi.clearAllMocks();
+
+    // Setup default mock behaviors
+    mockPageModel.findByPathAndViewer.mockResolvedValue(mockPage);
+    mockPageModel.isAccessiblePageByViewer.mockResolvedValue(true);
+
+    mockAttachmentModel.findOne.mockImplementation(() => ({
+      populate: vi.fn().mockResolvedValue(mockAttachment),
+    }));
+
+    mockAttachmentModel.find.mockReturnValue({
+      and: vi.fn().mockReturnThis(),
+      populate: vi.fn().mockReturnThis(),
+      exec: vi.fn().mockResolvedValue(mockAttachments),
+    });
+  });
+
+  describe('SWR Hook Integration Tests', () => {
+    it('should test useSWRxRef hook with real server requests', async () => {
+      // Arrange
+      const pagePath = '/test/page';
+      const fileNameOrId = 'test-file.pdf';
+
+      // Configure axios to use our test server
+      const originalDefaults = axios.defaults.baseURL;
+      axios.defaults.baseURL = baseURL;
+
+      try {
+        console.log(`Testing with baseURL: ${baseURL}`);
+
+        // Act - Test the hook with real server
+        const { result } = renderHook(() =>
+          useSWRxRef(pagePath, fileNameOrId, false),
+        );
+
+        // Wait for the hook to complete the request
+        await waitFor(
+          () => {
+            console.log('Hook result:', result.current);
+            expect(result.current.data).toBeDefined();
+          },
+          { timeout: 5000 },
+        );
+
+        // Assert - Hook should return attachment data from real server
+        expect(result.current.data).toEqual(mockAttachment);
+        expect(result.current.error).toBeUndefined();
+      } finally {
+        // Restore original axios defaults
+        axios.defaults.baseURL = originalDefaults;
+      }
+    });
+
+    it('should test useSWRxRefs hook with real server requests', async () => {
+      // Arrange
+      const pagePath = '/test/page';
+      const prefix = '/test';
+      const options = { depth: '1', regexp: '.*\\.pdf$' };
+
+      // Configure axios to use our test server
+      const originalDefaults = axios.defaults.baseURL;
+      axios.defaults.baseURL = baseURL;
+
+      try {
+        console.log(`Testing with baseURL: ${baseURL}`);
+
+        // Act - Test the hook with real server
+        const { result } = renderHook(() =>
+          useSWRxRefs(pagePath, prefix, options, false),
+        );
+
+        // Wait for the hook to complete the request
+        await waitFor(
+          () => {
+            console.log('Hook result:', result.current);
+            expect(result.current.data).toBeDefined();
+          },
+          { timeout: 5000 },
+        );
+
+        // Assert - Hook should return attachments data from real server
+        expect(result.current.data).toEqual(mockAttachments);
+        expect(result.current.error).toBeUndefined();
+      } finally {
+        // Restore original axios defaults
+        axios.defaults.baseURL = originalDefaults;
+      }
+    });
+  });
+});

+ 0 - 0
packages/remark-attachment-refs/test/refs.test.ts


+ 10 - 0
packages/remark-attachment-refs/test/setup.ts

@@ -0,0 +1,10 @@
+// Test setup file for vitest
+
+// Mock console methods to avoid noise in tests
+global.console = {
+  ...console,
+  // Uncomment to mock console methods
+  // log: vi.fn(),
+  // warn: vi.fn(),
+  // error: vi.fn(),
+};

+ 12 - 0
packages/remark-attachment-refs/vitest.config.ts

@@ -0,0 +1,12 @@
+import tsconfigPaths from 'vite-tsconfig-paths';
+import { defineConfig } from 'vitest/config';
+
+export default defineConfig({
+  plugins: [tsconfigPaths()],
+  test: {
+    environment: 'happy-dom', // React testing environment
+    clearMocks: true,
+    globals: true,
+    setupFiles: ['./test/setup.ts'],
+  },
+});

+ 143 - 1
pnpm-lock.yaml

@@ -1566,27 +1566,57 @@ importers:
         specifier: ^1.0.15
         specifier: ^1.0.15
         version: 1.0.15
         version: 1.0.15
     devDependencies:
     devDependencies:
+      '@testing-library/dom':
+        specifier: ^10.4.0
+        version: 10.4.0
+      '@testing-library/react':
+        specifier: ^16.0.1
+        version: 16.0.1(@testing-library/dom@10.4.0)(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
+      '@testing-library/react-hooks':
+        specifier: ^8.0.1
+        version: 8.0.1(@types/react@18.3.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
       '@types/bunyan':
       '@types/bunyan':
         specifier: ^1.8.11
         specifier: ^1.8.11
         version: 1.8.11
         version: 1.8.11
       '@types/hast':
       '@types/hast':
         specifier: ^3.0.4
         specifier: ^3.0.4
         version: 3.0.4
         version: 3.0.4
+      '@types/react':
+        specifier: ^18.2.14
+        version: 18.3.3
+      '@types/react-dom':
+        specifier: ^18.2.6
+        version: 18.3.0
+      '@types/supertest':
+        specifier: ^6.0.2
+        version: 6.0.3
       csstype:
       csstype:
         specifier: ^3.0.2
         specifier: ^3.0.2
         version: 3.1.3
         version: 3.1.3
+      happy-dom:
+        specifier: ^15.7.4
+        version: 15.7.4
       hast-util-sanitize:
       hast-util-sanitize:
         specifier: ^5.0.1
         specifier: ^5.0.1
         version: 5.0.1
         version: 5.0.1
       npm-run-all:
       npm-run-all:
         specifier: ^4.1.5
         specifier: ^4.1.5
         version: 4.1.5
         version: 4.1.5
+      supertest:
+        specifier: ^7.0.0
+        version: 7.1.1
       unified:
       unified:
         specifier: ^11.0.0
         specifier: ^11.0.0
         version: 11.0.5
         version: 11.0.5
       unist-util-visit:
       unist-util-visit:
         specifier: ^5.0.0
         specifier: ^5.0.0
         version: 5.0.0
         version: 5.0.0
+      vite-tsconfig-paths:
+        specifier: ^5.0.1
+        version: 5.0.1(typescript@5.4.2)(vite@5.4.19(@types/node@22.15.21)(sass@1.77.6)(terser@5.43.1))
+      vitest:
+        specifier: ^2.1.1
+        version: 2.1.1(@types/node@22.15.21)(@vitest/ui@2.1.1)(happy-dom@15.7.4)(sass@1.77.6)(terser@5.43.1)
 
 
   packages/remark-drawio:
   packages/remark-drawio:
     dependencies:
     dependencies:
@@ -5022,6 +5052,22 @@ packages:
     resolution: {integrity: sha512-xGGHpBXYSHUUr6XsKBfs85TWlYKpTc37cSBBVrXcib2MkHLboWlkClhWF37JKlDb9KEq3dHs+f2xR7XJEWGBxA==}
     resolution: {integrity: sha512-xGGHpBXYSHUUr6XsKBfs85TWlYKpTc37cSBBVrXcib2MkHLboWlkClhWF37JKlDb9KEq3dHs+f2xR7XJEWGBxA==}
     engines: {node: '>=14', npm: '>=6', yarn: '>=1'}
     engines: {node: '>=14', npm: '>=6', yarn: '>=1'}
 
 
+  '@testing-library/react-hooks@8.0.1':
+    resolution: {integrity: sha512-Aqhl2IVmLt8IovEVarNDFuJDVWVvhnr9/GCU6UUnrYXwgDFF9h2L2o2P9KBni1AST5sT6riAyoukFLyjQUgD/g==}
+    engines: {node: '>=12'}
+    peerDependencies:
+      '@types/react': ^16.9.0 || ^17.0.0
+      react: ^16.9.0 || ^17.0.0
+      react-dom: ^16.9.0 || ^17.0.0
+      react-test-renderer: ^16.9.0 || ^17.0.0
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+      react-dom:
+        optional: true
+      react-test-renderer:
+        optional: true
+
   '@testing-library/react@16.0.1':
   '@testing-library/react@16.0.1':
     resolution: {integrity: sha512-dSmwJVtJXmku+iocRhWOUFbrERC76TX2Mnf0ATODz8brzAZrMBbzLwQixlBSanZxR6LddK3eiwpSFZgDET1URg==}
     resolution: {integrity: sha512-dSmwJVtJXmku+iocRhWOUFbrERC76TX2Mnf0ATODz8brzAZrMBbzLwQixlBSanZxR6LddK3eiwpSFZgDET1URg==}
     engines: {node: '>=18'}
     engines: {node: '>=18'}
@@ -19883,6 +19929,15 @@ snapshots:
       lodash: 4.17.21
       lodash: 4.17.21
       redent: 3.0.0
       redent: 3.0.0
 
 
+  '@testing-library/react-hooks@8.0.1(@types/react@18.3.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
+    dependencies:
+      '@babel/runtime': 7.25.4
+      react: 18.2.0
+      react-error-boundary: 3.1.4(react@18.2.0)
+    optionalDependencies:
+      '@types/react': 18.3.3
+      react-dom: 18.2.0(react@18.2.0)
+
   '@testing-library/react@16.0.1(@testing-library/dom@10.4.0)(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
   '@testing-library/react@16.0.1(@testing-library/dom@10.4.0)(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
     dependencies:
     dependencies:
       '@babel/runtime': 7.25.4
       '@babel/runtime': 7.25.4
@@ -21135,6 +21190,14 @@ snapshots:
     optionalDependencies:
     optionalDependencies:
       vite: 5.4.19(@types/node@20.14.0)(sass@1.77.6)(terser@5.43.1)
       vite: 5.4.19(@types/node@20.14.0)(sass@1.77.6)(terser@5.43.1)
 
 
+  '@vitest/mocker@2.1.1(@vitest/spy@2.1.1)(vite@5.4.19(@types/node@22.15.21)(sass@1.77.6)(terser@5.43.1))':
+    dependencies:
+      '@vitest/spy': 2.1.1
+      estree-walker: 3.0.3
+      magic-string: 0.30.11
+    optionalDependencies:
+      vite: 5.4.19(@types/node@22.15.21)(sass@1.77.6)(terser@5.43.1)
+
   '@vitest/pretty-format@2.1.1':
   '@vitest/pretty-format@2.1.1':
     dependencies:
     dependencies:
       tinyrainbow: 1.2.0
       tinyrainbow: 1.2.0
@@ -21163,7 +21226,7 @@ snapshots:
       sirv: 2.0.4
       sirv: 2.0.4
       tinyglobby: 0.2.6
       tinyglobby: 0.2.6
       tinyrainbow: 1.2.0
       tinyrainbow: 1.2.0
-      vitest: 2.1.1(@types/node@20.14.0)(@vitest/ui@2.1.1)(happy-dom@15.7.4)(sass@1.77.6)(terser@5.43.1)
+      vitest: 2.1.1(@types/node@22.15.21)(@vitest/ui@2.1.1)(happy-dom@15.7.4)(sass@1.77.6)(terser@5.43.1)
 
 
   '@vitest/utils@2.1.1':
   '@vitest/utils@2.1.1':
     dependencies:
     dependencies:
@@ -30688,6 +30751,10 @@ snapshots:
     optionalDependencies:
     optionalDependencies:
       typescript: 5.0.4
       typescript: 5.0.4
 
 
+  tsconfck@3.0.3(typescript@5.4.2):
+    optionalDependencies:
+      typescript: 5.4.2
+
   tsconfig-paths@3.14.1:
   tsconfig-paths@3.14.1:
     dependencies:
     dependencies:
       '@types/json5': 0.0.29
       '@types/json5': 0.0.29
@@ -31265,6 +31332,23 @@ snapshots:
       - supports-color
       - supports-color
       - terser
       - terser
 
 
+  vite-node@2.1.1(@types/node@22.15.21)(sass@1.77.6)(terser@5.43.1):
+    dependencies:
+      cac: 6.7.14
+      debug: 4.4.1(supports-color@5.5.0)
+      pathe: 1.1.2
+      vite: 5.4.19(@types/node@22.15.21)(sass@1.77.6)(terser@5.43.1)
+    transitivePeerDependencies:
+      - '@types/node'
+      - less
+      - lightningcss
+      - sass
+      - sass-embedded
+      - stylus
+      - sugarss
+      - supports-color
+      - terser
+
   vite-plugin-dts@3.9.1(@types/node@20.14.0)(rollup@4.41.0)(typescript@5.0.4)(vite@5.4.19(@types/node@20.14.0)(sass@1.77.6)(terser@5.43.1)):
   vite-plugin-dts@3.9.1(@types/node@20.14.0)(rollup@4.41.0)(typescript@5.0.4)(vite@5.4.19(@types/node@20.14.0)(sass@1.77.6)(terser@5.43.1)):
     dependencies:
     dependencies:
       '@microsoft/api-extractor': 7.43.0(@types/node@20.14.0)
       '@microsoft/api-extractor': 7.43.0(@types/node@20.14.0)
@@ -31293,6 +31377,17 @@ snapshots:
       - supports-color
       - supports-color
       - typescript
       - typescript
 
 
+  vite-tsconfig-paths@5.0.1(typescript@5.4.2)(vite@5.4.19(@types/node@22.15.21)(sass@1.77.6)(terser@5.43.1)):
+    dependencies:
+      debug: 4.4.1(supports-color@5.5.0)
+      globrex: 0.1.2
+      tsconfck: 3.0.3(typescript@5.4.2)
+    optionalDependencies:
+      vite: 5.4.19(@types/node@22.15.21)(sass@1.77.6)(terser@5.43.1)
+    transitivePeerDependencies:
+      - supports-color
+      - typescript
+
   vite@5.4.19(@types/node@20.14.0)(sass@1.77.6)(terser@5.43.1):
   vite@5.4.19(@types/node@20.14.0)(sass@1.77.6)(terser@5.43.1):
     dependencies:
     dependencies:
       esbuild: 0.21.5
       esbuild: 0.21.5
@@ -31304,6 +31399,17 @@ snapshots:
       sass: 1.77.6
       sass: 1.77.6
       terser: 5.43.1
       terser: 5.43.1
 
 
+  vite@5.4.19(@types/node@22.15.21)(sass@1.77.6)(terser@5.43.1):
+    dependencies:
+      esbuild: 0.21.5
+      postcss: 8.5.5
+      rollup: 4.41.0
+    optionalDependencies:
+      '@types/node': 22.15.21
+      fsevents: 2.3.3
+      sass: 1.77.6
+      terser: 5.43.1
+
   vitest-mock-extended@2.0.2(typescript@5.0.4)(vitest@2.1.1):
   vitest-mock-extended@2.0.2(typescript@5.0.4)(vitest@2.1.1):
     dependencies:
     dependencies:
       ts-essentials: 10.0.2(typescript@5.0.4)
       ts-essentials: 10.0.2(typescript@5.0.4)
@@ -31346,6 +31452,42 @@ snapshots:
       - supports-color
       - supports-color
       - terser
       - terser
 
 
+  vitest@2.1.1(@types/node@22.15.21)(@vitest/ui@2.1.1)(happy-dom@15.7.4)(sass@1.77.6)(terser@5.43.1):
+    dependencies:
+      '@vitest/expect': 2.1.1
+      '@vitest/mocker': 2.1.1(@vitest/spy@2.1.1)(vite@5.4.19(@types/node@22.15.21)(sass@1.77.6)(terser@5.43.1))
+      '@vitest/pretty-format': 2.1.1
+      '@vitest/runner': 2.1.1
+      '@vitest/snapshot': 2.1.1
+      '@vitest/spy': 2.1.1
+      '@vitest/utils': 2.1.1
+      chai: 5.1.1
+      debug: 4.4.1(supports-color@5.5.0)
+      magic-string: 0.30.11
+      pathe: 1.1.2
+      std-env: 3.7.0
+      tinybench: 2.9.0
+      tinyexec: 0.3.0
+      tinypool: 1.0.1
+      tinyrainbow: 1.2.0
+      vite: 5.4.19(@types/node@22.15.21)(sass@1.77.6)(terser@5.43.1)
+      vite-node: 2.1.1(@types/node@22.15.21)(sass@1.77.6)(terser@5.43.1)
+      why-is-node-running: 2.3.0
+    optionalDependencies:
+      '@types/node': 22.15.21
+      '@vitest/ui': 2.1.1(vitest@2.1.1)
+      happy-dom: 15.7.4
+    transitivePeerDependencies:
+      - less
+      - lightningcss
+      - msw
+      - sass
+      - sass-embedded
+      - stylus
+      - sugarss
+      - supports-color
+      - terser
+
   void-elements@3.1.0: {}
   void-elements@3.1.0: {}
 
 
   vscode-jsonrpc@8.2.0: {}
   vscode-jsonrpc@8.2.0: {}