流畅的 PageModel API 与 TestCafe

fluent PageModel API with TestCafe

我试图在 TestCafe 中为测试作者提供流畅的 PageModel api,例如:

await MyApp // a Page Model class instance  
  .navigateTo(xyz) // clicks a button to navigate to a specific part in my app  
  .edit() // clicks the edit button  
  .setField(abc, 12.34)  
  .save()  
  .changeStatus('complete'); 

我将所有单独的方法作为异步方法工作,可以单独等待,但这使得代码非常不可读,因此容易出错。

但是,无论我试图使 api 流畅,都会导致以下错误:

Selector cannot implicitly resolve the test run in context of which it should be executed. If you need to call Selector from the Node.js API callback, pass the test controller manually via Selector's .with({ boundTestRun: t }) method first. Note that you cannot execute Selector outside the test code.

制作流畅的异步 api 的诀窍是恕我直言,从异步函数切换到常规函数作为方法,并让这些方法 return 成为可用的 'this' 值。并且为了防止await oscillating,'then'函数需要在调用一次时移除(然后在

时重新安装)

重现问题的一个非常基本的示例如下所示:

import { Selector } from 'testcafe'

class MyPage {
    queue: [];

    async asyncTest() {
        return await Selector(':focus').exists;
    }

    queuedTest() {
        this.then = (resolve, reject) => {
            delete this.then; // remove 'then' once thenable gets called to prevent endless loop

            // calling hardcoded method, in a fluent api would processes whatever is on the queue and then resolve with something
            resolve(this.asyncTest());
        };

        // In a real fluent api impl. there would be code here to put something into the queue 
        // to execute once the 'then' method gets called
        // ...

        return this;
    }
}

fixture `Demo`
    .page `https://google.com`;


test('demo', async () => {
  const myPage = new MyPage();

  console.log('BEFORE')
    await myPage.asyncTest();
    console.log('BETWEEN')
    await myPage.queuedTest(); // Here it bombs out
    console.log('AFTER')
});

请注意,上面的示例并未展示流畅的 api,它只是演示了通过 'then' 函数调用使用选择器的方法(恕我直言,这是创建流畅的 api) 导致上述错误。

注意:我知道错误的含义,建议是将 .with({boundTestRun: t}) 添加到选择器,但这会导致需要样板代码并使事情更难维护。

任何想法表示赞赏 P.

在您的示例中,无法评估选择器,因为它无权访问测试控制器 (t)。您可以尝试避免在没有断言的情况下直接评估选择器。

这是我的链式页面模型示例(基于这篇文章:Async Method Chaining in Node):

页面模型:

import { Selector, t } from 'testcafe';

export class MyPage {
    constructor () {
        this.queue = Promise.resolve();

        this.developerName = Selector('#developer-name');
        this.submitButton  = Selector('#submit-button');
        this.articleHeader = Selector('#article-header');
    }

    _chain (callback) {
        this.queue = this.queue.then(callback);

        return this;
    }

    then (callback) {
        return callback(this.queue);
    }

    navigateTo (url) { 
        return this._chain(async () => await t.navigateTo(url));
    }

    typeName (name) { 
        return this._chain(async () => await t.typeText(this.developerName, name));
    }

    submit () {
        return this._chain(async () => await t.click(this.submitButton));
    }

    checkName (name) {
        return this._chain(async () => await t.expect(this.articleHeader.textContent).contains(name));
    }

    getHeader () {
        this._chain(async () => console.log(await this.articleHeader.textContent));

        return this;
    }
}

测试:

import { MyPage } from "./page-model";

fixture`Page Model Tests`;

const page = new MyPage();

test('Test 1', async () => {
    await page
        .navigateTo('http://devexpress.github.io/testcafe/example/')
        .typeName('John')
        .submit()
        .checkName('John')
        .getHeader();
});