Futa Arai 7 месяцев назад
Родитель
Сommit
77b6b0d0e4

+ 3 - 1
packages/remark-lsx/package.json

@@ -45,12 +45,14 @@
     "xss": "^1.0.15"
     "xss": "^1.0.15"
   },
   },
   "devDependencies": {
   "devDependencies": {
+    "@testing-library/react": "^16.0.1",
     "@types/express": "^4",
     "@types/express": "^4",
     "@types/hast": "^3.0.4",
     "@types/hast": "^3.0.4",
     "axios": "^1.10.0",
     "axios": "^1.10.0",
-    "is-absolute-url": "^4.0.1",
     "hast-util-sanitize": "^5.0.1",
     "hast-util-sanitize": "^5.0.1",
     "hast-util-select": "^6.0.2",
     "hast-util-select": "^6.0.2",
+    "is-absolute-url": "^4.0.1",
+    "jsdom": "^26.1.0",
     "unified": "^11.0.0",
     "unified": "^11.0.0",
     "unist-util-visit": "^5.0.0"
     "unist-util-visit": "^5.0.0"
   },
   },

+ 206 - 0
packages/remark-lsx/src/client/stores/lsx/lsx.spec.ts

@@ -0,0 +1,206 @@
+import type { Server } from 'node:http';
+import { renderHook, waitFor } from '@testing-library/react';
+import axios from 'axios';
+import express from 'express';
+import lsxMiddleware from '../../../server';
+
+import { useSWRxLsx } from './lsx';
+
+// Mock the generateBaseQuery function that requires Mongoose
+vi.mock('../../../server/routes/list-pages/generate-base-query', () => ({
+  generateBaseQuery: vi.fn().mockResolvedValue({
+    query: {
+      skip: vi.fn().mockReturnThis(),
+      limit: vi.fn().mockReturnThis(),
+      sort: vi.fn().mockReturnThis(),
+      exec: vi.fn().mockResolvedValue([]),
+    },
+    addConditionToListOnlyDescendants: vi.fn().mockReturnThis(),
+    addConditionToFilteringByViewerForList: vi.fn().mockReturnThis(),
+  }),
+}));
+
+// Mock mongoose model
+vi.mock('mongoose', () => ({
+  model: vi.fn().mockReturnValue({
+    find: vi.fn().mockReturnValue({
+      skip: vi.fn().mockReturnThis(),
+      limit: vi.fn().mockReturnThis(),
+      sort: vi.fn().mockReturnThis(),
+      exec: vi.fn().mockResolvedValue([]),
+    }),
+    countDocuments: vi.fn().mockResolvedValue(0),
+  }),
+}));
+
+const TEST_PORT = 3001;
+const TEST_SERVER_URL = `http://localhost:${TEST_PORT}`;
+
+describe('useSWRxLsx integration tests', () => {
+  let server: Server;
+  let app: express.Application;
+
+  beforeAll(async () => {
+    // Create minimal Express app with just the LSX route
+    app = express();
+    app.use(express.json());
+    app.use(express.urlencoded({ extended: true }));
+
+    // Mock minimal GROWI-like structure for the middleware
+    const mockCrowi = {
+      require: () => () => (req: any, res: any, next: any) => next(), // Mock login middleware
+      accessTokenParser: () => (req: any, res: any, next: any) => next(), // Mock access token parser
+    };
+
+    // Import and setup the LSX middleware
+    lsxMiddleware(mockCrowi, app);
+
+    // Start test server
+    return new Promise<void>((resolve) => {
+      server = app.listen(TEST_PORT, () => {
+        console.log(`Test server running on port ${TEST_PORT}`);
+        resolve();
+      });
+    });
+  });
+
+  afterAll(() => {
+    return new Promise<void>((resolve) => {
+      if (server) {
+        server.close(() => {
+          resolve();
+        });
+      } else {
+        resolve();
+      }
+    });
+  });
+
+  it('should make actual server request and receive 2xx response for basic lsx request', async () => {
+    // Configure axios to use the test server
+    const originalAxios = axios.create();
+
+    // Mock axios.get to point to our test server
+    const axiosGetSpy = vi
+      .spyOn(axios, 'get')
+      .mockImplementation((url, config) => {
+        const fullUrl = url.startsWith('/_api')
+          ? `${TEST_SERVER_URL}${url}`
+          : url;
+        return originalAxios.get(fullUrl, config);
+      });
+
+    const { result } = renderHook(() =>
+      useSWRxLsx('/test-page', { depth: '1' }, false),
+    );
+
+    // Wait for either data or error to be defined
+    await waitFor(
+      () => {
+        expect(result.current.data !== undefined || result.current.error !== undefined).toBe(true);
+      },
+      { timeout: 5000 },
+    );
+
+    // Verify axios.get was called
+    expect(axiosGetSpy).toHaveBeenCalledWith(
+      '/_api/lsx',
+      expect.objectContaining({
+        params: expect.objectContaining({
+          pagePath: '/test-page',
+          options: expect.objectContaining({
+            depth: '1',
+          }),
+        }),
+      }),
+    );
+
+    // Verify we got a successful response (data) or proper error handling
+    expect(result.current.data !== undefined || result.current.error !== undefined).toBe(true);
+
+    axiosGetSpy.mockRestore();
+  });
+
+  it('should handle different lsx options correctly', async () => {
+    const originalAxios = axios.create();
+
+    const axiosGetSpy = vi
+      .spyOn(axios, 'get')
+      .mockImplementation((url, config) => {
+        const fullUrl = url.startsWith('/_api')
+          ? `${TEST_SERVER_URL}${url}`
+          : url;
+        return originalAxios.get(fullUrl, config);
+      });
+
+    const { result } = renderHook(() =>
+      useSWRxLsx(
+        '/parent-page',
+        {
+          depth: '2',
+          filter: 'test',
+          sort: 'createdAt',
+        },
+        false,
+      ),
+    );
+
+    await waitFor(
+      () => {
+        expect(
+          result.current.data !== undefined ||
+            result.current.error !== undefined,
+        ).toBe(true);
+      },
+      { timeout: 5000 },
+    );
+
+    // Verify the request was made with correct parameters
+    expect(axiosGetSpy).toHaveBeenCalledWith(
+      '/_api/lsx',
+      expect.objectContaining({
+        params: expect.objectContaining({
+          pagePath: '/parent-page',
+          options: expect.objectContaining({
+            depth: '2',
+            filter: 'test',
+            sort: 'createdAt',
+          }),
+        }),
+      }),
+    );
+
+    axiosGetSpy.mockRestore();
+  });
+
+  it('should handle server validation errors properly', async () => {
+    const originalAxios = axios.create();
+
+    const axiosGetSpy = vi
+      .spyOn(axios, 'get')
+      .mockImplementation((url, config) => {
+        const fullUrl = url.startsWith('/_api')
+          ? `${TEST_SERVER_URL}${url}`
+          : url;
+        return originalAxios.get(fullUrl, config);
+      });
+
+    const { result } = renderHook(() =>
+      // Missing required pagePath parameter should cause validation error
+      useSWRxLsx('', {}, false),
+    );
+
+    await waitFor(
+      () => {
+        expect(result.current.error).toBeDefined();
+      },
+      { timeout: 5000 },
+    );
+
+    // Should receive an error from the server
+    expect(result.current.error).toBeDefined();
+    expect(result.current.data).toBeUndefined();
+
+    axiosGetSpy.mockRestore();
+  });
+});

+ 5 - 0
packages/remark-lsx/vitest.config.ts

@@ -7,5 +7,10 @@ export default defineConfig({
     environment: 'node',
     environment: 'node',
     clearMocks: true,
     clearMocks: true,
     globals: true,
     globals: true,
+    environmentMatchGlobs: [
+      // Use jsdom for client-side tests
+      ['**/client/**/*.spec.ts', 'jsdom'],
+      ['**/client/**/*.test.ts', 'jsdom'],
+    ],
   },
   },
 });
 });

Разница между файлами не показана из-за своего большого размера
+ 277 - 70
pnpm-lock.yaml


Некоторые файлы не были показаны из-за большого количества измененных файлов