Back to Repositories

Validating JSX Runtime Implementation in PreactJS

This test suite validates the JSX Runtime implementation in Preact, focusing on JSX transformation, component rendering, and entity encoding. It ensures proper handling of props, refs, and template functionality while verifying core JSX operations and edge cases.

Test Coverage Overview

The test suite provides comprehensive coverage of Preact’s JSX Runtime functionality.

Key areas tested include:
  • JSX/JSXDEV exports and basic functionality
  • Props and ref handling
  • DefaultProps implementation
  • Entity encoding and escaping
  • Template construction and expression handling

Implementation Analysis

The testing approach uses Jest’s describe/it pattern for organized test grouping. Tests verify both basic and complex JSX operations, including component rendering, prop management, and template handling. The implementation leverages Preact’s internal utilities and custom helpers for setup and teardown.

Technical Details

Testing stack includes:
  • Jest test framework
  • Custom scratch element setup
  • Preact’s internal options API
  • Component lifecycle management
  • Sinon for spy functionality

Best Practices Demonstrated

The test suite exemplifies strong testing practices through:
  • Proper test isolation with beforeEach/afterEach hooks
  • Comprehensive edge case coverage
  • Clear test descriptions
  • Consistent assertion patterns
  • Proper cleanup of test environment

preactjs/preact

jsx-runtime/test/browser/jsx-runtime.test.js

            
import { Component, createElement, createRef, options } from 'preact';
import {
	jsx,
	jsxs,
	jsxDEV,
	Fragment,
	jsxAttr,
	jsxTemplate,
	jsxEscape
} from 'preact/jsx-runtime';
import { setupScratch, teardown } from '../../../test/_util/helpers';
import { encodeEntities } from 'preact/jsx-runtime/src/utils';

describe('Babel jsx/jsxDEV', () => {
	let scratch;
	let prevVNodeOption;

	beforeEach(() => {
		scratch = setupScratch();
		prevVNodeOption = options.vnode;
	});

	afterEach(() => {
		options.vnode = prevVNodeOption;
		teardown(scratch);
	});

	it('should have needed exports', () => {
		expect(typeof jsx).to.equal('function');
		expect(typeof jsxs).to.equal('function');
		expect(typeof jsxDEV).to.equal('function');
		expect(typeof Fragment).to.equal('function');
	});

	it('should keep ref in props', () => {
		const ref = () => null;
		const props = { ref };
		const vnode = jsx('div', props);
		expect(vnode.ref).to.equal(ref);
		expect(vnode.props).to.not.equal(props);
	});

	it('should not copy props wen there is no ref in props', () => {
		const props = { x: 'y' };
		const vnode = jsx('div', props);
		expect(vnode.props).to.equal(props);
	});

	it('should add keys', () => {
		const vnode = jsx('div', null, 'foo');
		expect(vnode.key).to.equal('foo');
	});

	it('should apply defaultProps', () => {
		class Foo extends Component {
			render() {
				return <div />;
			}
		}

		Foo.defaultProps = {
			foo: 'bar'
		};

		const vnode = jsx(Foo, {}, null);
		expect(vnode.props).to.deep.equal({
			foo: 'bar'
		});
	});

	it('should keep props over defaultProps', () => {
		class Foo extends Component {
			render() {
				return <div />;
			}
		}

		Foo.defaultProps = {
			foo: 'bar'
		};

		const vnode = jsx(Foo, { foo: 'baz' }, null);
		expect(vnode.props).to.deep.equal({
			foo: 'baz'
		});
	});

	it('should set __source and __self', () => {
		const vnode = jsx('div', { class: 'foo' }, 'key', false, 'source', 'self');
		expect(vnode.__source).to.equal('source');
		expect(vnode.__self).to.equal('self');
	});

	it('should return a vnode like createElement', () => {
		const elementVNode = createElement('div', {
			class: 'foo',
			key: 'key'
		});
		const jsxVNode = jsx('div', { class: 'foo' }, 'key');
		delete jsxVNode.__self;
		delete jsxVNode.__source;
		delete jsxVNode._original;
		delete elementVNode._original;
		expect(jsxVNode).to.deep.equal(elementVNode);
	});

	// #2839
	it('should remove ref from props', () => {
		const ref = createRef();
		const vnode = jsx('div', { ref }, null);
		expect(vnode.props).to.deep.equal({});
		expect(vnode.ref).to.equal(ref);
	});

	it('should call options.vnode with the vnode', () => {
		options.vnode = sinon.spy();
		const vnode = jsx('div', { class: 'foo' }, 'key');
		expect(options.vnode).to.have.been.calledWith(vnode);
	});
});

