diff --git a/eslint.config.ts b/eslint.config.ts index 44d1caea8e..d70f0dd793 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -1,5 +1,6 @@ import path from 'node:path'; import { globalIgnores } from 'eslint/config'; +import jest from 'eslint-plugin-jest'; import prettierRecommended from 'eslint-plugin-prettier/recommended'; import globals from 'globals'; import tsEslint from 'typescript-eslint'; @@ -52,10 +53,6 @@ const config = tsEslint.config([ globals: { ...globals.browser, ...globals.node, - ...globals.mocha, - ...globals.jest, - __DEBUG_SERVER_ERRORS__: true, - __SERVER_ERRORS__: true, }, parserOptions: { @@ -190,6 +187,16 @@ const config = tsEslint.config([ 'react/no-deprecated': 'off', }, }, + { + files: ['node_package/tests/**', '**/*.test.{js,jsx,ts,tsx}'], + + extends: [jest.configs['flat/recommended'], jest.configs['flat/style']], + + rules: { + // Allows Jest mocks before import + 'import/first': 'off', + }, + }, // must be the last config in the array // https://github.com/prettier/eslint-plugin-prettier?tab=readme-ov-file#configuration-new-eslintconfigjs prettierRecommended, diff --git a/node_package/tests/Authenticity.test.js b/node_package/tests/Authenticity.test.js index 68d97edaac..743b18c512 100644 --- a/node_package/tests/Authenticity.test.js +++ b/node_package/tests/Authenticity.test.js @@ -8,28 +8,22 @@ meta.content = testToken; document.head.appendChild(meta); describe('authenticityToken', () => { - expect.assertions(2); it('exists in ReactOnRails API', () => { - expect.assertions(1); - expect(typeof ReactOnRails.authenticityToken).toEqual('function'); + expect(typeof ReactOnRails.authenticityToken).toBe('function'); }); it('can read Rails CSRF token from ', () => { - expect.assertions(1); const realToken = ReactOnRails.authenticityToken(); expect(realToken).toEqual(testToken); }); }); describe('authenticityHeaders', () => { - expect.assertions(2); it('exists in ReactOnRails API', () => { - expect.assertions(1); - expect(typeof ReactOnRails.authenticityHeaders).toEqual('function'); + expect(typeof ReactOnRails.authenticityHeaders).toBe('function'); }); it('returns valid header with CSRF token', () => { - expect.assertions(1); const realHeader = ReactOnRails.authenticityHeaders(); expect(realHeader).toEqual({ 'X-CSRF-Token': testToken, 'X-Requested-With': 'XMLHttpRequest' }); }); diff --git a/node_package/tests/ComponentRegistry.test.js b/node_package/tests/ComponentRegistry.test.js index 44e460cdfe..1b5fe51773 100644 --- a/node_package/tests/ComponentRegistry.test.js +++ b/node_package/tests/ComponentRegistry.test.js @@ -37,7 +37,6 @@ describe('ComponentRegistry', () => { }); it('registers and retrieves React function components', () => { - expect.assertions(1); const C1 = () =>
HELLO
; ComponentRegistry.register({ C1 }); const actual = ComponentRegistry.get('C1'); @@ -46,7 +45,6 @@ describe('ComponentRegistry', () => { }); it('registers and retrieves Render-Function components where property renderFunction is set and zero params', () => { - expect.assertions(1); const C1 = () =>
HELLO
; C1.renderFunction = true; ComponentRegistry.register({ C1 }); @@ -56,7 +54,6 @@ describe('ComponentRegistry', () => { }); it('registers and retrieves ES5 class components', () => { - expect.assertions(1); const C2 = createReactClass({ render() { return
WORLD
; @@ -69,7 +66,6 @@ describe('ComponentRegistry', () => { }); it('registers and retrieves ES6 class components', () => { - expect.assertions(1); class C3 extends React.Component { render() { return
Wow!
; @@ -82,7 +78,6 @@ describe('ComponentRegistry', () => { }); it('registers and retrieves renderers if 3 params', () => { - expect.assertions(1); const C4 = (a1, a2, a3) => null; ComponentRegistry.register({ C4 }); const actual = ComponentRegistry.get('C4'); @@ -95,7 +90,6 @@ describe('ComponentRegistry', () => { * Thus, tests are cumulative. */ it('registers and retrieves multiple components', () => { - expect.assertions(4); // Plain react stateless functional components const C5 = () =>
WHY
; const C6 = () =>
NOW
; @@ -127,7 +121,6 @@ describe('ComponentRegistry', () => { }); it('only detects a renderer function if it has three arguments', () => { - expect.assertions(2); const C7 = (a1, a2) => null; const C8 = (a1) => null; ComponentRegistry.register({ C7 }); @@ -148,20 +141,17 @@ describe('ComponentRegistry', () => { }); it('throws error for retrieving unregistered component', () => { - expect.assertions(1); expect(() => ComponentRegistry.get('foobar')).toThrow( /Could not find component registered with name foobar/, ); }); it('throws error for setting null component', () => { - expect.assertions(1); const C9 = null; expect(() => ComponentRegistry.register({ C9 })).toThrow(/Called register with null component named C9/); }); it('retrieves component asynchronously when registered later', async () => { - expect.assertions(1); const C1 = () =>
HELLO
; const componentPromise = ComponentRegistry.getOrWaitForComponent('C1'); ComponentRegistry.register({ C1 }); @@ -175,11 +165,12 @@ describe('ComponentRegistry', () => { }); it('handles timeout for unregistered components', async () => { - expect.assertions(1); + let error; try { await ComponentRegistry.getOrWaitForComponent('NonExistent'); - } catch (error) { - expect(error.message).toMatch(/Could not find component/); + } catch (e) { + error = e; } + expect(error.message).toMatch(/Could not find component/); }); }); diff --git a/node_package/tests/ReactOnRails.test.jsx b/node_package/tests/ReactOnRails.test.jsx index 3e7f757adf..a39b67040d 100644 --- a/node_package/tests/ReactOnRails.test.jsx +++ b/node_package/tests/ReactOnRails.test.jsx @@ -7,9 +7,7 @@ import * as createReactClass from 'create-react-class'; import ReactOnRails from '../src/ReactOnRails.client.ts'; describe('ReactOnRails', () => { - expect.assertions(14); it('render returns a virtual DOM element for component', () => { - expect.assertions(1); const R1 = createReactClass({ render() { return
WORLD
; @@ -25,12 +23,11 @@ describe('ReactOnRails', () => { document.body.appendChild(root); ReactOnRails.render('R1', {}, 'root'); - expect(document.getElementById('root').textContent).toEqual(' WORLD '); + expect(document.getElementById('root').textContent).toBe(' WORLD '); }); it('accepts traceTurbolinks as an option true', () => { ReactOnRails.resetOptions(); - expect.assertions(1); ReactOnRails.setOptions({ traceTurbolinks: true }); const actual = ReactOnRails.option('traceTurbolinks'); expect(actual).toBe(true); @@ -38,7 +35,6 @@ describe('ReactOnRails', () => { it('accepts traceTurbolinks as an option false', () => { ReactOnRails.resetOptions(); - expect.assertions(1); ReactOnRails.setOptions({ traceTurbolinks: false }); const actual = ReactOnRails.option('traceTurbolinks'); expect(actual).toBe(false); @@ -46,7 +42,6 @@ describe('ReactOnRails', () => { it('not specified has traceTurbolinks as false', () => { ReactOnRails.resetOptions(); - expect.assertions(1); ReactOnRails.setOptions({}); const actual = ReactOnRails.option('traceTurbolinks'); expect(actual).toBe(false); @@ -54,13 +49,10 @@ describe('ReactOnRails', () => { it('setOptions method throws error for invalid options', () => { ReactOnRails.resetOptions(); - expect.assertions(1); expect(() => ReactOnRails.setOptions({ foobar: true })).toThrow(/Invalid option/); }); it('registerStore throws if passed a falsey object (null, undefined, etc)', () => { - expect.assertions(3); - expect(() => ReactOnRails.registerStore(null)).toThrow(/null or undefined/); expect(() => ReactOnRails.registerStore(undefined)).toThrow(/null or undefined/); @@ -69,7 +61,6 @@ describe('ReactOnRails', () => { }); it('register store and getStoreGenerator allow registration', () => { - expect.assertions(2); function reducer() { return {}; } @@ -87,7 +78,6 @@ describe('ReactOnRails', () => { }); it('setStore and getStore', () => { - expect.assertions(2); function reducer() { return {}; } @@ -109,7 +99,6 @@ describe('ReactOnRails', () => { }); it('clearHydratedStores', () => { - expect.assertions(2); function reducer() { return {}; } diff --git a/node_package/tests/StoreRegistry.test.js b/node_package/tests/StoreRegistry.test.js index 2ebb98aedd..3a498fb345 100644 --- a/node_package/tests/StoreRegistry.test.js +++ b/node_package/tests/StoreRegistry.test.js @@ -14,11 +14,12 @@ function storeGenerator2(props) { return createStore(reducer, props); } -describe('', () => { - expect.assertions(11); - it('StoreRegistry throws error for registering null or undefined store', () => { - expect.assertions(2); +describe('StoreRegistry', () => { + beforeEach(() => { StoreRegistry.stores().clear(); + }); + + it('StoreRegistry throws error for registering null or undefined store', () => { expect(() => StoreRegistry.register({ storeGenerator: null })).toThrow( /Called ReactOnRails.registerStoreGenerators with a null or undefined as a value/, ); @@ -28,15 +29,12 @@ describe('', () => { }); it('StoreRegistry throws error for retrieving unregistered store', () => { - expect.assertions(1); - StoreRegistry.stores().clear(); expect(() => StoreRegistry.getStore('foobar')).toThrow( /There are no stores hydrated and you are requesting the store/, ); }); it('StoreRegistry registers and retrieves Render-Function stores', () => { - expect.assertions(2); StoreRegistry.register({ storeGenerator, storeGenerator2 }); const actual = StoreRegistry.getStoreGenerator('storeGenerator'); const expected = storeGenerator; @@ -46,15 +44,13 @@ describe('', () => { expect(actual2).toEqual(expected2); }); - it('StoreRegistry throws error for retrieving unregistered store', () => { - expect.assertions(1); + it('StoreRegistry throws error for retrieving unregistered store generator', () => { expect(() => StoreRegistry.getStoreGenerator('foobar')).toThrow( /Could not find store generator registered with name foobar\. Registered store generator names include/, ); }); it('StoreRegistry returns undefined for retrieving unregistered store, passing throwIfMissing = false', () => { - expect.assertions(1); StoreRegistry.setStore('foobarX', {}); const actual = StoreRegistry.getStore('foobar', false); const expected = undefined; @@ -62,7 +58,6 @@ describe('', () => { }); it('StoreRegistry getStore, setStore', () => { - expect.assertions(1); const store = storeGenerator({}); StoreRegistry.setStore('storeGenerator', store); const actual = StoreRegistry.getStore('storeGenerator'); @@ -71,14 +66,12 @@ describe('', () => { }); it('StoreRegistry throws error for retrieving unregistered hydrated store', () => { - expect.assertions(1); expect(() => StoreRegistry.getStore('foobar')).toThrow( /Could not find hydrated store registered with name foobar\. Registered hydrated store names include/, ); }); it('StoreRegistry clearHydratedStores', () => { - expect.assertions(2); StoreRegistry.clearHydratedStores(); const result = storeGenerator({}); diff --git a/node_package/tests/buildConsoleReplay.test.js b/node_package/tests/buildConsoleReplay.test.js index 142941fbe6..80bb01aae2 100644 --- a/node_package/tests/buildConsoleReplay.test.js +++ b/node_package/tests/buildConsoleReplay.test.js @@ -1,27 +1,17 @@ import buildConsoleReplay, { consoleReplay } from '../src/buildConsoleReplay.ts'; describe('consoleReplay', () => { - expect.assertions(8); it('does not throw an exception if no console.history object', () => { - expect.assertions(1); expect(() => consoleReplay()).not.toThrow(/Error/); }); it('returns empty string if no console.history object', () => { - expect.assertions(1); const actual = consoleReplay(); const expected = ''; expect(actual).toEqual(expected); }); - it('does not throw an exception if console.history.length is zero', () => { - expect.assertions(1); - console.history = []; - expect(() => consoleReplay()).not.toThrow(/Error/); - }); - - it('returns empty string if no console.history object', () => { - expect.assertions(1); + it('returns empty string if console.history is empty', () => { console.history = []; const actual = consoleReplay(); const expected = ''; @@ -29,7 +19,6 @@ describe('consoleReplay', () => { }); it('replays multiple history messages', () => { - expect.assertions(1); console.history = [ { arguments: ['a', 'b'], level: 'log' }, { arguments: ['c', 'd'], level: 'warn' }, @@ -40,7 +29,6 @@ describe('consoleReplay', () => { }); it('replays converts console param objects to JSON', () => { - expect.assertions(1); console.history = [ { arguments: ['some message', { a: 1, b: 2 }], level: 'log' }, { arguments: ['other message', { c: 3, d: 4 }], level: 'warn' }, @@ -52,8 +40,7 @@ console.warn.apply(console, ["other message","{\\"c\\":3,\\"d\\":4}"]);`; expect(actual).toEqual(expected); }); - it('replays converts script tag inside of object string to be safe ', () => { - expect.assertions(1); + it('replays converts script tag inside of object string to be safe', () => { console.history = [ { arguments: [ @@ -74,7 +61,6 @@ console.warn.apply(console, ["other message","{\\"c\\":3,\\"d\\":4}"]);`; }); it('buildConsoleReplay wraps console replay in a script tag', () => { - expect.assertions(1); console.history = [ { arguments: ['some message', { a: 1, b: 2 }], level: 'log' }, { arguments: ['other message', { c: 3, d: 4 }], level: 'warn' }, diff --git a/node_package/tests/renderFunction.test.jsx b/node_package/tests/renderFunction.test.jsx index 4cbc5c8c02..7372216656 100644 --- a/node_package/tests/renderFunction.test.jsx +++ b/node_package/tests/renderFunction.test.jsx @@ -8,10 +8,7 @@ import * as createReactClass from 'create-react-class'; import isRenderFunction from '../src/isRenderFunction.ts'; describe('isRenderFunction', () => { - expect.assertions(6); it('returns false for a ES5 React Component', () => { - expect.assertions(1); - const es5Component = createReactClass({ render() { return
ES5 React Component
; @@ -22,8 +19,6 @@ describe('isRenderFunction', () => { }); it('returns false for a ES6 React class', () => { - expect.assertions(1); - class ES6Component extends React.Component { render() { return
ES6 Component
; @@ -34,8 +29,6 @@ describe('isRenderFunction', () => { }); it('returns false for a ES6 React subclass', () => { - expect.assertions(1); - class ES6Component extends React.Component { render() { return
ES6 Component
; @@ -52,24 +45,18 @@ describe('isRenderFunction', () => { }); it('returns false for a stateless functional component with zero params', () => { - expect.assertions(1); - const pureComponent = () =>

Hello

; expect(isRenderFunction(pureComponent)).toBe(false); }); it('returns false for a stateless functional component with one param', () => { - expect.assertions(1); - const pureComponent = (props) =>

{props.title}

; expect(isRenderFunction(pureComponent)).toBe(false); }); it('returns true for a Render-Function (containing two params)', () => { - expect.assertions(1); - const foobarComponent = () =>
Component for Render-Function
; const foobarrenderFunction = (_props, _railsContext) => foobarComponent; @@ -77,8 +64,6 @@ describe('isRenderFunction', () => { }); it('returns false for simple object', () => { - expect.assertions(1); - const foobarComponent = { hello() { return 'world'; diff --git a/node_package/tests/scriptSanitizedVal.test.js b/node_package/tests/scriptSanitizedVal.test.js index 13cd8700e3..3dba559eac 100644 --- a/node_package/tests/scriptSanitizedVal.test.js +++ b/node_package/tests/scriptSanitizedVal.test.js @@ -1,9 +1,7 @@ import scriptSanitizedVal from '../src/scriptSanitizedVal.ts'; describe('scriptSanitizedVal', () => { - expect.assertions(5); it('returns no { - expect.assertions(1); const input = '[SERVER] This is a script:"" 2', () => { - expect.assertions(1); const input = 'Script2:"" '; const actual = scriptSanitizedVal(input); const expected = 'Script2:""(/script xx> 3', () => { - expect.assertions(1); const input = 'Script3:"" '; const actual = scriptSanitizedVal(input); const expected = 'Script3:""(/script xx> 4', () => { - expect.assertions(1); const input = 'Script4""alert(\'WTF4\')'; const actual = scriptSanitizedVal(input); const expected = 'Script4""(/script 5', () => { - expect.assertions(1); const input = 'Script5:"" '; const actual = scriptSanitizedVal(input); const expected = 'Script5:""(/script>