使用 Cypress 测试 Angular 时如何防止整页重新加载?
How to prevent full page reload when testing Angular with Cypress?
当我为我的 Angular 应用程序编写我的 Cypress e2e 测试时,我经常使用这样的 visit() 命令:
.visit('/foo/bar')
这就完成了工作,即 Cypress 导航到 /foo/bar
,但整个应用程序会重新加载。这非常慢,并且没有模仿实际的用户行为。
是否可以 navigate/visit Angular 应用程序而不重新加载整个页面?
我试过了:
cy.window().then((win) => {
win.history.pushState({}, '', '/foo/bar')
})
但是angular对此没有反应。
我通过添加一个调用 Angular 应用程序 app.component.ts
上的方法的自定义 cypress 命令解决了这个问题。解决方案看起来像这样更新到 Ivy:
app.component.ts
export class AppComponent {
constructor(
private router: Router,
private ngZone: NgZone,
) {}
// Method Cypress will call
public navigateByUrl(url: string) {
this.ngZone.run(() => {
this.router.navigateByUrl(url);
});
}
}
cypress/support/commands.ts
// add new command to the existing Cypress interface
declare global {
namespace Cypress {
interface Chainable {
visitAngular: (url: string) => Chainable<Window>;
}
}
}
// Custom function
export function visitAngular(url: string) {
cy.get('body').then($body => {
try {
const el = $body.find('app-root')[0];
const win = el.ownerDocument.defaultView;
const componentInstance = win.ng.getComponent(el);
cy.log(`Angular nav to '${url}' `);
componentInstance.navigateByUrl(url);
cy.url().should('contain', url);
} catch (error) {
cy.log(`Cypress nav to '${url}' `);
cy.visit(url);
}
});
}
Cypress.Commands.add('visitAngular', visitAngular);
cypress/support/index.d.ts
interface Window {
ng: {
getComponent: (element: any) => any;
};
}
我们已经使用它 2 个月了,它在本地开发中效果很好,使用 x3 加快了测试执行速度。但在 CI 中是另一回事了。
你可以让它在 CI 环境中工作,注册一个全局函数调用,而不是 angular 组件:
app.component.ts
export class AppComponent {
constructor(
private router: Router,
private ngZone: NgZone,
) {
// Method Cypress will call
if ((window as any).Cypress) {
(window as any).cypressNavigateByUrl = (url: string) => this.cypressNavigateByUrl(url);
}
}
public cypressNavigateByUrl(url: string) {
this.ngZone.run(() => {
this.router.navigateByUrl(url);
});
}
}
cypress/support/commands.ts
Cypress.Commands.add('visitAngular', (url) => {
cy.window().then((win) => {
win.cypressNavigateByUrl(url);
});
});
如果您使用基于散列的路由,您可以手动操作 URL:
cy.window().then(win => win.location.hash = "/foo/bar")
当我为我的 Angular 应用程序编写我的 Cypress e2e 测试时,我经常使用这样的 visit() 命令:
.visit('/foo/bar')
这就完成了工作,即 Cypress 导航到 /foo/bar
,但整个应用程序会重新加载。这非常慢,并且没有模仿实际的用户行为。
是否可以 navigate/visit Angular 应用程序而不重新加载整个页面?
我试过了:
cy.window().then((win) => {
win.history.pushState({}, '', '/foo/bar')
})
但是angular对此没有反应。
我通过添加一个调用 Angular 应用程序 app.component.ts
上的方法的自定义 cypress 命令解决了这个问题。解决方案看起来像这样更新到 Ivy:
app.component.ts
export class AppComponent {
constructor(
private router: Router,
private ngZone: NgZone,
) {}
// Method Cypress will call
public navigateByUrl(url: string) {
this.ngZone.run(() => {
this.router.navigateByUrl(url);
});
}
}
cypress/support/commands.ts
// add new command to the existing Cypress interface
declare global {
namespace Cypress {
interface Chainable {
visitAngular: (url: string) => Chainable<Window>;
}
}
}
// Custom function
export function visitAngular(url: string) {
cy.get('body').then($body => {
try {
const el = $body.find('app-root')[0];
const win = el.ownerDocument.defaultView;
const componentInstance = win.ng.getComponent(el);
cy.log(`Angular nav to '${url}' `);
componentInstance.navigateByUrl(url);
cy.url().should('contain', url);
} catch (error) {
cy.log(`Cypress nav to '${url}' `);
cy.visit(url);
}
});
}
Cypress.Commands.add('visitAngular', visitAngular);
cypress/support/index.d.ts
interface Window {
ng: {
getComponent: (element: any) => any;
};
}
我们已经使用它 2 个月了,它在本地开发中效果很好,使用 x3 加快了测试执行速度。但在 CI 中是另一回事了。
你可以让它在 CI 环境中工作,注册一个全局函数调用,而不是 angular 组件:
app.component.ts
export class AppComponent {
constructor(
private router: Router,
private ngZone: NgZone,
) {
// Method Cypress will call
if ((window as any).Cypress) {
(window as any).cypressNavigateByUrl = (url: string) => this.cypressNavigateByUrl(url);
}
}
public cypressNavigateByUrl(url: string) {
this.ngZone.run(() => {
this.router.navigateByUrl(url);
});
}
}
cypress/support/commands.ts
Cypress.Commands.add('visitAngular', (url) => {
cy.window().then((win) => {
win.cypressNavigateByUrl(url);
});
});
如果您使用基于散列的路由,您可以手动操作 URL:
cy.window().then(win => win.location.hash = "/foo/bar")