Update failing tests for Jest v29

- Use a console.error spy to avoid the console error bubbling into logs for FieldBlock.test.js (also allows us to confirm an error was thrown)
- Adopt proper approach to 'flush promises' now that API is available for JSDom usage of microtasks/animation frame
- Adapt to new Jest timer mocking approach, use legacy timers in one file as there is not a suitable setTimeout (with zero delay) while also using async await
pull/10650/head
LB Johnston 2023-06-24 21:50:42 +10:00 zatwierdzone przez Thibaud Colas
rodzic f90e03ab35
commit f80e64989e
7 zmienionych plików z 46 dodań i 44 usunięć

Wyświetl plik

@ -22,7 +22,7 @@ class DummyWidgetDefinition {
render(placeholder, name, id, initialState) {
if (this.throwErrorOnRender) {
throw new Error();
throw new Error('Mock rendering error');
}
const widgetName = this.widgetName;
@ -190,6 +190,9 @@ describe('telepath: wagtail.blocks.FieldBlock catches widget render errors', ()
let boundBlock;
beforeEach(() => {
// mock console.error to ensure it does not bubble to the logs
jest.spyOn(console, 'error').mockImplementation(() => {});
// Create mocks for callbacks
constructor = jest.fn();
setState = jest.fn();
@ -212,6 +215,7 @@ describe('telepath: wagtail.blocks.FieldBlock catches widget render errors', ()
// Render it
document.body.innerHTML = '<div id="placeholder"></div>';
boundBlock = blockDef.render(
$('#placeholder'),
'the-prefix',
@ -219,7 +223,16 @@ describe('telepath: wagtail.blocks.FieldBlock catches widget render errors', ()
);
});
afterEach(() => {
/* eslint-disable no-console */
console.error.mockRestore();
});
test('it renders correctly', () => {
expect(console.error).toHaveBeenCalledTimes(1);
expect(console.error).toHaveBeenCalledWith(
new Error('Mock rendering error'),
);
expect(document.body.innerHTML).toMatchSnapshot();
});
});

Wyświetl plik

@ -1,9 +1,7 @@
import { Application } from '@hotwired/stimulus';
import { ProgressController } from './ProgressController';
jest.useFakeTimers();
const flushPromises = () => new Promise(setImmediate);
jest.useFakeTimers({ legacyFakeTimers: true });
describe('ProgressController', () => {
// form submit is not implemented in jsdom
@ -37,7 +35,7 @@ describe('ProgressController', () => {
jest.clearAllTimers();
});
it('should not change the text of the button to Loading if the form is not valid', async () => {
it('should not change the text of the button to Loading if the form is not valid', () => {
const form = document.querySelector('form');
const button = document.querySelector('.button-longrunning');
expect(mockSubmit).not.toHaveBeenCalled();
@ -46,23 +44,15 @@ describe('ProgressController', () => {
form.checkValidity = jest.fn().mockReturnValue(false);
const onClick = jest.fn();
button.addEventListener('click', onClick);
button.dispatchEvent(new CustomEvent('click'));
jest.advanceTimersByTime(10);
await flushPromises();
expect(mockSubmit).not.toHaveBeenCalled();
expect(setTimeout).not.toHaveBeenCalled();
expect(button.disabled).toEqual(false);
expect(onClick).toHaveBeenCalledTimes(1);
jest.runAllTimers();
await flushPromises();
expect(mockSubmit).not.toHaveBeenCalled();
});
it('should trigger a timeout based on the value attribute', () => {
const form = document.querySelector('form');
const button = document.querySelector('.button-longrunning');
jest.spyOn(global, 'setTimeout');
@ -91,14 +81,14 @@ describe('ProgressController', () => {
button.click();
jest.advanceTimersByTime(10);
await flushPromises();
await new Promise(queueMicrotask);
expect(label.textContent).toBe('Loading');
expect(button.getAttribute('disabled')).toEqual('');
expect(button.classList.contains('button-longrunning-active')).toBe(true);
jest.runAllTimers();
await flushPromises();
await new Promise(queueMicrotask);
expect(mockSubmit).toHaveBeenCalled();
expect(label.textContent).toBe('Sign in');

Wyświetl plik

@ -1,3 +1,4 @@
import { setImmediate } from 'timers';
import { Application } from '@hotwired/stimulus';
import { SwapController } from './SwapController';
import { range } from '../utils/range';

Wyświetl plik

@ -4,10 +4,15 @@ import { SyncController } from './SyncController';
import { range } from '../utils/range';
jest.useFakeTimers();
jest.spyOn(global, 'setTimeout');
describe('SyncController', () => {
let application;
afterEach(() => {
jest.clearAllMocks();
});
describe('basic sync between two fields', () => {
beforeEach(() => {
application?.stop();

Wyświetl plik

@ -3,8 +3,6 @@ import $ from 'jquery';
import { Application } from '@hotwired/stimulus';
import { TagController } from './TagController';
const flushPromises = () => new Promise(setImmediate);
window.$ = $;
describe('TagController', () => {
@ -41,7 +39,7 @@ describe('TagController', () => {
someOther: 'option',
});
await flushPromises();
await new Promise(requestAnimationFrame);
// check the jQuery instance is the correct element
expect(element).toContain(document.getElementById('tag-input'));
@ -69,7 +67,7 @@ describe('TagController', () => {
window.initTagField('not-present');
await flushPromises();
await new Promise(requestAnimationFrame);
expect(tagitMock).not.toHaveBeenCalled();
});
@ -90,7 +88,7 @@ describe('TagController', () => {
expect(tagitMock).not.toHaveBeenCalled();
await flushPromises();
await new Promise(requestAnimationFrame);
expect(tagitMock).toHaveBeenCalledWith({
allowSpaces: true,
@ -116,7 +114,8 @@ describe('TagController', () => {
.getElementById('id_tags')
.dispatchEvent(new CustomEvent('example:event'));
await flushPromises();
await new Promise(requestAnimationFrame);
expect(tagitMock).toHaveBeenCalledWith('removeAll');
});
});

Wyświetl plik

@ -2,9 +2,6 @@ import { Application } from '@hotwired/stimulus';
import { UpgradeController } from './UpgradeController';
// https://stackoverflow.com/a/51045733
const flushPromises = () => new Promise(setImmediate);
describe('UpgradeController', () => {
let application;
const url = 'https://releases.wagtail.org/mock.txt';
@ -66,7 +63,7 @@ describe('UpgradeController', () => {
document.getElementById('panel').classList.contains('w-hidden'),
).toBe(true);
await flushPromises();
await new Promise(requestAnimationFrame);
// should remove the hidden class on success
expect(
@ -112,7 +109,7 @@ describe('UpgradeController', () => {
).toBe(true);
});
it('should throw an error if the fetch fails', () => {
it('should throw an error if the fetch fails', async () => {
// Spy on console.error to verify that it is called with the expected error message
jest.spyOn(console, 'error').mockImplementation(() => {});
@ -127,17 +124,16 @@ describe('UpgradeController', () => {
application.register('w-upgrade', UpgradeController);
// Wait for the catch block to be executed
/* eslint-disable-next-line no-promise-executor-return */
return new Promise((resolve) => setImmediate(resolve)).then(() => {
// Verify that console.error was called with the expected error message
/* eslint-disable-next-line no-console */
expect(console.error).toHaveBeenCalledWith(
`Error fetching ${url}. Error: Error: Fetch failed`,
);
await new Promise(requestAnimationFrame);
// Restore the original implementation of console.error
/* eslint-disable no-console */
console.error.mockRestore();
});
// Verify that console.error was called with the expected error message
/* eslint-disable-next-line no-console */
expect(console.error).toHaveBeenCalledWith(
`Error fetching ${url}. Error: Error: Fetch failed`,
);
// Restore the original implementation of console.error
/* eslint-disable no-console */
console.error.mockRestore();
});
});

Wyświetl plik

@ -1,7 +1,5 @@
import { initIconSprite } from './initIconSprite';
const flushPromises = () => new Promise(setImmediate);
describe('initIconSprite', () => {
const spriteURL = 'https://example.com/sprite.svg';
const responseText = '<svg>...</svg>';
@ -22,7 +20,7 @@ describe('initIconSprite', () => {
text: () => Promise.resolve(responseText),
});
initIconSprite(spriteContainer, spriteURL);
await flushPromises();
await new Promise(requestAnimationFrame);
expect(global.fetch).toHaveBeenCalled();
expect(global.fetch).toHaveBeenCalledWith(spriteURL);
@ -38,7 +36,7 @@ describe('initIconSprite', () => {
text: () => Promise.resolve(responseText),
});
initIconSprite(spriteContainer, spriteURL);
await flushPromises();
await new Promise(requestAnimationFrame);
expect(localStorage).not.toBe(null);
expect(localStorage['wagtail:spriteData']).toBe(responseText);
@ -51,7 +49,7 @@ describe('initIconSprite', () => {
const spy = jest.spyOn(console, 'error').mockImplementation();
initIconSprite(spriteContainer, spriteURL);
await flushPromises();
await new Promise(requestAnimationFrame);
expect(global.fetch).toHaveBeenCalled();
expect(global.fetch).toHaveBeenCalledWith(spriteURL);