Explorar o código

Merge pull request #10353 from growilabs/support/136168-update-axios

support: Update axios
mergify[bot] hai 6 meses
pai
achega
35d0be97ff

+ 1 - 3
apps/app/package.json

@@ -104,7 +104,7 @@
     "archiver": "^5.3.0",
     "archiver": "^5.3.0",
     "array.prototype.flatmap": "^1.2.2",
     "array.prototype.flatmap": "^1.2.2",
     "async-canvas-to-blob": "^1.0.3",
     "async-canvas-to-blob": "^1.0.3",
-    "axios": "^0.24.0",
+    "axios": "^1.11.0",
     "axios-retry": "^3.2.4",
     "axios-retry": "^3.2.4",
     "babel-plugin-superjson-next": "^0.4.2",
     "babel-plugin-superjson-next": "^0.4.2",
     "body-parser": "^1.20.3",
     "body-parser": "^1.20.3",
@@ -273,9 +273,7 @@
     "@popperjs/core": "^2.11.8",
     "@popperjs/core": "^2.11.8",
     "@swc-node/jest": "^1.8.1",
     "@swc-node/jest": "^1.8.1",
     "@swc/jest": "^0.2.36",
     "@swc/jest": "^0.2.36",
-    "@testing-library/dom": "^10.4.0",
     "@testing-library/jest-dom": "^6.5.0",
     "@testing-library/jest-dom": "^6.5.0",
-    "@testing-library/react": "^16.0.1",
     "@testing-library/user-event": "^14.5.2",
     "@testing-library/user-event": "^14.5.2",
     "@types/archiver": "^6.0.2",
     "@types/archiver": "^6.0.2",
     "@types/bunyan": "^1.8.11",
     "@types/bunyan": "^1.8.11",

+ 1 - 1
apps/app/src/client/util/apiv1-client.ts

