From c9cecd36b01e28ac770688d525f364e03ef17180 Mon Sep 17 00:00:00 2001 From: James Garbutt <43081j@users.noreply.github.com> Date: Thu, 26 Jun 2025 13:13:12 +0100 Subject: [PATCH] test: migrate debug tests to vitest spies Migrates the debug tests to use `vi.fn`. --- debug/test/browser/component-stack-2.test.js | 9 +- debug/test/browser/debug-compat.test.js | 11 +- debug/test/browser/debug-hooks.test.js | 9 +- debug/test/browser/debug-suspense.test.js | 19 +-- debug/test/browser/debug.options.test.js | 9 +- debug/test/browser/debug.test.js | 137 ++++++++++--------- debug/test/browser/fakeDevTools.js | 2 +- debug/test/browser/prop-types.test.js | 26 ++-- debug/test/browser/validateHookArgs.test.js | 13 +- 9 files changed, 123 insertions(+), 112 deletions(-) diff --git a/debug/test/browser/component-stack-2.test.js b/debug/test/browser/component-stack-2.test.js index a9a75af13f..0cf88d1a9c 100644 --- a/debug/test/browser/component-stack-2.test.js +++ b/debug/test/browser/component-stack-2.test.js @@ -1,6 +1,7 @@ import { createElement, render, Component } from 'preact'; import 'preact/debug'; import { setupScratch, teardown } from '../../../test/_util/helpers'; +import { vi } from 'vitest'; /** @jsx createElement */ @@ -19,13 +20,13 @@ describe('component stack', () => { errors = []; warnings = []; - sinon.stub(console, 'error').callsFake(e => errors.push(e)); - sinon.stub(console, 'warn').callsFake(w => warnings.push(w)); + vi.spyOn(console, 'error').mockImplementation(e => errors.push(e)); + vi.spyOn(console, 'warn').mockImplementation(w => warnings.push(w)); }); afterEach(() => { - console.error.restore(); - console.warn.restore(); + console.error.mockRestore(); + console.warn.mockRestore(); teardown(scratch); }); diff --git a/debug/test/browser/debug-compat.test.js b/debug/test/browser/debug-compat.test.js index d177406d99..244e61f9d2 100644 --- a/debug/test/browser/debug-compat.test.js +++ b/debug/test/browser/debug-compat.test.js @@ -7,6 +7,7 @@ import * as PropTypes from 'prop-types'; // eslint-disable-next-line no-duplicate-imports import { resetPropWarnings } from 'preact/debug'; import { forwardRef, createPortal } from 'preact/compat'; +import { vi } from 'vitest'; const h = createElement; /** @jsx createElement */ @@ -21,8 +22,8 @@ describe('debug compat', () => { errors = []; warnings = []; scratch = setupScratch(); - sinon.stub(console, 'error').callsFake(e => errors.push(e)); - sinon.stub(console, 'warn').callsFake(w => warnings.push(w)); + vi.spyOn(console, 'error').mockImplementation(e => errors.push(e)); + vi.spyOn(console, 'warn').mockImplementation(w => warnings.push(w)); root = document.createElement('div'); document.body.appendChild(root); @@ -30,8 +31,8 @@ describe('debug compat', () => { afterEach(() => { /** @type {*} */ - console.error.restore(); - console.warn.restore(); + console.error.mockRestore(); + console.warn.mockRestore(); teardown(scratch); document.body.removeChild(root); @@ -73,7 +74,7 @@ describe('debug compat', () => { render(, scratch); - expect(console.error).not.been.called; + expect(console.error).not.toHaveBeenCalled(); expect(ref.current).to.not.be.undefined; }); diff --git a/debug/test/browser/debug-hooks.test.js b/debug/test/browser/debug-hooks.test.js index 84bc0281ea..698bc430d0 100644 --- a/debug/test/browser/debug-hooks.test.js +++ b/debug/test/browser/debug-hooks.test.js @@ -3,6 +3,7 @@ import { useState, useEffect } from 'preact/hooks'; import 'preact/debug'; import { act } from 'preact/test-utils'; import { setupScratch, teardown } from '../../../test/_util/helpers'; +import { vi } from 'vitest'; /** @jsx createElement */ @@ -15,13 +16,13 @@ describe('debug with hooks', () => { errors = []; warnings = []; scratch = setupScratch(); - sinon.stub(console, 'error').callsFake(e => errors.push(e)); - sinon.stub(console, 'warn').callsFake(w => warnings.push(w)); + vi.spyOn(console, 'error').mockImplementation(e => errors.push(e)); + vi.spyOn(console, 'warn').mockImplementation(w => warnings.push(w)); }); afterEach(() => { - console.error.restore(); - console.warn.restore(); + console.error.mockRestore(); + console.warn.mockRestore(); teardown(scratch); }); diff --git a/debug/test/browser/debug-suspense.test.js b/debug/test/browser/debug-suspense.test.js index a436154fdf..2368beb482 100644 --- a/debug/test/browser/debug-suspense.test.js +++ b/debug/test/browser/debug-suspense.test.js @@ -6,6 +6,7 @@ import { teardown, serializeHtml } from '../../../test/_util/helpers'; +import { vi } from 'vitest'; /** @jsx createElement */ @@ -21,13 +22,13 @@ describe('debug with suspense', () => { warnings = []; scratch = setupScratch(); rerender = setupRerender(); - sinon.stub(console, 'error').callsFake(e => errors.push(e)); - sinon.stub(console, 'warn').callsFake(w => warnings.push(w)); + vi.spyOn(console, 'error').mockImplementation(e => errors.push(e)); + vi.spyOn(console, 'warn').mockImplementation(w => warnings.push(w)); }); afterEach(() => { - console.error.restore(); - console.warn.restore(); + console.error.mockRestore(); + console.warn.mockRestore(); teardown(scratch); }); @@ -74,7 +75,7 @@ describe('debug with suspense', () => { render(suspense, scratch); rerender(); // render fallback - expect(console.error).to.not.be.called; + expect(console.error).not.toHaveBeenCalled(); expect(serializeHtml(scratch)).to.equal('
fallback...
'); return loader.then(() => { @@ -106,7 +107,7 @@ describe('debug with suspense', () => { return loader.then(() => { rerender(); - expect(console.warn).to.be.calledTwice; + expect(console.warn).toHaveBeenCalledTimes(2); expect(warnings[1].includes('MyLazyLoaded')).to.equal(true); expect(serializeHtml(scratch)).to.equal('
Hi there
'); }); @@ -132,7 +133,7 @@ describe('debug with suspense', () => { return loader.then(() => { rerender(); - expect(console.warn).to.be.calledTwice; + expect(console.warn).toHaveBeenCalledTimes(2); expect(warnings[1].includes('HelloLazy')).to.equal(true); expect(serializeHtml(scratch)).to.equal('
Hi there
'); }); @@ -160,7 +161,7 @@ describe('debug with suspense', () => { } // Called once on initial render, and again when promise rejects - expect(console.warn).to.be.calledTwice; + expect(console.warn).toHaveBeenCalledTimes(2); }); }); @@ -181,7 +182,7 @@ describe('debug with suspense', () => { error = e; } - expect(console.warn).to.be.calledOnce; + expect(console.warn).toHaveBeenCalledOnce(); expect(error).not.to.be.undefined; expect(error.message).to.eql('Hello'); }); diff --git a/debug/test/browser/debug.options.test.js b/debug/test/browser/debug.options.test.js index e49e46cccf..6c8f9eed9f 100644 --- a/debug/test/browser/debug.options.test.js +++ b/debug/test/browser/debug.options.test.js @@ -12,6 +12,7 @@ import { useState } from 'preact/hooks'; import { setupRerender } from 'preact/test-utils'; import 'preact/debug'; import { setupScratch, teardown } from '../../../test/_util/helpers'; +import { vi } from 'vitest'; /** @jsx createElement */ @@ -25,7 +26,7 @@ describe('debug options', () => { /** @type {(count: number) => void} */ let setCount; - /** @type {import('sinon').SinonFakeTimers | undefined} */ + /** @type {import('vitest').VitestUtils | undefined} */ let clock; beforeEach(() => { @@ -42,7 +43,7 @@ describe('debug options', () => { afterEach(() => { teardown(scratch); - if (clock) clock.restore(); + if (clock) vi.useRealTimers(); }); class ClassApp extends Component { @@ -123,7 +124,7 @@ describe('debug options', () => { } } - clock = sinon.useFakeTimers(); + clock = vi.useFakeTimers(); render(, scratch); rerender(); @@ -132,6 +133,6 @@ describe('debug options', () => { // we expect to throw after setTimeout to trigger a window.onerror // this is to ensure react compat (i.e. with next.js' dev overlay) - expect(() => clock.tick(0)).to.throw(e); + expect(() => clock.advanceTimersByTime(0)).to.throw(e); }); }); diff --git a/debug/test/browser/debug.test.js b/debug/test/browser/debug.test.js index 1f777ba35d..f70434ec93 100644 --- a/debug/test/browser/debug.test.js +++ b/debug/test/browser/debug.test.js @@ -11,6 +11,7 @@ import { setupScratch, teardown } from '../../../test/_util/helpers'; import './fakeDevTools'; import 'preact/debug'; import { setupRerender } from 'preact/test-utils'; +import { vi } from 'vitest'; const h = createElement; /** @jsx createElement */ @@ -27,19 +28,19 @@ describe('debug', () => { warnings = []; scratch = setupScratch(); rerender = setupRerender(); - sinon.stub(console, 'error').callsFake(e => errors.push(e)); - sinon.stub(console, 'warn').callsFake(w => warnings.push(w)); + vi.spyOn(console, 'error').mockImplementation(e => errors.push(e)); + vi.spyOn(console, 'warn').mockImplementation(w => warnings.push(w)); }); afterEach(() => { /** @type {*} */ - console.error.restore(); - console.warn.restore(); + console.error.mockRestore(); + console.warn.mockRestore(); teardown(scratch); }); it('should initialize devtools', () => { - expect(window.__PREACT_DEVTOOLS__.attachPreact).to.have.been.called; + expect(window.__PREACT_DEVTOOLS__.attachPreact).toHaveBeenCalled(); }); it('should print an error on rendering on undefined parent', () => { @@ -128,37 +129,37 @@ describe('debug', () => { const res = []; res.push(vnode); res.push(vnode.attributes); - expect(console.warn).to.be.calledOnce; - expect(console.warn.args[0]).to.match(/use vnode.props/); + expect(console.warn).toHaveBeenCalledOnce(); + expect(console.warn.mock.calls[0]).to.match(/use vnode.props/); res.push(vnode.nodeName); - expect(console.warn).to.be.calledTwice; - expect(console.warn.args[1]).to.match(/use vnode.type/); + expect(console.warn).toHaveBeenCalledTimes(2); + expect(console.warn.mock.calls[1]).to.match(/use vnode.type/); res.push(vnode.children); - expect(console.warn).to.be.calledThrice; - expect(console.warn.args[2]).to.match(/use vnode.props.children/); + expect(console.warn).toHaveBeenCalledTimes(3); + expect(console.warn.mock.calls[2]).to.match(/use vnode.props.children/); // Should only warn once res.push(vnode.attributes); - expect(console.warn).to.be.calledThrice; + expect(console.warn).toHaveBeenCalledTimes(3); res.push(vnode.nodeName); - expect(console.warn).to.be.calledThrice; + expect(console.warn).toHaveBeenCalledTimes(3); res.push(vnode.children); - expect(console.warn).to.be.calledThrice; + expect(console.warn).toHaveBeenCalledTimes(3); vnode.attributes = {}; - expect(console.warn.args[3]).to.match(/use vnode.props/); + expect(console.warn.mock.calls[3]).to.match(/use vnode.props/); vnode.nodeName = ''; - expect(console.warn.args[4]).to.match(/use vnode.type/); + expect(console.warn.mock.calls[4]).to.match(/use vnode.type/); vnode.children = []; - expect(console.warn.args[5]).to.match(/use vnode.props.children/); + expect(console.warn.mock.calls[5]).to.match(/use vnode.props.children/); // Should only warn once vnode.attributes = {}; - expect(console.warn.args.length).to.equal(6); + expect(console.warn.mock.calls.length).to.equal(6); vnode.nodeName = ''; - expect(console.warn.args.length).to.equal(6); + expect(console.warn.mock.calls.length).to.equal(6); vnode.children = []; - expect(console.warn.args.length).to.equal(6); + expect(console.warn.mock.calls.length).to.equal(6); // Mark res as used, otherwise it will be dead code eliminated expect(res.length).to.equal(7); @@ -176,8 +177,8 @@ describe('debug', () => { } render(, scratch); - expect(console.warn).to.be.calledOnce; - expect(console.warn.args[0]).to.match(/no-op/); + expect(console.warn).toHaveBeenCalledOnce(); + expect(console.warn.mock.calls[0]).to.match(/no-op/); }); it('should NOT warn when calling setState inside the cWM', () => { @@ -191,7 +192,7 @@ describe('debug', () => { } render(, scratch); - expect(console.warn).to.not.be.called; + expect(console.warn).not.toHaveBeenCalled(); }); it('should warn when calling forceUpdate inside the constructor', () => { @@ -206,8 +207,8 @@ describe('debug', () => { } render(, scratch); - expect(console.warn).to.be.calledOnce; - expect(console.warn.args[0]).to.match(/no-op/); + expect(console.warn).toHaveBeenCalledOnce(); + expect(console.warn.mock.calls[0]).to.match(/no-op/); }); it('should warn when calling forceUpdate on an unmounted Component', () => { @@ -225,13 +226,13 @@ describe('debug', () => { render(, scratch); forceUpdate(); - expect(console.warn).to.not.be.called; + expect(console.warn).not.toHaveBeenCalled(); render(null, scratch); forceUpdate(); - expect(console.warn).to.be.calledOnce; - expect(console.warn.args[0]).to.match(/no-op/); + expect(console.warn).toHaveBeenCalledOnce(); + expect(console.warn.mock.calls[0]).to.match(/no-op/); }); it('should print an error when child is a plain object', () => { @@ -271,7 +272,7 @@ describe('debug', () => { let ref = createRef(); render(
, scratch); - expect(console.error).to.not.be.called; + expect(console.error).not.toHaveBeenCalled(); }); it('throws an error if a component rerenders too many times', () => { @@ -333,7 +334,7 @@ describe('debug', () => {
, scratch ); - expect(console.error).to.be.calledOnce; + expect(console.error).toHaveBeenCalledOnce(); }); it('should allow distinct object keys', () => { @@ -346,7 +347,7 @@ describe('debug', () => { , scratch ); - expect(console.error).not.to.be.called; + expect(console.error).not.toHaveBeenCalled(); }); it('should print an error for duplicate object keys', () => { @@ -358,7 +359,7 @@ describe('debug', () => { , scratch ); - expect(console.error).to.be.calledOnce; + expect(console.error).toHaveBeenCalledOnce(); }); it('should print an error on duplicate keys with Components', () => { @@ -374,7 +375,7 @@ describe('debug', () => { } render(, scratch); - expect(console.error).to.be.calledOnce; + expect(console.error).toHaveBeenCalledOnce(); }); it('should print an error on duplicate keys with Fragments', () => { @@ -398,7 +399,7 @@ describe('debug', () => { } render(, scratch); - expect(console.error).to.be.calledTwice; + expect(console.error).toHaveBeenCalledTimes(2); }); }); @@ -412,7 +413,7 @@ describe('debug', () => { ); render(, scratch); - expect(console.error).to.be.calledOnce; + expect(console.error).toHaveBeenCalledOnce(); }); it('missing
with ', () => { @@ -426,7 +427,7 @@ describe('debug', () => { ); render(
, scratch); - expect(console.error).to.be.calledOnce; + expect(console.error).toHaveBeenCalledOnce(); }); it('missing
with ', () => { @@ -440,7 +441,7 @@ describe('debug', () => { ); render(
, scratch); - expect(console.error).to.be.calledOnce; + expect(console.error).toHaveBeenCalledOnce(); }); it('missing
with ', () => { @@ -454,7 +455,7 @@ describe('debug', () => { ); render(
, scratch); - expect(console.error).to.be.calledOnce; + expect(console.error).toHaveBeenCalledOnce(); }); it('missing ', () => { @@ -466,7 +467,7 @@ describe('debug', () => {
); render(, scratch); - expect(console.error).to.be.calledOnce; + expect(console.error).toHaveBeenCalledOnce(); }); it('missing with td component', () => { @@ -479,7 +480,7 @@ describe('debug', () => {
); render(, scratch); - expect(console.error).to.be.calledOnce; + expect(console.error).toHaveBeenCalledOnce(); }); it('missing with th component', () => { @@ -492,7 +493,7 @@ describe('debug', () => {
); render(, scratch); - expect(console.error).to.be.calledOnce; + expect(console.error).toHaveBeenCalledOnce(); }); it('Should accept ', () => { @@ -506,7 +507,7 @@ describe('debug', () => {
instead of in
); render(, scratch); - expect(console.error).to.not.be.called; + expect(console.error).not.toHaveBeenCalled(); }); it('Accepts well formed table with TD components', () => { @@ -531,7 +532,7 @@ describe('debug', () => {
); render(, scratch); - expect(console.error).to.not.be.called; + expect(console.error).not.toHaveBeenCalled(); }); it('Accepts well formed table', () => { @@ -555,7 +556,7 @@ describe('debug', () => {
); render(, scratch); - expect(console.error).to.not.be.called; + expect(console.error).not.toHaveBeenCalled(); }); it('Accepts minimal well formed table', () => { @@ -572,7 +573,7 @@ describe('debug', () => {
); render(, scratch); - expect(console.error).to.not.be.called; + expect(console.error).not.toHaveBeenCalled(); }); it('should include DOM parents outside of root node', () => { @@ -585,7 +586,7 @@ describe('debug', () => { const table = document.createElement('table'); scratch.appendChild(table); render(
, table); - expect(console.error).to.not.be.called; + expect(console.error).not.toHaveBeenCalled(); }); it('should warn for improper nested table', () => { @@ -600,7 +601,7 @@ describe('debug', () => { ); render(
, scratch); - expect(console.error).to.be.calledOnce; + expect(console.error).toHaveBeenCalledOnce(); }); it('accepts valid nested tables', () => { @@ -631,7 +632,7 @@ describe('debug', () => { ); render(
, scratch); - expect(console.error).to.not.be.called; + expect(console.error).not.toHaveBeenCalled(); }); }); @@ -640,14 +641,14 @@ describe('debug', () => { const Paragraph = () =>

Hello world

; render(, scratch); - expect(console.error).to.not.be.called; + expect(console.error).not.toHaveBeenCalled(); }); it('should not crash for an empty pragraph', () => { const Paragraph = () =>

; render(, scratch); - expect(console.error).to.not.be.called; + expect(console.error).not.toHaveBeenCalled(); }); it('should warn for nesting illegal dom-nodes under a paragraph', () => { @@ -658,7 +659,7 @@ describe('debug', () => { ); render(, scratch); - expect(console.error).to.be.calledOnce; + expect(console.error).toHaveBeenCalledOnce(); }); it('should warn for nesting illegal dom-nodes under a paragraph with a parent', () => { @@ -671,7 +672,7 @@ describe('debug', () => { ); render(, scratch); - expect(console.error).to.be.calledOnce; + expect(console.error).toHaveBeenCalledOnce(); }); it('should warn for nesting illegal dom-nodes under a paragraph as func', () => { @@ -683,7 +684,7 @@ describe('debug', () => { ); render(, scratch); - expect(console.error).to.be.calledOnce; + expect(console.error).toHaveBeenCalledOnce(); }); it('should not warn for nesting span under a paragraph', () => { @@ -694,7 +695,7 @@ describe('debug', () => { ); render(, scratch); - expect(console.error).to.not.be.called; + expect(console.error).not.toHaveBeenCalled(); }); }); @@ -703,7 +704,7 @@ describe('debug', () => { const Button = () => ; render(