3

We want to automate testing for our analytics calls. I am currently evaluation front end test tools, that could be used for this. I found TestCafe Studio to be what we would need to create the tests, but first I need some proof of concept. Thus I try to code a simple test case first. But I struggle with what seems to be the most basic. I want to assert that some parameters in a request have a certain value. So what I did is to create a RequestHook according to the documentation: https://devexpress.github.io/testcafe/documentation/test-api/intercepting-http-requests/creating-a-custom-http-request-hook.html (what they omit is that you need to export your class if you place it in a separate file...) I have two problems now:

  1. How can I wait for this request to be executed? The await t is only for the page I am requesting, but the analytics call is then executed later.
  2. How do I provide the test with some data collected in the hook?

Here is how far I have gotten (I got the URL in the console):

import { RequestHook} from 'testcafe';

export class AnalyticsRequestHook extends RequestHook {
    constructor (requestFilterRules, responseEventConfigureOpts) {
        super(requestFilterRules, responseEventConfigureOpts);
    }
    async onRequest (event) {
        console.log(event.requestOptions.url);
    }
    async onResponse (event) {
    }
}

I then instantiate this class with:

const analyticsRequestHook = new AnalyticsRequestHook(/https:\/\/trackinghost.com\//);

In some other examples, they just import t as well and you should have access to it in the methods. But this seems not to work for RequestHooks, as I get the following error as soon as I try to access t:

Cannot implicitly resolve the test run in the context of which the test controller action should be executed. Use test function's 't' argument instead.

But in onRequest I can't pass other arguments.

So are my two questions even possible, if yes, please provide an example, as I am really a complete newbie with testcafe.

Alex Skorkin
  • 4,264
  • 3
  • 25
  • 47
Thomas
  • 6,325
  • 4
  • 30
  • 65

2 Answers2

2

what they omit is that you need to export your class if you place it in a separate file...

It's not related to the TestCafe framework. It's part of the JavaScript standard (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export).

How can I wait for this request to be executed? The await t is only for the page I am requesting, but the analytics call is then executed later. How do I provide the test with some data collected in the hook?

Use the way from described in the example from this documentation topic - https://devexpress.github.io/testcafe/documentation/test-api/intercepting-http-requests/logging-http-requests.html

fixture `Fixture`;

class AnalyticsRequestHook extends RequestHook {
    constructor (requestFilterRules, responseEventConfigureOpts) {
        super(requestFilterRules, responseEventConfigureOpts);
    }
    async onRequest (event) {
        console.log(event.requestOptions.url);
    }
    async onResponse (event) {
    }
}

const analyticsRequestHook = new AnalyticsRequestHook(/https:\/\/trackinghost.com\//);

test
    .requestHooks(analyticsRequestHook)
    ('test', async t => {
        // Ensure that the response has been received and that its status code is 200.
        await t.expect(analyticsRequestHook.contains(record => record.response.statusCode === 200)).ok();

        const logRecord = analyticsRequestHook.requests[0];

        await t.expect(logRecord.request.url).eql('expected value');
    });

Cannot implicitly resolve the test run in the context of which the test controller action should be executed. Use test function's 't' argument instead.

Could you please provide the test code that raises this error message?

mlosev
  • 5,130
  • 1
  • 19
  • 31
  • Thanks for the post, but it doesn't answer my questions. RequestLogger is different from a custom RequestHook. I had a look at the code of the former and it uses a ton of internal helpers which I don't know how to include in my custom code as they are not really exported. And regarding the error it happens if I add t.debug() or t.ctx in the onRequest method. t is unknown in this context. – Thomas Jul 01 '19 at 15:01
  • 1
    If you don't need specific/custom logic in your request hook, you can use the built-in [`RequestLogger`](https://devexpress.github.io/testcafe/documentation/test-api/intercepting-http-requests/logging-http-requests.html) hook that can wait for requests to be executed. You can see how to use `RequsetLogger` in the example above and in TestCafe Documentation. – Helen Dikareva Jul 02 '19 at 12:28
  • If you do need specific/custom logic in your hook, please provide us with more details about your test case: - your testing page - code of your hook and test - what logic would you like to implement in your custom hook? – Helen Dikareva Jul 02 '19 at 12:28
  • You can't use TestCafe actions in a request hook's code. To debug the hook, take a look at [this recipe](https://devexpress.github.io/testcafe/documentation/recipes/debug-tests/chrome-dev-tools.html). If you want to store some value, you can use a variable instead of `t.ctx`. If this functionality doesn't suit your needs, please let us know. – Helen Dikareva Jul 02 '19 at 12:29
  • @HelenDikareva Trying to address the multiple comments: 1. I think it doesn't make a difference if I use RequestLogger or RequestHook. As the request is independent from the page load it didn't always wait for this additional request. Maybe I did something wrong there though. 2. Have it wrapped in a custom hook makes it easier to reuse, but maybe I could also achieve this with a helper function/class. 3. In a custom RequestHook I don't have access to t and thus also t.ctx. See the error I posted in my question. – Thomas Jul 02 '19 at 12:48
  • Ok, I think I found the problem I had with the `RequestLogger`, but still the code posted in this answer does not work, as `RequestHook` does not provide `contains` and `get requests`. If you change it to a simple `RequestLogger` it is closer to a correct answer. – Thomas Jul 02 '19 at 13:46
  • @HelenDikareva Still, as I said, RequestHook doesn't offer these methods used in the example. They are part of the RequestLogger API and with that I seem to have got it working. I just can't accept an answer that doesn't work and I wanted to give mlosev the chance to change the answer to get rep. Else I can post my found solution as an answer. – Thomas Jul 03 '19 at 07:55
  • Thomas, Feel free to share your solution with everyone. – Alex Skorkin Jul 04 '19 at 06:57
1

After a day of try and error I found a solution based on the comments of the other answer. I was simply using the RequestLogger wrong as I tried to do my asserts in the first expect. So, the documentation is almost right. Here is my test case now:

import { AnalyticsUrlParser } from './AnalyticsUrlParser.js';

const analyticsLogger = RequestLogger(/https:\/\/trackingdomain.com\//);

fixture('Analytics Test')
    .page('https://testdomain.com/en.html')
    .requestHooks(analyticsLogger);

test('Page load', async t => {
    await t.expect(analyticsLogger.count(record => record.response.statusCode === 200)).eql(1);

    const urlParser = new AnalyticsUrlParser(analyticsLogger.requests[0].request);
    const messages = urlParser.assertAll({
        pageName: 'Homepage English',
        events: ['event1'],
    });
    await t.expect(messages.length === 0).ok(messages.join('\n'));
}

The AnalyticsUrlParser is my own class that parses the query parameter and with the assertAll checks if certain parameters match the value provided. If not it adds to an array of messages which are then printed out.

Thomas
  • 6,325
  • 4
  • 30
  • 65