docs(docs-infra): replace wait with debounce for search

Replaces custom search debounce with `debounced`
This commit is contained in:
SkyZeroZx
2026-03-26 14:52:52 -05:00
committed by Pawel Kozlowski
parent 7234432715
commit ff2ba7c864
4 changed files with 64 additions and 30 deletions
@@ -14,7 +14,7 @@ import {Router, provideRouter} from '@angular/router';
import {SearchDialog} from './search-dialog.component';
import {ENVIRONMENT, WINDOW} from '../../providers';
import {ALGOLIA_CLIENT, Search} from '../../services';
import {FakeEventTarget} from '../../testing/index';
import {FakeEventTarget, timeout, useAutoTick} from '../../testing/index';
import {AlgoliaIcon} from '../algolia-icon/algolia-icon.component';
import {SearchResult} from '../../interfaces';
@@ -27,8 +27,10 @@ describe('SearchDialog', () => {
let search: Search;
useAutoTick();
beforeEach(async () => {
searchResults.and.returnValue([]);
searchResults.and.returnValue(Promise.resolve({results: [{hits: []}]}));
TestBed.configureTestingModule({
imports: [SearchDialog],
@@ -55,6 +57,9 @@ describe('SearchDialog', () => {
// Fire the request
TestBed.inject(ApplicationRef).tick();
// The delay from debounced (200ms)
await timeout(300);
// Wait for the resource to resolve
await TestBed.inject(ApplicationRef).whenStable();
@@ -85,6 +90,9 @@ describe('SearchDialog', () => {
// Fire the request
TestBed.inject(ApplicationRef).tick();
// The delay from debounced (200ms)
await timeout(300);
// Wait for the resource to resolve
await TestBed.inject(ApplicationRef).whenStable();
@@ -96,7 +104,7 @@ describe('SearchDialog', () => {
});
it('should display `Start typing to see results` message when there are no provided query', () => {
searchResults.and.returnValue(undefined);
searchResults.and.returnValue(Promise.resolve(undefined));
const startTypingContainer = fixture.debugElement.query(
By.css('.docs-search-results__start-typing'),
@@ -112,6 +120,9 @@ describe('SearchDialog', () => {
// Fire the request
TestBed.inject(ApplicationRef).tick();
// The delay from debounced (200ms)
await timeout(300);
// Wait for the resource to resolve
await TestBed.inject(ApplicationRef).whenStable();
+5 -27
View File
@@ -10,6 +10,7 @@ import {
Injectable,
InjectionToken,
Provider,
debounced,
inject,
linkedSignal,
resource,
@@ -48,13 +49,11 @@ export class Search {
private readonly config = inject(ENVIRONMENT);
private readonly client = inject(ALGOLIA_CLIENT);
debounceParams = debounced(this.searchQuery, SEARCH_DELAY);
readonly resultsResource = resource({
params: () => this.searchQuery() || undefined, // coerces empty string to undefined
loader: async ({params: query, abortSignal}) => {
// Until we have a better alternative we debounce by awaiting for a short delay.
await wait(SEARCH_DELAY, abortSignal);
return this.searchWithQuery(query);
},
params: () => this.debounceParams.value() || undefined, // coerces empty string to undefined
loader: async ({params}) => this.searchWithQuery(params),
});
readonly searchResults = linkedSignal<SearchResultItem[] | undefined, SearchResultItem[]>({
@@ -223,27 +222,6 @@ function matched(snippet: SnippetResult | undefined): boolean {
return snippet?.matchLevel !== undefined && snippet.matchLevel !== 'none';
}
/**
* Temporary helper to implement the debounce functionality on the search resource
*/
function wait(ms: number, signal: AbortSignal): Promise<void> {
return new Promise<void>((resolve, reject) => {
let timeout: ReturnType<typeof setTimeout> | undefined;
const onAbort = () => {
clearTimeout(timeout);
reject(new Error('Operation aborted'));
};
timeout = setTimeout(() => {
signal.removeEventListener('abort', onAbort);
resolve();
}, ms);
signal.addEventListener('abort', onAbort, {once: true});
});
}
function extractPackageNameFromUrl(url: string): string | null {
const extractedSegment = url.match(/\/api\/(.*)\/.*#?/);
if (extractedSegment == null) {
+2
View File
@@ -21,8 +21,10 @@ ts_project(
"index.ts",
],
),
tsconfig = "//adev/shared-docs:tsconfig_test",
deps = [
"//adev:node_modules/@angular/core",
"//adev:node_modules/@types/jasmine",
"//adev:node_modules/@webcontainer/api",
],
)
@@ -181,3 +181,46 @@ export class FakeWebContainerProcess implements WebContainerProcess {
kill(): void {}
resize(dimensions: {cols: number; rows: number}): void {}
}
// Copy from utils in packages/private/testing
/**
* Returns a promise that resolves after the specified time.
*
* @param ms - Time to wait in milliseconds. Defaults to 0.
*
* @example
* ```ts
* await timeout(100); // Wait 100ms
* ```
*/
export async function timeout(ms?: number): Promise<void> {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
/**
* Installs Jasmine's fake clock with auto-tick enabled for all tests in the describe block.
* Call at the top level of a describe block to automatically advance time for async operations.
*
* @example
* ```ts
* describe('MyComponent', () => {
* useAutoTick();
*
* it('should handle timers', () => {
* // setTimeout, setInterval, etc. will execute synchronously
* });
* });
* ```
*/
export function useAutoTick() {
beforeEach(() => {
jasmine.clock().install();
jasmine.clock().autoTick();
});
afterEach(() => {
jasmine.clock().uninstall();
});
}