Back to Repositories

Testing URLSearchParams Reactivity Implementation in Svelte

This test suite validates the SvelteURLSearchParams implementation, ensuring proper reactivity and compatibility with the standard URLSearchParams API. The tests verify parameter manipulation methods and reactive updates in a Svelte context.

Test Coverage Overview

The test suite provides comprehensive coverage of URLSearchParams functionality with reactive updates.

Key areas tested include:
  • Parameter manipulation (set, append, delete)
  • Parameter retrieval (get, getAll)
  • String representation
  • Instance type verification
Edge cases cover empty parameters, duplicate values, and no-op operations.

Implementation Analysis

Tests utilize Svelte’s effect_root and render_effect for tracking reactive updates. The implementation follows a structured pattern of setting up test scenarios, performing operations with flushSync, and verifying the resulting state through logged outputs.

Framework-specific features include:
  • Reactive effect tracking
  • Synchronous update flushing
  • Cleanup handling

Technical Details

Testing infrastructure includes:
  • Vitest as the test runner
  • Svelte’s internal reactivity system
  • TypeScript for type safety
  • Custom SvelteURLSearchParams implementation
Configuration leverages Svelte’s client-side reactivity modules and effect management.

Best Practices Demonstrated

The test suite exemplifies strong testing practices through isolated test cases, comprehensive state verification, and proper cleanup.

Notable practices include:
  • Consistent test structure
  • Reactive state tracking
  • Explicit operation verification
  • Memory leak prevention
  • Edge case coverage

sveltejs/svelte

packages/svelte/src/reactivity/url-search-params.test.ts

            
import { render_effect, effect_root } from '../internal/client/reactivity/effects.js';
import { flushSync } from '../index-client.js';
import { assert, test } from 'vitest';
import { SvelteURLSearchParams } from './url-search-params';

test('new URLSearchParams', () => {
	const params = new SvelteURLSearchParams('a=b');
	const log: any = [];

	const cleanup = effect_root(() => {
		render_effect(() => {
			log.push(params.toString());
		});
	});

	flushSync(() => {
		params.set('a', 'c');
	});

	flushSync(() => {
		// nothing should happen here
		params.set('a', 'c');
	});

	assert.deepEqual(log, ['a=b', 'a=c']);

	cleanup();
});

test('URLSearchParams.set', () => {
	const params = new SvelteURLSearchParams();
	const log: any = [];

	const cleanup = effect_root(() => {
		render_effect(() => {
			log.push(params.toString());
		});
	});

	flushSync(() => {
		params.set('a', 'b');
	});

	flushSync(() => {
		params.set('a', 'c');
	});

	flushSync(() => {
		// nothing should happen here
		params.set('a', 'c');
	});

	assert.deepEqual(log, ['', 'a=b', 'a=c']);

	cleanup();
});

test('URLSearchParams.append', () => {
	const params = new SvelteURLSearchParams();
	const log: any = [];

	const cleanup = effect_root(() => {
		render_effect(() => {
			log.push(params.toString());
		});
	});

	flushSync(() => {
		params.append('a', 'b');
	});

	flushSync(() => {
		// nothing should happen here
		params.set('a', 'b');
	});

	flushSync(() => {
		params.append('a', 'c');
	});

	assert.deepEqual(log, ['', 'a=b', 'a=b&a=c']);

	cleanup();
});

test('URLSearchParams.delete', () => {
	const params = new SvelteURLSearchParams('a=b&c=d');
	const log: any = [];

	const cleanup = effect_root(() => {
		render_effect(() => {
			log.push(params.toString());
		});
	});

	flushSync(() => {
		params.delete('a');
	});

	flushSync(() => {
		// nothing should happen here
		params.delete('a');
	});

	flushSync(() => {
		params.set('a', 'b');
	});

	assert.deepEqual(log, ['a=b&c=d', 'c=d', 'c=d&a=b']);

	cleanup();
});

test('URLSearchParams.get', () => {
	const params = new SvelteURLSearchParams('a=b&c=d');
	const log: any = [];

	const cleanup = effect_root(() => {
		render_effect(() => {
			log.push(params.get('a'));
		});
		render_effect(() => {
			log.push(params.get('c'));
		});
		render_effect(() => {
			log.push(params.get('e'));
		});
	});

	flushSync(() => {
		params.set('a', 'b');
	});

	flushSync(() => {
		params.set('a', 'new-b');
	});

	flushSync(() => {
		params.delete('a');
	});

	assert.deepEqual(log, ['b', 'd', null, 'new-b', 'd', null, null, 'd', null]);

	cleanup();
});

test('URLSearchParams.getAll', () => {
	const params = new SvelteURLSearchParams('a=b&c=d');
	const log: any = [];

	const cleanup = effect_root(() => {
		render_effect(() => {
			log.push(params.getAll('a'));
		});
		render_effect(() => {
			log.push(params.getAll('q'));
		});
	});

	flushSync(() => {
		params.append('a', 'b1');
	});

	flushSync(() => {
		params.append('q', 'z');
	});

	assert.deepEqual(log, [
		// initial
		['b'],
		[],
		// first flush
		['b', 'b1'],
		[],
		// second flush
		['b', 'b1'],
		['z']
	]);

	cleanup();
});

test('URLSearchParams.toString', () => {
	const params = new SvelteURLSearchParams();
	const log: any = [];

	const cleanup = effect_root(() => {
		render_effect(() => {
			log.push(params.toString());
		});
	});

	flushSync(() => {
		params.set('a', 'b');
	});

	flushSync(() => {
		params.append('a', 'c');
	});

	assert.deepEqual(log, ['', 'a=b', 'a=b&a=c']);

	cleanup();
});

test('SvelteURLSearchParams instanceof URLSearchParams', () => {
	assert.ok(new SvelteURLSearchParams() instanceof URLSearchParams);
});