chore: remove GitHub Actions workflows, use Woodpecker CI exclusively

This commit is contained in:
张翔
2026-03-10 13:10:11 +08:00
parent 0a1adfc2a2
commit e8dffa4f05
82 changed files with 19565 additions and 101 deletions
+111
View File
@@ -0,0 +1,111 @@
import { describe, it, expect, beforeEach, jest } from '@jest/globals';
import { renderHook, act } from '@testing-library/react';
import { useFocusTrap } from './use-focus-trap';
describe('useFocusTrap', () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe('Initial State', () => {
it('should return a ref', () => {
const { result } = renderHook(() => useFocusTrap<HTMLDivElement>(false));
expect(result.current).toBeDefined();
expect(result.current.current).toBeNull();
});
});
describe('When Active', () => {
it('should store previous active element', () => {
const { result } = renderHook(() => useFocusTrap<HTMLDivElement>(true));
expect(result.current).toBeDefined();
});
it('should add keydown event listener', () => {
const addEventListenerSpy = jest.spyOn(document, 'addEventListener');
renderHook(() => useFocusTrap<HTMLDivElement>(true));
expect(addEventListenerSpy).toHaveBeenCalledWith('keydown', expect.any(Function));
});
it('should set body overflow to hidden', () => {
renderHook(() => useFocusTrap<HTMLDivElement>(true));
expect(document.body.style.overflow).toBe('hidden');
});
});
describe('When Inactive', () => {
it('should not add keydown event listener', () => {
const addEventListenerSpy = jest.spyOn(document, 'addEventListener');
renderHook(() => useFocusTrap<HTMLDivElement>(false));
expect(addEventListenerSpy).not.toHaveBeenCalled();
});
it('should not set body overflow', () => {
renderHook(() => useFocusTrap<HTMLDivElement>(false));
expect(document.body.style.overflow).toBe('unset');
});
});
describe('Cleanup', () => {
it('should remove keydown event listener on unmount', () => {
const removeEventListenerSpy = jest.spyOn(document, 'removeEventListener');
const { unmount } = renderHook(() => useFocusTrap<HTMLDivElement>(true));
unmount();
expect(removeEventListenerSpy).toHaveBeenCalledWith('keydown', expect.any(Function));
});
it('should restore body overflow on unmount', () => {
const { unmount } = renderHook(() => useFocusTrap<HTMLDivElement>(true));
unmount();
expect(document.body.style.overflow).toBe('unset');
});
});
describe('Tab Navigation', () => {
it('should handle Tab key press', () => {
const { result } = renderHook(() => useFocusTrap<HTMLDivElement>(true));
const mockEvent = new KeyboardEvent('keydown', { key: 'Tab' });
document.dispatchEvent(mockEvent);
expect(result.current).toBeDefined();
});
it('should handle Shift+Tab key press', () => {
const { result } = renderHook(() => useFocusTrap<HTMLDivElement>(true));
const mockEvent = new KeyboardEvent('keydown', { key: 'Tab', shiftKey: true });
document.dispatchEvent(mockEvent);
expect(result.current).toBeDefined();
});
});
describe('Escape Key', () => {
it('should handle Escape key press', () => {
const { result } = renderHook(() => useFocusTrap<HTMLDivElement>(true));
const mockEvent = new KeyboardEvent('keydown', { key: 'Escape' });
document.dispatchEvent(mockEvent);
expect(result.current).toBeDefined();
});
});
describe('State Changes', () => {
it('should handle activation change', () => {
const { result, rerender } = renderHook(
({ isActive }) => useFocusTrap<HTMLDivElement>(isActive),
{ initialProps: { isActive: false } }
);
expect(result.current).toBeDefined();
rerender({ isActive: true });
expect(document.body.style.overflow).toBe('hidden');
rerender({ isActive: false });
expect(document.body.style.overflow).toBe('unset');
});
});
});
@@ -0,0 +1,97 @@
import { describe, it, expect, beforeEach, jest } from '@jest/globals';
import { renderHook } from '@testing-library/react';
import { useIntersectionObserver } from './use-intersection-observer';
describe('useIntersectionObserver', () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe('Initial State', () => {
it('should return ref and isIntersecting state', () => {
const { result } = renderHook(() => useIntersectionObserver<HTMLDivElement>());
expect(result.current[0]).toBeDefined();
expect(typeof result.current[1]).toBe('boolean');
expect(result.current[1]).toBe(false);
});
it('should return ref with null current value', () => {
const { result } = renderHook(() => useIntersectionObserver<HTMLDivElement>());
expect(result.current[0].current).toBeNull();
});
});
describe('Options', () => {
it('should accept threshold option', () => {
const { result } = renderHook(() =>
useIntersectionObserver<HTMLDivElement>({ threshold: 0.5 })
);
expect(result.current[1]).toBe(false);
});
it('should accept root option', () => {
const { result } = renderHook(() =>
useIntersectionObserver<HTMLDivElement>({ root: null })
);
expect(result.current[1]).toBe(false);
});
it('should accept rootMargin option', () => {
const { result } = renderHook(() =>
useIntersectionObserver<HTMLDivElement>({ rootMargin: '10px' })
);
expect(result.current[1]).toBe(false);
});
it('should accept freezeOnceVisible option', () => {
const { result } = renderHook(() =>
useIntersectionObserver<HTMLDivElement>({ freezeOnceVisible: true })
);
expect(result.current[1]).toBe(false);
});
it('should use default options', () => {
const { result } = renderHook(() => useIntersectionObserver<HTMLDivElement>());
expect(result.current[1]).toBe(false);
});
it('should accept multiple thresholds', () => {
const { result } = renderHook(() =>
useIntersectionObserver<HTMLDivElement>({ threshold: [0, 0.25, 0.5, 0.75, 1] })
);
expect(result.current[1]).toBe(false);
});
});
describe('Behavior', () => {
it('should not observe when freezeOnceVisible is true and already intersecting', () => {
const { result, rerender } = renderHook(() =>
useIntersectionObserver<HTMLDivElement>({ freezeOnceVisible: true })
);
expect(result.current[1]).toBe(false);
rerender();
expect(result.current[1]).toBe(false);
});
});
describe('Cleanup', () => {
it('should cleanup observer on unmount', () => {
const { unmount } = renderHook(() => useIntersectionObserver<HTMLDivElement>());
unmount();
});
});
describe('Type Safety', () => {
it('should work with different element types', () => {
const { result: result1 } = renderHook(() => useIntersectionObserver<HTMLDivElement>());
const { result: result2 } = renderHook(() => useIntersectionObserver<HTMLSpanElement>());
const { result: result3 } = renderHook(() => useIntersectionObserver<HTMLElement>());
expect(result1.current[1]).toBe(false);
expect(result2.current[1]).toBe(false);
expect(result3.current[1]).toBe(false);
});
});
});
+74
View File
@@ -0,0 +1,74 @@
import { describe, it, expect, beforeEach, jest } from '@jest/globals';
import { renderHook } from '@testing-library/react';
import { useMediaQuery, useIsMobile, useIsTablet, useIsDesktop } from './use-media-query';
describe('useMediaQuery', () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe('useMediaQuery', () => {
it('should return false on server side', () => {
const { result } = renderHook(() => useMediaQuery('(min-width: 768px)'));
expect(result.current).toBe(false);
});
it('should return boolean value', () => {
const { result } = renderHook(() => useMediaQuery('(min-width: 768px)'));
expect(typeof result.current).toBe('boolean');
});
});
describe('useIsMobile', () => {
it('should return boolean value', () => {
const { result } = renderHook(() => useIsMobile());
expect(typeof result.current).toBe('boolean');
});
it('should use correct media query', () => {
const { result } = renderHook(() => useIsMobile());
expect(result.current).toBeDefined();
});
});
describe('useIsTablet', () => {
it('should return boolean value', () => {
const { result } = renderHook(() => useIsTablet());
expect(typeof result.current).toBe('boolean');
});
it('should use correct media query', () => {
const { result } = renderHook(() => useIsTablet());
expect(result.current).toBeDefined();
});
});
describe('useIsDesktop', () => {
it('should return boolean value', () => {
const { result } = renderHook(() => useIsDesktop());
expect(typeof result.current).toBe('boolean');
});
it('should use correct media query', () => {
const { result } = renderHook(() => useIsDesktop());
expect(result.current).toBeDefined();
});
});
describe('Media Query Behavior', () => {
it('should handle different queries', () => {
const { result: result1 } = renderHook(() => useMediaQuery('(min-width: 1024px)'));
const { result: result2 } = renderHook(() => useMediaQuery('(max-width: 767px)'));
expect(typeof result1.current).toBe('boolean');
expect(typeof result2.current).toBe('boolean');
});
it('should handle complex queries', () => {
const { result } = renderHook(() =>
useMediaQuery('(min-width: 768px) and (max-width: 1023px)')
);
expect(typeof result.current).toBe('boolean');
});
});
});
+95
View File
@@ -0,0 +1,95 @@
import { describe, it, expect, beforeEach, jest } from '@jest/globals';
import { renderHook } from '@testing-library/react';
import { useScrollReveal, useScrollProgress, useParallax } from './use-scroll-reveal';
describe('useScrollReveal', () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe('useScrollReveal', () => {
it('should return ref and isVisible state', () => {
const { result } = renderHook(() => useScrollReveal());
expect(result.current).toHaveProperty('ref');
expect(result.current).toHaveProperty('isVisible');
expect(result.current.isVisible).toBe(false);
});
it('should accept custom options', () => {
const { result } = renderHook(() =>
useScrollReveal({ threshold: 0.5, rootMargin: '10px', triggerOnce: false })
);
expect(result.current).toHaveProperty('ref');
expect(result.current).toHaveProperty('isVisible');
});
it('should use default options', () => {
const { result } = renderHook(() => useScrollReveal());
expect(result.current.isVisible).toBe(false);
});
});
describe('useScrollProgress', () => {
it('should return progress value', () => {
const { result } = renderHook(() => useScrollProgress());
expect(typeof result.current).toBe('number');
expect(result.current).toBeGreaterThanOrEqual(0);
expect(result.current).toBeLessThanOrEqual(1);
});
it('should accept threshold option', () => {
const { result } = renderHook(() => useScrollProgress({ threshold: 0.5 }));
expect(typeof result.current).toBe('number');
});
it('should use default threshold', () => {
const { result } = renderHook(() => useScrollProgress());
expect(result.current).toBeDefined();
});
});
describe('useParallax', () => {
it('should return ref and offset', () => {
const { result } = renderHook(() => useParallax());
expect(result.current).toHaveProperty('ref');
expect(result.current).toHaveProperty('offset');
expect(typeof result.current.offset).toBe('number');
});
it('should accept speed option', () => {
const { result } = renderHook(() => useParallax({ speed: 0.8 }));
expect(result.current).toHaveProperty('ref');
expect(result.current).toHaveProperty('offset');
});
it('should use default speed', () => {
const { result } = renderHook(() => useParallax());
expect(result.current.offset).toBe(0);
});
it('should handle different speed values', () => {
const { result: result1 } = renderHook(() => useParallax({ speed: 0 }));
const { result: result2 } = renderHook(() => useParallax({ speed: 1 }));
expect(result1.current.offset).toBeDefined();
expect(result2.current.offset).toBeDefined();
});
});
describe('Cleanup', () => {
it('should cleanup intersection observer on unmount', () => {
const { unmount } = renderHook(() => useScrollReveal());
unmount();
});
it('should cleanup scroll listener on unmount', () => {
const { unmount } = renderHook(() => useScrollProgress());
unmount();
});
it('should cleanup parallax scroll listener on unmount', () => {
const { unmount } = renderHook(() => useParallax());
unmount();
});
});
});