@@ -46,7 +46,7 @@ export async function apiPost<T>(path: string, params: unknown = {}): Promise<T>
 }
 }
 
 
 export async function apiPostForm<T>(path: string, formData: FormData): Promise<T> {
 export async function apiPostForm<T>(path: string, formData: FormData): Promise<T> {
-  return apiPost<T>(path, formData);
+  return apiRequest<T>('postForm', path, formData);
 }
 }
 
 
 export async function apiDelete<T>(path: string, params: unknown = {}): Promise<T> {
 export async function apiDelete<T>(path: string, params: unknown = {}): Promise<T> {

+ 1 - 1
apps/app/src/client/util/apiv3-client.ts

@@ -50,7 +50,7 @@ export async function apiv3Post<T = any>(path: string, params: unknown = {}): Pr
 }
 }
 
 
 export async function apiv3PostForm<T = any>(path: string, formData: FormData): Promise<AxiosResponse<T>> {
 export async function apiv3PostForm<T = any>(path: string, formData: FormData): Promise<AxiosResponse<T>> {
-  return apiv3Post<T>(path, formData);
+  return apiv3Request('postForm', path, formData);
 }
 }
 
 
 export async function apiv3Put<T = any>(path: string, params: unknown = {}): Promise<AxiosResponse<T>> {
 export async function apiv3Put<T = any>(path: string, params: unknown = {}): Promise<AxiosResponse<T>> {

+ 0 - 1
apps/pdf-converter/package.json

@@ -36,7 +36,6 @@
     "@tsed/schema": "=8.5.0",
     "@tsed/schema": "=8.5.0",
     "@tsed/swagger": "=8.5.0",
     "@tsed/swagger": "=8.5.0",
     "@tsed/terminus": "=8.5.0",
     "@tsed/terminus": "=8.5.0",
-    "axios": "^0.24.0",
     "express": "^4.19.2",
     "express": "^4.19.2",
     "puppeteer": "^23.1.1",
     "puppeteer": "^23.1.1",
     "puppeteer-cluster": "^0.24.0",
     "puppeteer-cluster": "^0.24.0",

+ 1 - 1
apps/slackbot-proxy/package.json

@@ -45,7 +45,7 @@
     "@tsed/schema": "=6.43.0",
     "@tsed/schema": "=6.43.0",
     "@tsed/swagger": "=6.43.0",
     "@tsed/swagger": "=6.43.0",
     "@tsed/typeorm": "=6.43.0",
     "@tsed/typeorm": "=6.43.0",
-    "axios": "^0.24.0",
+    "axios": "^1.11.0",
     "body-parser": "^1.20.3",
     "body-parser": "^1.20.3",
     "browser-bunyan": "^1.6.3",
     "browser-bunyan": "^1.6.3",
     "bunyan": "^1.8.15",
     "bunyan": "^1.8.15",

+ 3 - 0
package.json

@@ -47,6 +47,9 @@
     "@changesets/cli": "^2.27.3",
     "@changesets/cli": "^2.27.3",
     "@faker-js/faker": "^9.0.1",
     "@faker-js/faker": "^9.0.1",
     "@playwright/test": "^1.49.1",
     "@playwright/test": "^1.49.1",
+    "@testing-library/dom": "^10.4.0",
+    "@testing-library/react": "^16.0.1",
+    "@testing-library/react-hooks": "^8.0.1",
     "@swc-node/register": "^1.10.9",
     "@swc-node/register": "^1.10.9",
     "@swc/core": "^1.5.25",
     "@swc/core": "^1.5.25",
     "@swc/helpers": "^0.5.11",
     "@swc/helpers": "^0.5.11",

+ 1 - 1
packages/pdf-converter-client/package.json

@@ -12,7 +12,7 @@
     "build": "pnpm gen:client-code && tsc -p tsconfig.json"
     "build": "pnpm gen:client-code && tsc -p tsconfig.json"
   },
   },
   "dependencies": {
   "dependencies": {
-    "axios": "^0.24.0",
+    "axios": "^1.11.0",
     "tslib": "^2.8.0"
     "tslib": "^2.8.0"
   },
   },
   "devDependencies": {
   "devDependencies": {

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

@@ -41,16 +41,16 @@
     "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 run --coverage"
   },
   },
   "dependencies": {
   "dependencies": {
     "@growi/core": "workspace:^",
     "@growi/core": "workspace:^",
     "@growi/remark-growi-directive": "workspace:^",
     "@growi/remark-growi-directive": "workspace:^",
     "@growi/ui": "workspace:^",
     "@growi/ui": "workspace:^",
-    "axios": "^0.24.0",
+    "axios": "^1.11.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",
@@ -59,10 +59,15 @@
   "devDependencies": {
   "devDependencies": {
     "@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"
   },
   },

+ 2 - 0
packages/remark-attachment-refs/src/@types/declaration.d.ts

@@ -0,0 +1,2 @@
+// prevent TS2307: Cannot find module './xxx.module.scss' or its corresponding type declarations.
+declare module '*.scss';

+ 298 - 0
packages/remark-attachment-refs/src/client/stores/refs.spec.ts

@@ -0,0 +1,298 @@
+// tests for assuring axios request succeeds in version change
+
+import type { Server } from 'node:http';
+import { renderHook, waitFor } from '@testing-library/react';
+import axios from 'axios';
+import express from 'express';
+import refsMiddleware from '../../server';
+
+import { useSWRxRef, useSWRxRefs } from './refs';
+
+// Mock the IAttachmentHasId type for testing
+const mockAttachment = {
+  _id: '507f1f77bcf86cd799439011',
+  fileFormat: 'image/jpeg',
+  fileName: 'test-image.jpg',
+  originalName: 'test-image.jpg',
+  filePath: 'attachment/507f1f77bcf86cd799439011.jpg',
+  creator: {
+    _id: '507f1f77bcf86cd799439012',
+    name: 'Test User',
+    username: 'testuser',
+  },
+  page: '507f1f77bcf86cd799439013',
+  createdAt: '2023-01-01T00:00:00.000Z',
+  fileSize: 1024000,
+};
+
+// Mock PageQueryBuilder
+const mockPageQueryBuilder = {
+  addConditionToListWithDescendants: vi.fn().mockReturnThis(),
+  addConditionToExcludeTrashed: vi.fn().mockReturnThis(),
+  query: {
+    select: vi.fn().mockReturnValue({
+      exec: vi.fn().mockResolvedValue([{ id: '507f1f77bcf86cd799439013' }]),
+    }),
+    and: vi.fn().mockReturnThis(),
+  },
+};
+
+vi.mock('mongoose', async (importOriginal) => {
+  const actual = await importOriginal<typeof import('mongoose')>();
+  return {
+    ...actual,
+    default: {
+      ...actual.default,
+      model: vi.fn().mockImplementation((modelName) => {
+        const mockModel = {
+          findByPathAndViewer: vi.fn().mockResolvedValue({
+            _id: '507f1f77bcf86cd799439013',
+            path: '/test-page',
+          }),
+          isAccessiblePageByViewer: vi.fn().mockResolvedValue(true),
+          find: vi.fn().mockReturnValue({
+            select: vi.fn().mockReturnValue({
+              exec: vi
+                .fn()
+                .mockResolvedValue([{ id: '507f1f77bcf86cd799439013' }]),
+            }),
+            and: vi.fn().mockReturnThis(),
+          }),
+          addConditionToFilteringByViewerForList: vi.fn(),
+          PageQueryBuilder: vi
+            .fn()
+            .mockImplementation(() => mockPageQueryBuilder),
+        };
+
+        if (modelName === 'Attachment') {
+          return {
+            findOne: vi.fn().mockReturnValue({
+              populate: vi.fn().mockResolvedValue(mockAttachment),
+            }),
+            find: vi.fn().mockReturnValue({
+              and: vi.fn().mockReturnThis(),
+              populate: vi.fn().mockReturnThis(),
+              exec: vi.fn().mockResolvedValue([mockAttachment]),
+            }),
+          };
+        }
+
+        return mockModel;
+      }),
+    },
+    model: vi.fn().mockImplementation((modelName) => {
+      const mockModel = {
+        findByPathAndViewer: vi.fn().mockResolvedValue({
+          _id: '507f1f77bcf86cd799439013',
+          path: '/test-page',
+        }),
+        isAccessiblePageByViewer: vi.fn().mockResolvedValue(true),
+        find: vi.fn().mockReturnValue({
+          select: vi.fn().mockReturnValue({
+            exec: vi
+              .fn()
+              .mockResolvedValue([{ id: '507f1f77bcf86cd799439013' }]),
+          }),
+          and: vi.fn().mockReturnThis(),
+        }),
+        addConditionToFilteringByViewerForList: vi.fn(),
+        PageQueryBuilder: vi
+          .fn()
+          .mockImplementation(() => mockPageQueryBuilder),
+      };
+
+      if (modelName === 'Attachment') {
+        return {
+          findOne: vi.fn().mockReturnValue({
+            populate: vi.fn().mockResolvedValue(mockAttachment),
+          }),
+          find: vi.fn().mockReturnValue({
+            and: vi.fn().mockReturnThis(),
+            populate: vi.fn().mockReturnThis(),
+            exec: vi.fn().mockResolvedValue([mockAttachment]),
+          }),
+        };
+      }
+
+      return mockModel;
+    }),
+  };
+});
+
+// Mock @growi/core modules
+vi.mock('@growi/core', () => ({
+  SCOPE: {
+    READ: { FEATURES: { PAGE: 'read:page' } },
+  },
+}));
+
+vi.mock('@growi/core/dist/models/serializers', () => ({
+  serializeAttachmentSecurely: vi
+    .fn()
+    .mockImplementation((attachment) => attachment),
+}));
+
+vi.mock('@growi/core/dist/remark-plugins', () => ({
+  OptionParser: {
+    parseRange: vi.fn().mockReturnValue({ start: 1, end: 3 }),
+  },
+}));
+
+// Mock FilterXSS
+vi.mock('xss', () => ({
+  FilterXSS: vi.fn().mockImplementation(() => ({
+    process: vi.fn().mockImplementation((input) => input),
+  })),
+}));
+
+const TEST_PORT = 3002;
+const TEST_SERVER_URL = `http://localhost:${TEST_PORT}`;
+
+describe('useSWRxRef and useSWRxRefs integration tests', () => {
+  let server: Server;
+  let app: express.Application;
+
+  const setupAxiosSpy = () => {
+    const originalAxios = axios.create();
+    return vi.spyOn(axios, 'get').mockImplementation((url, config) => {
+      const fullUrl = url.startsWith('/_api')
+        ? `${TEST_SERVER_URL}${url}`
+        : url;
+      return originalAxios.get(fullUrl, config);
+    });
+  };
+
+  beforeAll(async () => {
+    app = express();
+    app.use(express.json());
+    app.use(express.urlencoded({ extended: true }));
+
+    app.use((req, res, next) => {
+      res.header('Access-Control-Allow-Origin', '*');
+      res.header(
+        'Access-Control-Allow-Methods',
+        'GET, POST, PUT, DELETE, OPTIONS',
+      );
+      res.header(
+        'Access-Control-Allow-Headers',
+        'Origin, X-Requested-With, Content-Type, Accept, Authorization',
+      );
+      next();
+    });
+
+    const mockCrowi = {
+      require: () => () => (req: any, res: any, next: any) => next(),
+      accessTokenParser: () => (req: any, res: any, next: any) => {
+        req.user = { _id: '507f1f77bcf86cd799439012', username: 'testuser' };
+        next();
+      },
+    };
+
+    refsMiddleware(mockCrowi, app);
+
+    return new Promise<void>((resolve) => {
+      server = app.listen(TEST_PORT, () => {
+        resolve();
+      });
+    });
+  });
+
+  afterAll(() => {
+    return new Promise<void>((resolve) => {
+      if (server) {
+        server.close(() => {
+          resolve();
+        });
+      } else {
+        resolve();
+      }
+    });
+  });
+
+  describe('useSWRxRef', () => {
+    it('should make actual server request and receive attachment data for single ref request', async () => {
+      const axiosGetSpy = setupAxiosSpy();
+
+      const { result } = renderHook(() =>
+        useSWRxRef('/test-page', 'test-image.jpg', false),
+      );
+
+      await waitFor(() => expect(result.current.data).toBeDefined(), {
+        timeout: 5000,
+      });
+
+      expect(axiosGetSpy).toHaveBeenCalledWith(
+        '/_api/attachment-refs/ref',
+        expect.objectContaining({
+          params: expect.objectContaining({
+            pagePath: '/test-page',
+            fileNameOrId: 'test-image.jpg',
+          }),
+        }),
+      );
+
+      expect(result.current.data).toBeDefined();
+      expect(result.current.error).toBeUndefined();
+
+      axiosGetSpy.mockRestore();
+    });
+  });
+
+  describe('useSWRxRefs', () => {
+    it('should make actual server request and receive attachments data for refs request with pagePath', async () => {
+      const axiosGetSpy = setupAxiosSpy();
+
+      const { result } = renderHook(() =>
+        useSWRxRefs('/test-page', undefined, {}, false),
+      );
+
+      await waitFor(() => expect(result.current.data).toBeDefined(), {
+        timeout: 5000,
+      });
+
+      expect(axiosGetSpy).toHaveBeenCalledWith(
+        '/_api/attachment-refs/refs',
+        expect.objectContaining({
+          params: expect.objectContaining({
+            pagePath: '/test-page',
+            prefix: undefined,
+            options: {},
+          }),
+        }),
+      );
+
+      expect(result.current.data).toBeDefined();
+      expect(result.current.error).toBeUndefined();
+
+      axiosGetSpy.mockRestore();
+    });
+
+    it('should make actual server request and receive attachments data for refs request with prefix', async () => {
+      const axiosGetSpy = setupAxiosSpy();
+
+      const { result } = renderHook(() =>
+        useSWRxRefs('', '/test-prefix', { depth: '2' }, false),
+      );
+
+      await waitFor(() => expect(result.current.data).toBeDefined(), {
+        timeout: 5000,
+      });
+
+      expect(axiosGetSpy).toHaveBeenCalledWith(
+        '/_api/attachment-refs/refs',
+        expect.objectContaining({
+          params: expect.objectContaining({
+            pagePath: '',
+            prefix: '/test-prefix',
+            options: { depth: '2' },
+          }),
+        }),
+      );
+
+      expect(result.current.data).toBeDefined();
+      expect(result.current.error).toBeUndefined();
+
+      axiosGetSpy.mockRestore();
+    });
+  });
+});

+ 4 - 3
packages/remark-attachment-refs/src/server/routes/refs.ts

@@ -179,9 +179,10 @@ export const routesFactory = (crowi): any => {
     async (req: RequestWithUser, res) => {
     async (req: RequestWithUser, res) => {
       const user = req.user;
       const user = req.user;
       const { prefix, pagePath } = req.query;
       const { prefix, pagePath } = req.query;
-      const options: Record<string, string | undefined> = JSON.parse(
-        req.query.options?.toString() ?? '',
-      );
+      const options: Record<string, string | undefined> =
+        typeof req.query.options === 'string'
+          ? JSON.parse(req.query.options)
+          : (req.query.options ?? {});
 
 
       // check either 'prefix' or 'pagePath ' is specified
       // check either 'prefix' or 'pagePath ' is specified
       if (prefix == null && pagePath == null) {
       if (prefix == null && pagePath == null) {

+ 1 - 0
packages/remark-attachment-refs/tsconfig.json

@@ -7,6 +7,7 @@
     "paths": {
     "paths": {
       "~/*": ["./src/*"]
       "~/*": ["./src/*"]
     },
     },
+    "types": ["vitest/globals"],
 
 
     /* TODO: remove below flags for strict checking */
     /* TODO: remove below flags for strict checking */
     "strict": false,
     "strict": false,

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

@@ -0,0 +1,16 @@
+import tsconfigPaths from 'vite-tsconfig-paths';
+import { defineConfig } from 'vitest/config';
+
+export default defineConfig({
+  plugins: [tsconfigPaths()],
+  test: {
+    environment: 'node',
+    clearMocks: true,
+    globals: true,
+    environmentMatchGlobs: [
+      // Use jsdom for client-side tests
+      ['**/client/**/*.spec.ts', 'jsdom'],
+      ['**/client/**/*.test.ts', 'jsdom'],
+    ],
+  },
+});

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

@@ -47,10 +47,11 @@
   "devDependencies": {
   "devDependencies": {
     "@types/express": "^4",
     "@types/express": "^4",
     "@types/hast": "^3.0.4",
     "@types/hast": "^3.0.4",
-    "axios": "^0.24.0",
-    "is-absolute-url": "^4.0.1",
+    "axios": "^1.11.0",
     "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"
   },
   },

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

@@ -0,0 +1,153 @@
+// tests for assuring axios request succeeds in version change
+
+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
+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(),
+      and: vi.fn().mockReturnThis(),
+      clone: vi.fn().mockReturnThis(),
+      count: vi.fn().mockResolvedValue(10),
+      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(),
+      and: vi.fn().mockReturnThis(),
+      clone: vi.fn().mockReturnThis(),
+      count: vi.fn().mockResolvedValue(10),
+      exec: vi.fn().mockResolvedValue([]),
+    }),
+    countDocuments: vi.fn().mockResolvedValue(0),
+    aggregate: vi.fn().mockResolvedValue([{ count: 5 }]),
+  }),
+}));
+
+const TEST_PORT = 3001;
+const TEST_SERVER_URL = `http://localhost:${TEST_PORT}`;
+
+describe('useSWRxLsx integration tests', () => {
+  let server: Server;
+  let app: express.Application;
+
+  // Helper function to setup axios spy
+  const setupAxiosSpy = () => {
+    const originalAxios = axios.create();
+    return vi.spyOn(axios, 'get').mockImplementation((url, config) => {
+      const fullUrl = url.startsWith('/_api')
+        ? `${TEST_SERVER_URL}${url}`
+        : url;
+      return originalAxios.get(fullUrl, config);
+    });
+  };
+
+  beforeAll(async () => {
+    // Create minimal Express app with just the LSX route
+    app = express();
+    app.use(express.json());
+    app.use(express.urlencoded({ extended: true }));
+
+    // Add CORS headers to prevent cross-origin issues
+    app.use((req, res, next) => {
+      res.header('Access-Control-Allow-Origin', '*');
+      res.header(
+        'Access-Control-Allow-Methods',
+        'GET, POST, PUT, DELETE, OPTIONS',
+      );
+      res.header(
+        'Access-Control-Allow-Headers',
+        'Origin, X-Requested-With, Content-Type, Accept, Authorization',
+      );
+      next();
+    });
+
+    // Mock minimal GROWI-like structure for the middleware
+    const mockCrowi = {
+      require: () => () => (req: any, res: any, next: any) => next(),
+      accessTokenParser: () => (req: any, res: any, next: any) => next(),
+    };
+
+    // Import and setup the LSX middleware
+    lsxMiddleware(mockCrowi, app);
+
+    // Start test server
+    return new Promise<void>((resolve) => {
+      server = app.listen(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 () => {
+    const axiosGetSpy = setupAxiosSpy();
+
+    const { result } = renderHook(() =>
+      useSWRxLsx('/test-page', { depth: '1' }, false),
+    );
+
+    await waitFor(() => expect(result.current.data).toBeDefined(), {
+      timeout: 5000,
+    });
+
+    expect(axiosGetSpy).toHaveBeenCalledWith(
+      '/_api/lsx',
+      expect.objectContaining({
+        params: expect.objectContaining({
+          pagePath: '/test-page',
+          options: expect.objectContaining({ depth: '1' }),
+        }),
+      }),
+    );
+
+    expect(result.current.data).toBeDefined();
+    expect(result.current.error).toBeUndefined();
+
+    axiosGetSpy.mockRestore();
+  });
+
+  it('should handle server validation errors properly', async () => {
+    const axiosGetSpy = setupAxiosSpy();
+
+    const { result } = renderHook(() => useSWRxLsx('', {}, false));
+
+    await waitFor(() => expect(result.current.error).toBeDefined(), {
+      timeout: 5000,
+    });
+
+    expect(result.current.error).toBeDefined();
+    expect(result.current.data).toBeUndefined();
+
+    axiosGetSpy.mockRestore();
+  });
+});

+ 2 - 1
packages/remark-lsx/src/server/index.ts

@@ -19,7 +19,8 @@ const lsxValidator = [
     .optional()
     .optional()
     .customSanitizer((options) => {
     .customSanitizer((options) => {
       try {
       try {
-        const jsonData: LsxApiOptions = JSON.parse(options);
+        const jsonData: LsxApiOptions =
+          typeof options === 'string' ? JSON.parse(options) : options;
 
 
         for (const key in jsonData) {
         for (const key in jsonData) {
           jsonData[key] = filterXSS.process(jsonData[key]);
           jsonData[key] = filterXSS.process(jsonData[key]);

+ 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'],
+    ],
   },
   },
 });
 });

+ 1 - 1
packages/slack/package.json

@@ -54,7 +54,7 @@
     "@types/bunyan": "^1.8.10",
     "@types/bunyan": "^1.8.10",
     "@types/http-errors": "^2.0.3",
     "@types/http-errors": "^2.0.3",
     "@types/url-join": "^4.0.2",
     "@types/url-join": "^4.0.2",
-    "axios": "^0.24.0",
+    "axios": "^1.11.0",
     "browser-bunyan": "^1.6.3",
     "browser-bunyan": "^1.6.3",
     "bunyan": "^1.8.15",
     "bunyan": "^1.8.15",
     "crypto": "^1.0.1",
     "crypto": "^1.0.1",

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 261 - 148
pnpm-lock.yaml


Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio