Skip to content

Commit b92a959

Browse files
authored
feat: accept query params in initialRoute (#409)
Closes #407
1 parent b6fd475 commit b92a959

File tree

3 files changed

+69
-38
lines changed

3 files changed

+69
-38
lines changed

projects/testing-library/src/lib/testing-library.ts

+40-37
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,45 @@ export async function render<SutType, WrapperType = SutType>(
112112

113113
const zone = safeInject(NgZone);
114114
const router = safeInject(Router);
115+
const _navigate = async (elementOrPath: Element | string, basePath = ''): Promise<boolean> => {
116+
const href = typeof elementOrPath === 'string' ? elementOrPath : elementOrPath.getAttribute('href');
117+
const [path, params] = (basePath + href).split('?');
118+
const queryParams = params
119+
? params.split('&').reduce((qp, q) => {
120+
const [key, value] = q.split('=');
121+
const currentValue = qp[key];
122+
if (typeof currentValue === 'undefined') {
123+
qp[key] = value;
124+
} else if (Array.isArray(currentValue)) {
125+
qp[key] = [...currentValue, value];
126+
} else {
127+
qp[key] = [currentValue, value];
128+
}
129+
return qp;
130+
}, {} as Record<string, string | string[]>)
131+
: undefined;
115132

116-
if (initialRoute) await router.navigate([initialRoute]);
133+
const navigateOptions: NavigationExtras | undefined = queryParams
134+
? {
135+
queryParams,
136+
}
137+
: undefined;
138+
139+
const doNavigate = () => {
140+
return navigateOptions ? router?.navigate([path], navigateOptions) : router?.navigate([path]);
141+
};
142+
143+
let result;
144+
145+
if (zone) {
146+
await zone.run(() => (result = doNavigate()));
147+
} else {
148+
result = doNavigate();
149+
}
150+
return result ?? false;
151+
};
152+
153+
if (initialRoute) await _navigate(initialRoute);
117154

118155
if (typeof router?.initialNavigation === 'function') {
119156
if (zone) {
@@ -167,43 +204,9 @@ export async function render<SutType, WrapperType = SutType>(
167204
};
168205

169206
const navigate = async (elementOrPath: Element | string, basePath = ''): Promise<boolean> => {
170-
const href = typeof elementOrPath === 'string' ? elementOrPath : elementOrPath.getAttribute('href');
171-
const [path, params] = (basePath + href).split('?');
172-
const queryParams = params
173-
? params.split('&').reduce((qp, q) => {
174-
const [key, value] = q.split('=');
175-
const currentValue = qp[key];
176-
if (typeof currentValue === 'undefined') {
177-
qp[key] = value;
178-
} else if (Array.isArray(currentValue)) {
179-
qp[key] = [...currentValue, value];
180-
} else {
181-
qp[key] = [currentValue, value];
182-
}
183-
return qp;
184-
}, {} as Record<string, string | string[]>)
185-
: undefined;
186-
187-
const navigateOptions: NavigationExtras | undefined = queryParams
188-
? {
189-
queryParams,
190-
}
191-
: undefined;
192-
193-
const doNavigate = () => {
194-
return navigateOptions ? router?.navigate([path], navigateOptions) : router?.navigate([path]);
195-
};
196-
197-
let result;
198-
199-
if (zone) {
200-
await zone.run(() => (result = doNavigate()));
201-
} else {
202-
result = doNavigate();
203-
}
204-
207+
const result = await _navigate(elementOrPath, basePath);
205208
detectChanges();
206-
return result ?? false;
209+
return result;
207210
};
208211

209212
return {

projects/testing-library/tests/integrations/ng-mocks.spec.ts

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { By } from '@angular/platform-browser';
33

44
import { MockComponent } from 'ng-mocks';
55
import { render } from '../../src/public_api';
6+
import { NgIf } from '@angular/common';
67

78
test('sends the correct value to the child input', async () => {
89
const utils = await render(TargetComponent, {
@@ -34,6 +35,7 @@ test('sends the correct value to the child input 2', async () => {
3435
selector: 'atl-child',
3536
template: 'child',
3637
standalone: true,
38+
imports: [NgIf],
3739
})
3840
class ChildComponent {
3941
@ContentChild('something')

projects/testing-library/tests/render.spec.ts

+27-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ import {
1414
import { NoopAnimationsModule, BrowserAnimationsModule } from '@angular/platform-browser/animations';
1515
import { TestBed } from '@angular/core/testing';
1616
import { render, fireEvent, screen } from '../src/public_api';
17-
import { Resolve, RouterModule } from '@angular/router';
17+
import { ActivatedRoute, Resolve, RouterModule } from '@angular/router';
18+
import { map } from 'rxjs';
19+
import { AsyncPipe, NgIf } from '@angular/common';
1820

1921
@Component({
2022
selector: 'atl-fixture',
@@ -365,6 +367,30 @@ describe('initialRoute', () => {
365367
expect(screen.queryByText('Secondary Component')).not.toBeInTheDocument();
366368
expect(screen.getByText('button')).toBeInTheDocument();
367369
});
370+
371+
it('allows initially rendering a specific route with query parameters', async () => {
372+
@Component({
373+
standalone: true,
374+
selector: 'atl-query-param-fixture',
375+
template: `<p>paramPresent$: {{ paramPresent$ | async }}</p>`,
376+
imports: [NgIf, AsyncPipe],
377+
})
378+
class QueryParamFixtureComponent {
379+
constructor(public route: ActivatedRoute) {}
380+
381+
paramPresent$ = this.route.queryParams.pipe(map((queryParams) => (queryParams?.param ? 'present' : 'missing')));
382+
}
383+
384+
const initialRoute = 'initial-route?param=query';
385+
const routes = [{ path: 'initial-route', component: QueryParamFixtureComponent }];
386+
387+
await render(RouterFixtureComponent, {
388+
initialRoute,
389+
routes,
390+
});
391+
392+
expect(screen.getByText(/present/i)).toBeVisible();
393+
});
368394
});
369395

370396
describe('configureTestBed', () => {

0 commit comments

Comments
 (0)