【问题标题】:How do you reliably wait for page idle in cypress.io test您如何可靠地等待 cypress.io 测试中的页面空闲
【发布时间】:2018-05-25 08:48:53
【问题描述】:

当使用 cypress.io 测试有角度的网页时,检测页面何时完全加载和空闲的最佳/最可靠的方法是什么。不仅仅是 onload 事件。需要包括所有 XHR 请求、角度摘要周期完成和所有渲染完成,包括所有动画完成。

原因是此时我想测试页面不包含元素,并且在上述所有内容完全完成之前无法测试。

【问题讨论】:

  • this work?
  • 感谢您的输入 Dwelle。这适用于上述某些情况,但我不太高兴仅仅为了测试工作而更改代码并且在生产中剥离此代码意味着我们没有测试生产中的确切代码。我们的应用程序有数千个控制器、指令和服务,我正在寻找一种方法来检测页面何时空闲。无论如何,谢谢。

标签: angular testing web cypress


【解决方案1】:

我们为此做的记录是添加一个新的waitforpageidle 命令,可以像这样使用:

cy.waitforpageidle();

打字稿如下:

Cypress.Commands.add(
    "waitforpageidle",
    () => {
        console.warn("Waiting for page idle state");

        const pageIdleDetector = new PageIdleDetector();

        pageIdleDetector.WaitForPageToBeIdle();
    }
);

其中PageIdleDetector如下:

export class PageIdleDetector
{   
    defaultOptions: Object = { timeout: 60000 };

    public WaitForPageToBeIdle(): void
    {
        this.WaitForPageToLoad();
        this.WaitForAngularRequestsToComplete();
        this.WaitForAngularDigestCycleToComplete();
        this.WaitForAnimationsToStop();
    }

    public WaitForPageToLoad(options: Object = this.defaultOptions): void
    {
        cy.document(options).should((myDocument: any) =>
        {
            expect(myDocument.readyState, "WaitForPageToLoad").to.be.oneOf(["interactive", "complete"]);
        });
    }

    public WaitForAngularRequestsToComplete(options: Object = this.defaultOptions): void
    {
        cy.window(options).should((myWindow: any) =>
        {
            if (!!myWindow.angular)
            {
                expect(this.NumberOfPendingAngularRequests(myWindow), "WaitForAngularRequestsToComplete").to.have.length(0);
            }
        });
    }

    public WaitForAngularDigestCycleToComplete(options: Object = this.defaultOptions): void
    {
        cy.window(options).should((myWindow: any) =>
        {
            if (!!myWindow.angular)
            {
                expect(this.AngularRootScopePhase(myWindow), "WaitForAngularDigestCycleToComplete").to.be.null;
            }
        });
    }

    public WaitForAnimationsToStop(options: Object = this.defaultOptions): void
    {
        cy.get(":animated", options).should("not.exist");
    }

    private getInjector(myWindow: any)
    {
        return myWindow.angular.element(myWindow.document.body).injector();
    }

    private NumberOfPendingAngularRequests(myWindow: any)
    {
        return this.getInjector(myWindow).get('$http').pendingRequests;
    }

    private AngularRootScopePhase(myWindow: any)
    {
        return this.getInjector(myWindow).get("$rootScope").$$phase;
    }
}

【讨论】:

  • 非常感谢!你的PageIdleDetector 太棒了,正是我们所需要的
  • 看起来不错,但这不是 AngularJS(而不是 Angular 2+)的代码
【解决方案2】:

您可以让赛普拉斯等待任何请求完成后再继续。因此,如果您想等待某个页面的所有 XHR,您可以为每个页面执行以下操作。等待多长时间由responseTimeout configuration 定义。

cy.server();
cy.route('**/api/getData').as('getData');
cy.visit('/home');
cy.wait('@getData');

或者等待几条路线:

cy.server();
cy.route('**/api/getData').as('getDataX');
cy.route('**/api/getData').as('getDataY');

cy.visit('/home');
cy.wait(['@getDataX', '@getDataY']);

Cypress best practices: Unnecessary-Waiting.

Cypress docs on wait Alias.

【讨论】:

  • 当你有几十个请求发出时,这没有用,最后一个是你想要等待的。
  • 您可以等待几条路线。检查更新的答案。
  • 可以理解,也还是不是很有用。我最近一直在解决这个问题,开发人员在检测到应用程序的缓存未设置时让应用程序执行批处理请求。如果我要添加足够多的路由/等待来匹配在这种情况下发出的请求数量,有两件事会出错: 1. 测试会非常脆弱。 2.等待的目的是等到应用程序完成加载并准备好。如果在那段时间内请求失败,等待也将失败。我们的解决方法是公开一个加载标志以供 cypress 使用。
  • 也许测试会很脆弱,因为应用程序也很脆弱?这类问题可能就像“代码异味”,这意味着它可能是应用架构可以改进的迹象。
  • 这是一个公平的说法。我的场景确实涉及一个脆弱的(遗留)应用程序,其中进行更改通常涉及办公室战争。这不是最佳的,但你必须尽你所能到达好地方。我很想和你进一步讨论这个问题,虽然也许......在元频道上?我不知道它是如何工作的。
【解决方案3】:

对于 Angular (2+)

前面的答案与 AngularJS 有关。对于 Angular 2+,您可以使用其他解决方案。

免责声明:在大多数情况下,Cypress 等待机制就足够了。但如果您确实需要应用程序稳定(即:所有请求和异步任务(如去抖动)都已完成),您可以使用此命令。

  • 在您的 src/main.ts 中,将以下快捷方式添加到窗口对象:
    /**
     * Used by Cypress.waitForAngularIdle() (in commands.ts) to wait for all Pending request to finish
     */
    function ngIsStable() {
      const test = (window as any).getAngularTestability(window.document.querySelector('body div')) as Testability // you need to select an element managed by Angular. Usually any div in your html body

      const stable = test.isStable()
      //if (!stable) {   // this console.log block is just for information, you can remove it
      console.log('Angular is ' + (stable ? '' : 'NOT ') + 'stable:', {
        pendingRequestCount: test.getPendingRequestCount(),
        _pendingCount: (test as any)._pendingCount,
        pendingTasks: (test as any).getPendingTasks(),
      })
      // }
      return stable
    }

    window['ngIsStable'] = ngIsStable;
  • 在您的赛普拉斯 commands.ts 文件中:
    function waitForAngularIdle() { 
        cy.window().invoke('ngIsStable').should('be.true')
    }
    Cypress.Commands.add('waitForAngularIdle', waitForAngularIdle);
  • 在您的赛普拉斯索引文件 index.d.ts(用于 typescript 编译)中
    declare namespace Cypress {
      interface Chainable<Subject = any> {
        waitForAngularIdle()
      }
    }
  • 在赛普拉斯规范中,当您需要赛普拉斯等待 Angular 应用程序稳定时:
    cy.waitForAngularIdle()

【讨论】:

    猜你喜欢
    • 2018-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多