describe('encodeEntities', () => {
	it('should encode', () => {
		expect(encodeEntities("&<'")).to.equal("&<'");
	});
});

describe('precompiled JSX', () => {
	describe('jsxAttr', () => {
		beforeEach(() => {
			options.attr = undefined;
		});

		afterEach(() => {
			options.attr = undefined;
		});

		it('should render simple values', () => {
			expect(jsxAttr('foo', 'bar')).to.equal('foo="bar"');
		});

		it('should render boolean values', () => {
			expect(jsxAttr('foo', true)).to.equal('foo');
			expect(jsxAttr('foo', false)).to.equal('');
		});

		it('should ignore invalid values', () => {
			expect(jsxAttr('foo', false)).to.equal('');
			expect(jsxAttr('foo', null)).to.equal('');
			expect(jsxAttr('foo', undefined)).to.equal('');
			expect(jsxAttr('foo', () => null)).to.equal('');
			expect(jsxAttr('foo', [])).to.equal('');
			expect(jsxAttr('key', 'foo')).to.equal('');
			expect(jsxAttr('ref', 'foo')).to.equal('');
		});

		it('should escape values', () => {
			expect(jsxAttr('foo', "&<'")).to.equal('foo="&<\'"');
		});

		it('should call options.attr()', () => {
			options.attr = (name, value) => {
				return `data-${name}="foo${value}"`;
			};

			expect(jsxAttr('foo', 'bar')).to.equal('data-foo="foobar"');
		});

		it('should serialize style object', () => {
			expect(jsxAttr('style', { padding: 3 })).to.equal('style="padding:3px;"');
		});
	});

	describe('jsxTemplate', () => {
		it('should construct basic template vnode', () => {
			const tpl = [`<div>foo</div>`];
			const vnode = jsxTemplate(tpl);
			expect(vnode.props.tpl).to.equal(tpl);
			expect(vnode.type).to.equal(Fragment);
			expect(vnode.key).not.to.equal(null);
		});

		it('should constructe template vnode with expressions', () => {
			const tpl = [`<div>foo`, '</div>'];
			const vnode = jsxTemplate(tpl, 'bar');
			expect(vnode.props.tpl).to.equal(tpl);
			expect(vnode.props.exprs).to.deep.equal(['bar']);
			expect(vnode.type).to.equal(Fragment);
			expect(vnode.key).not.to.equal(null);
		});
	});

	describe('jsxEscape', () => {
		it('should escape string children', () => {
			expect(jsxEscape('foo')).to.equal('foo');
			expect(jsxEscape(2)).to.equal('2');
			expect(jsxEscape('&"<')).to.equal('&"<');
			expect(jsxEscape(null)).to.equal(null);
			expect(jsxEscape(undefined)).to.equal(null);
			expect(jsxEscape(true)).to.equal(null);
			expect(jsxEscape(false)).to.equal(null);
		});

		it("should leave VNode's as is", () => {
			const vnode = jsx('div', null);
			expect(jsxEscape(vnode)).to.equal(vnode);
		});

		it('should escape arrays', () => {
			const vnode = jsx('div', null);
			expect(
				jsxEscape([vnode, 'foo&"<', null, undefined, true, false, 2, 'foo'])
			).to.deep.equal([
				vnode,
				'foo&"<',
				null,
				null,
				null,
				null,
				'2',
				'foo'
			]);
		});
	});
});