import { JSDOM } from 'jsdom' import React from 'react' import {render, waitFor, within, fireEvent} from '@testing-library/react' import {userEvent} from '@testing-library/user-event' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { createBrowserRouter, RouterProvider } from 'react-router' import { assertEquals, assertExists, assertNotEquals, assertRejects, assert, } from "https://deno.land/std@0.217.0/assert/mod.ts"; import axios from 'axios'; import { KnowInfoPage } from "./pages_know_info.tsx" import { AuthProvider } from './hooks_sys.tsx' import { ProtectedRoute } from './components_protected_route.tsx' // 拦截React DOM中的attachEvent和detachEvent错误 const originalError = console.error; console.error = (...args) => { // 过滤掉attachEvent和detachEvent相关的错误 if (args[0] instanceof Error) { if (args[0].message?.includes('attachEvent is not a function') || args[0].message?.includes('detachEvent is not a function')) { return; // 不输出这些错误 } } else if (typeof args[0] === 'string') { if (args[0].includes('attachEvent is not a function') || args[0].includes('detachEvent is not a function')) { return; // 不输出这些错误 } } originalError(...args); }; // 应用入口组件 const App = () => { // 路由配置 const router = createBrowserRouter([ { path: '/', element: ( ) }, ]); return }; // setup function function setup() { const dom = new JSDOM(``, { runScripts: "dangerously", pretendToBeVisual: true, url: "http://localhost", }); // 模拟浏览器环境 globalThis.window = dom.window; globalThis.document = dom.window.document; // 添加必要的 DOM 配置 globalThis.Node = dom.window.Node; globalThis.Document = dom.window.Document; globalThis.HTMLInputElement = dom.window.HTMLInputElement; globalThis.HTMLButtonElement = dom.window.HTMLButtonElement; // 定义浏览器环境所需的类 globalThis.Element = dom.window.Element; globalThis.HTMLElement = dom.window.HTMLElement; globalThis.ShadowRoot = dom.window.ShadowRoot; globalThis.SVGElement = dom.window.SVGElement; // 模拟 getComputedStyle globalThis.getComputedStyle = (elt) => { const style = new dom.window.CSSStyleDeclaration(); style.getPropertyValue = () => ''; return style; }; // 模拟matchMedia函数 globalThis.matchMedia = (query) => ({ matches: query.includes('max-width'), media: query, onchange: null, addListener: () => {}, removeListener: () => {}, addEventListener: () => {}, removeEventListener: () => {}, dispatchEvent: () => false, }); // 模拟动画相关API globalThis.AnimationEvent = globalThis.AnimationEvent || dom.window.Event; globalThis.TransitionEvent = globalThis.TransitionEvent || dom.window.Event; // 模拟requestAnimationFrame globalThis.requestAnimationFrame = globalThis.requestAnimationFrame || ((cb) => setTimeout(cb, 0)); globalThis.cancelAnimationFrame = globalThis.cancelAnimationFrame || clearTimeout; // 设置浏览器尺寸相关方法 window.resizeTo = (width, height) => { window.innerWidth = width || window.innerWidth; window.innerHeight = height || window.innerHeight; window.dispatchEvent(new Event('resize')); }; window.scrollTo = () => {}; const customScreen = within(document.body); const user = userEvent.setup({ document: dom.window.document, delay: 10, skipAutoClose: true, }); localStorage.setItem('token', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwidXNlcm5hbWUiOiJhZG1pbiIsInNlc3Npb25JZCI6Ijk4T2lzTW5SMm0zQ0dtNmo4SVZrNyIsInJvbGVJbmZvIjpudWxsLCJpYXQiOjE3NDQzNjIzNTUsImV4cCI6MTc0NDQ0ODc1NX0.k1Ld7qWAZmdzsbjmrl_0ec1FqF_GimaOuQIic4znRtc'); axios.defaults.baseURL = 'https://23957.dev.d8dcloud.com' const queryClient = new QueryClient() return { user, // Import `render` from the framework library of your choice. // See https://testing-library.com/docs/dom-testing-library/install#wrappers ...render( ), } } // // 使用异步测试处理组件渲染 // Deno.test({ // name: '知识库管理页面基础测试', // fn: async (t) => { // // 存储所有需要清理的定时器 // const timers: number[] = []; // const originalSetTimeout = globalThis.setTimeout; // const originalSetInterval = globalThis.setInterval; // // 重写定时器方法以跟踪所有创建的定时器 // globalThis.setTimeout = ((callback, delay, ...args) => { // const id = originalSetTimeout(callback, delay, ...args); // timers.push(id); // return id; // }) as typeof setTimeout; // globalThis.setInterval = ((callback, delay, ...args) => { // const id = originalSetInterval(callback, delay, ...args); // timers.push(id); // return id; // }) as typeof setInterval; // // 清理函数 // const cleanup = () => { // for (const id of timers) { // clearTimeout(id); // clearInterval(id); // } // // 恢复原始定时器方法 // globalThis.setTimeout = originalSetTimeout; // globalThis.setInterval = originalSetInterval; // }; // try { // // // 渲染组件 // // const { // // findByText, findByPlaceholderText, queryByText, // // findByRole, findAllByRole, findByLabelText, findAllByText, debug, // // queryByRole // // } = render( // // // // // // // // // // // // ); // // 测试1: 基本渲染 // await t.step('应正确渲染页面元素', async () => { // const { findByText } = setup() // await waitFor(async () => { // const title = await findByText(/知识库管理/i); // assertExists(title, '未找到知识库管理标题'); // }, { // timeout: 1000 * 5, // }); // }); // // 初始加载表格数据 // await t.step('初始加载表格数据', async () => { // const { findByRole } = setup() // await waitFor(async () => { // const table = await findByRole('table'); // const rows = await within(table).findAllByRole('row'); // // 应该大于2行 // assert(rows.length > 2, '表格没有数据'); // 1是表头行 2是数据行 // }, { // timeout: 1000 * 5, // }); // }); // // 测试2: 搜索表单功能 // await t.step('搜索表单应正常工作', async () => { // const {findByPlaceholderText, findByText, findByRole, user} = setup() // // 等待知识库管理标题出现 // await waitFor(async () => { // const title = await findByText(/知识库管理/i); // assertExists(title, '未找到知识库管理标题'); // }, { // timeout: 1000 * 5, // }); // // 直接查找标题搜索输入框和搜索按钮 // const searchInput = await findByPlaceholderText(/请输入文章标题/i) as HTMLInputElement; // const searchButton = await findByText(/搜 索/i); // assertExists(searchInput, '未找到搜索输入框'); // assertExists(searchButton, '未找到搜索按钮'); // // 输入搜索内容 // await user.type(searchInput, '数据分析') // assertEquals(searchInput.value, '数据分析', '搜索输入框值未更新'); // // 提交搜索 // await user.click(searchButton); // let rows: HTMLElement[] = []; // const table = await findByRole('table'); // assertExists(table, '未找到数据表格'); // // 等待表格刷新并验证 // await waitFor(async () => { // rows = await within(table).findAllByRole('row'); // assert(rows.length === 2, '表格未刷新'); // }, { // timeout: 1000 * 5, // onTimeout: () => new Error('等待表格刷新超时') // }); // // 等待搜索结果并验证 // await waitFor(async () => { // rows = await within(table).findAllByRole('row'); // assert(rows.length > 2, '表格没有数据'); // }, { // timeout: 1000 * 5, // onTimeout: () => new Error('等待搜索结果超时') // }); // // 检查至少有一行包含"数据分析" // const matchResults = await Promise.all(rows.map(async row => { // try{ // const cells = await within(row).findAllByRole('cell'); // return cells.some(cell => { // return cell.textContent?.includes('数据分析') // }); // } catch (error: unknown) { // // console.error('搜索结果获取失败', error) // return false // } // })) // // console.log('matchResults', matchResults) // const hasMatch = matchResults.some(result => result); // assert(hasMatch, '搜索结果中没有找到包含"数据分析"的文章'); // }); // // 测试3: 表格数据加载 // await t.step('表格应加载并显示数据', async () => { // const {findByRole, queryByText} = setup() // // 等待数据加载完成或表格出现,最多等待5秒 // await waitFor(async () => { // // 检查加载状态是否消失 // const loading = queryByText(/正在加载数据/i); // if (loading) { // throw new Error('数据仍在加载中'); // } // // 检查表格是否出现 // const table = await findByRole('table'); // assertExists(table, '未找到数据表格'); // // 检查表格是否有数据行 // const rows = await within(table).findAllByRole('row'); // assertNotEquals(rows.length, 1, '表格没有数据行'); // 1是表头行 // }, { // timeout: 5000, // 5秒超时 // onTimeout: (error) => { // return new Error(`数据加载超时: ${error.message}`); // } // }); // }); // // 测试4: 添加文章功能 // await t.step('应能打开添加文章模态框', async () => { // const {findByText, findByRole, user} = setup() // // 等待知识库管理标题出现 // await waitFor(async () => { // const title = await findByText(/知识库管理/i); // assertExists(title, '未找到知识库管理标题'); // }, { // timeout: 1000 * 5, // }); // const addButton = await findByText(/添加文章/i); // assertExists(addButton, '未找到添加文章按钮'); // await user.click(addButton); // // 找到模态框 // const modal = await findByRole('dialog'); // assertExists(modal, '未找到模态框'); // const modalTitle = await within(modal).findByText(/添加知识库文章/i); // assertExists(modalTitle, '未找到添加文章模态框'); // // 验证表单字段 // const titleInput = await within(modal).findByPlaceholderText(/请输入文章标题/i); // assertExists(titleInput, '未找到标题输入框'); // const contentInput = await within(modal).findByPlaceholderText(/请输入文章内容/i); // assertExists(contentInput, '未找到文章内容输入框'); // // 取消 // const cancelButton = await within(modal).findByText(/取 消/i); // assertExists(cancelButton, '未找到取消按钮'); // await user.click(cancelButton); // // 验证模态框是否关闭 // await waitFor(async () => { // const modalTitle = await within(modal).findByText(/添加知识库文章/i); // assertExists(!modalTitle, '模态框未关闭'); // }, { // timeout: 1000 * 5, // onTimeout: () => new Error('等待模态框关闭超时') // }); // }); // } finally { // // 确保清理所有定时器 // cleanup(); // } // }, // sanitizeOps: false, // 禁用操作清理检查 // sanitizeResources: false, // 禁用资源清理检查 // }); Deno.test({ name: '知识库管理页面新增测试', fn: async (t) => { // 存储所有需要清理的定时器 const timers: number[] = []; const originalSetTimeout = globalThis.setTimeout; const originalSetInterval = globalThis.setInterval; // 重写定时器方法以跟踪所有创建的定时器 globalThis.setTimeout = ((callback, delay, ...args) => { const id = originalSetTimeout(callback, delay, ...args); timers.push(id); return id; }) as typeof setTimeout; globalThis.setInterval = ((callback, delay, ...args) => { const id = originalSetInterval(callback, delay, ...args); timers.push(id); return id; }) as typeof setInterval; // 清理函数 const cleanup = () => { for (const id of timers) { clearTimeout(id); clearInterval(id); } // 恢复原始定时器方法 globalThis.setTimeout = originalSetTimeout; globalThis.setInterval = originalSetInterval; }; try { // // 渲染组件 // const { // findByText, findByPlaceholderText, queryByText, // findByRole, findAllByRole, findByLabelText, findAllByText, debug, // queryByRole // } = render( // // // // // // ); // 测试5: 完整添加文章流程 await t.step('应能完整添加一篇文章', async () => { const {findByText, findByRole, debug, user} = setup() // 等待知识库管理标题出现 await waitFor(async () => { const title = await findByText(/知识库管理/i); assertExists(title, '未找到知识库管理标题'); }, { timeout: 1000 * 5, }); // 打开添加模态框 const addButton = await findByText(/添加文章/i); assertExists(addButton, '未找到添加文章按钮'); await user.click(addButton); // 找到模态框 const modal = await findByRole('dialog'); assertExists(modal, '未找到模态框'); const modalTitle = await within(modal).findByText(/添加知识库文章/i); assertExists(modalTitle, '未找到添加文章模态框'); // 确保上一个测试的输入框值不会影响这个测试 // 在模态框内查找标题和内容输入框 const titleInput = await within(modal).findByPlaceholderText(/请输入文章标题/i) as HTMLInputElement; const contentInput = await within(modal).findByPlaceholderText(/请输入文章内容/i) as HTMLTextAreaElement; const submitButton = await within(modal).findByText(/确 定/i); assertExists(titleInput, '未找到模态框中的标题输入框'); assertExists(contentInput, '未找到文章内容输入框'); assertExists(submitButton, '未找到提交按钮'); // 先清空输入框,以防止之前的测试影响 // await user.clear(titleInput); // await user.clear(contentInput); // 输入新值 await user.type(titleInput, '测试文章标题'); await user.type(contentInput, '这是测试文章内容'); debug(titleInput); debug(contentInput); debug(submitButton); // 提交表单 await user.click(submitButton); // 验证表单字段 assertEquals(titleInput.value, '测试文章标题', '模态框中标题输入框值未更新'); assertEquals(contentInput.value, '这是测试文章内容', '内容输入框值未更新'); let rows: HTMLElement[] = []; const table = await findByRole('table'); assertExists(table, '未找到数据表格'); // 等待表格刷新并验证 await waitFor(async () => { rows = await within(table).findAllByRole('row'); assert(rows.length === 2, '表格未刷新'); }, { timeout: 1000 * 5, onTimeout: () => new Error('等待表格刷新超时') }); // 等待搜索结果并验证 await waitFor(async () => { rows = await within(table).findAllByRole('row'); assert(rows.length > 2, '表格没有数据'); }, { timeout: 1000 * 5, onTimeout: () => new Error('等待搜索结果超时') }); // 检查至少有一行包含"测试文章标题" const matchResults = await Promise.all(rows.map(async row => { try{ const cells = await within(row).findAllByRole('cell'); return cells.some(cell => { return cell.textContent?.includes('测试文章标题') }); } catch (error: unknown) { // console.error('搜索结果获取失败', error) return false } })) // console.log('matchResults', matchResults) const hasMatch = matchResults.some(result => result); assert(hasMatch, '搜索结果中没有找到包含"测试文章标题"的文章'); }); // // 测试5: 分页功能 // await t.step('应显示分页控件', async () => { // const pagination = await findByRole('navigation'); // assertExists(pagination, '未找到分页控件'); // const pageItems = await findAllByRole('button', { name: /1|2|3|下一页|上一页/i }); // assertNotEquals(pageItems.length, 0, '未找到分页按钮'); // }); // // 测试6: 操作按钮 // await t.step('应显示操作按钮', async () => { // const editButtons = await findAllByText(/编辑/i); // assertNotEquals(editButtons.length, 0, '未找到编辑按钮'); // const deleteButtons = await findAllByText(/删除/i); // assertNotEquals(deleteButtons.length, 0, '未找到删除按钮'); // }); } finally { // 确保清理所有定时器 cleanup(); } }, sanitizeOps: false, // 禁用操作清理检查 sanitizeResources: false, // 禁用资源清理检查 });