Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement request hooks #1341

Closed
inikulin opened this issue Mar 22, 2017 · 18 comments
Closed

Implement request hooks #1341

inikulin opened this issue Mar 22, 2017 · 18 comments
Assignees
Labels
AREA: server STATE: Auto-locked An issue has been automatically locked by the Lock bot. SYSTEM: API SYSTEM: hammerhead TYPE: enhancement The accepted proposal for future implementation.
Milestone

Comments

@inikulin
Copy link
Contributor

Are you requesting a feature or reporting a bug?

feature

What is the current behavior?

You can't intercept HTTP requests

What is the expected behavior?

Add API to intercept HTTP requests:

  • export RequestHook abstract class from which implementations could inherit;
  • add t.addRequestHook, test.useRequestHook and fixture.useRequestHook methods.
  • add t.removeRequestHook methods

Having this mechanism we can implement RequestLogger (#1270) and RequestMock (#1271)

@oneillci
Copy link

oneillci commented Sep 7, 2017

Just want to add my use case for this feature.

I'm currently building a SPA that has a .net web api backend.
TestCafe seems to be awesome for functional UI testing, but we have another type of testing that we perform: API testing in a regression environment.

At the moment we're trialling using SoapUI for this kind of testing but are finding it a little clunky.
Our entire application requires a user to be authenticated.
Testing the API which requires authentication can be achieved in SoapUI, but we have to jump through some hoops by:

  1. Actually requesting the login page as we use request verification tokens
  2. Posting the form back
  3. Capturing the auth cookie
  4. Attach the auth cookie to each subsequent API call

All of this is trivial to achieve in TestCafe as it's actually running in the browser.

In our regression environment we then call our API and compare the results to a saved json file. As it's a financial application, some of the saved results can be quite large and property level testing is not appropriate - we usually just want to ensure that the entire response is the same as the previous test run.

I'd like to use TestCafe to login, navigate to a report screen, fill out the report parameters, run the report and then intercept the xhr request for the results - this might be an xhr request that I'd like to wait several minutes for.
I'd then like to do jest-style snapshot testing of the json results i.e.

t.expect(jsonResults).toMatchSnapshot();

I'd be happy taking a dependency on another library (think Ava rely on Jest to do a similar assertion) to do that snapshot.

If I could do this in TestCafe, then I'd have one coherent strategy and framework for all functional UI and API testing which would be 💯

@AlexanderMoskovkin AlexanderMoskovkin modified the milestones: Sprint #9, Planned Sep 20, 2017
@oneillci
Copy link

Is there any movement on this issue? Looks like it's planned for development. Any timeframe on this?

@AlexanderMoskovkin
Copy link
Contributor

Hi @oneillci,

Thanks for your interest.
We've done some preparations for this feature in our proxy and we plan implement this in the next release iteration (after the current one). It can take about a couple of months.

@camsjams
Copy link

camsjams commented Feb 6, 2018

Would love to use this feature too!

@miherlosev
Copy link
Collaborator

miherlosev commented Mar 15, 2018

Request Hook

API for adding/removing

  • fixture.requestHooks(...)
  • test.requestHooks(...)
  • t.addRequestHooks(...), t.removeRequestHooks(...)

where ... is rest parameter. Also passed array will be flatten to plain list.

Examples of usage

fixture.requestHooks(hook1);
test.requestHooks(hook1, hook2, ...);
fixture.requestHooks([hook1, hook2], hook3);
test.requestHooks([hook1, hook2], hook3, [hook4, hook5]);

Exporting classes

  • RequestLogger - allows to collect http requests by specified rules (see RequestFilterRule section).
  • RequestMock - allows to return test responses for specified http requests
  • RequestHook - the base class, allows to implement a custom logic to intercept http requests.

RequestLogger

RequestLogger(filter, logOptions)

filter - rule for filtering http requests (see RequestFilterRule description below)

logOptions - determinate which parts of request should collect.

Default values for logOptions:

{
    logRequestHeaders: false,
    logRequestBody: false,
    stringifyRequestBody: false,
    logResponseHeaders: false,
    logResponseBody: false,
    stringifyResponseBody: false
}

i.e. by default the RequestLogger collects only url, statusCode, userAgent, sessionId(internal) request properties.

API

Methods

Name Returned type Description
async contains(predicate) Promise Finds requests by predicate and returns true/false depending on result
async count(predicate) Promise Finds requests by predicate and returns their count.
clear () None Clears collected requests

Properties

Name Type Description
requests Array returns collected requests as Array

For async function the build-in Smart Assertion Query mechanism will be applied.

Example

import { RequestLogger } from 'testcafe';

const logger = RequestLogger('https://example.com');

fixture `test`
    .page('https://example.com');

test
    .requestHooks(logger)
    ('test', async t => {
        await t.expect(logger.contains(r => r.response.statusCode === 200)).ok();
    });

Request Mock

API

We build our API depending on nock library.

RequestMock configures with pairs of methods .onRequestTo(requestFilterRule).respond(responseMock).

Example

var mock = RequestMock()
            .onRequestTo(requestFilterRule1)
            .respond(responseMock1)
            .onRequestTo(requestFilterRule2)
            .respond(responseMock2)

where information about RequestFilterRule see below, ResponseMock - allows to construct response using various arguments.

Example

import { RequestMock } from 'testcafe';

const requestMock = RequestMock()
    .onRequestTo('http://external-service.com/api/users'}) /*see RequestFilterRule*/
    .respond({data: 123}) /* JSON response */
    .onRequestTo(...)
    .respond('The error is occured!!!') /*HTML response*/
    .onRequestTo (...)
    .respond(null, 204) /*custom statusCode*/
    .onRequestTo(...)
    .respond('<html_markup>', 200, { 'server': 'nginx/1.10.3' }) /* custom headers */
    .onRequestTo(...)
    .respond(function (req, res){ /* respond function */		  
          res.headers[‘x-calculated-header’] = ‘calculated-value’;
          res.statusCode = ‘200’;
          
          const responseBody = fs.readFileSync(req.params['filename']).toString();
          res.setBody(responseBody);
    });


fixture `Fixture`
    .page(‘http://example.com/’)
    .rquestHooks(requestMock);

test('test', () => {
    await t.click(‘body’);
        
})

RequestFilterRule

Allows to specify request filtering rule for requests.
It's a public term that implemented with an internal class.
In public API we will use a simple object construction syntax.

Examples

/*String*/
'http://example.com' -> RequestFilterRule(`'http://example.com'`) 

/*Regular expession*/
/example.com/ -> RequestFilterRule(`/example.com/`) 

/*Object with properties*/
{ url: 'http://example.com', method: 'GET', isAjax: false } -> RequestFilterRule(`{ url: 'http://example.com', method: 'GET', isAjax: false }`) 

 /*Custom function*/
RequestFilterRule(function (request) {
            return request.url === 'http://example.com' &&
                   request.method === 'post' &&
                   request.isAjax &&
                   request.body === '{ test: true }' &&
                   request.headers['content-type'] === 'application/json';
}

@curtisblackwell
Copy link

@miherlosev is this functionality available somewhere now, or is this just the planned interface?

@AlexanderMoskovkin
Copy link
Contributor

@curtisblackwell This is in progress now. @miherlosev already have created a pull request but it requires some time to finish it. If you are interested in this we'll be able to provide you with a dev build once this feature is merged

@Alxandr
Copy link

Alxandr commented Mar 19, 2018

This looks really promising. I have a question though. Will you be able to assert that a request-hook has been triggered?

@miherlosev
Copy link
Collaborator

Will you be able to assert that a request-hook has been triggered?

I am not sure understand your question.
For which case does it need?

@curtisblackwell
Copy link

@AlexanderMoskovkin understood, thank you. I'm interested, but it sounds like timing may be an issue.

@Alxandr
Copy link

Alxandr commented Mar 20, 2018

Thinking about it, it's probably not actually needed for integration tests.

@elgreco247
Copy link

Will you release a dev version so that we can try out the request hooks feature?

@miherlosev
Copy link
Collaborator

@elgreco247
Thank you for your interest in this feature.
 
The Request Hooks feature significantly affects the TestCafe subsystems. First, we will perform internal testing. After that a public development version will be released.

@zoejobson
Copy link

I notice that at least some of this feature is present in the alpha 2 build. Could you explain to me how to use it to achieve what I want to achieve.
https://testcafe-discuss.devexpress.com/t/testcafe-not-always-catching-requests-from-browser/818/4
that is:-
Our webapp gets a series of images when a report is run.
When I run the report via test cafe each image is GOT twice, once without the proxy wrapper and without our internal auth cookie, and once with the proxy wrapper (the cookie is not visible but it works anyway, so I'm assuming some sort of proxy magic). We need to be able to prevent testcafe from attempting to get these images without the correct authorisation. Because doing so sets off alarms in our data dog as they look like attacks. Without being able to do this we will be forced to throw away a few months of work and switch to a different technology.

@miherlosev
Copy link
Collaborator

Feature Request Hooks is ready for testing. Anyone can try this.

Before testing you need to install the latest alpha version - [email protected].
Final documentation is in progress, but you can already use this PR.

@miherlosev
Copy link
Collaborator

@miherlosev
Copy link
Collaborator

The TypeScript typings will be added in separate PR.

@lock
Copy link

lock bot commented Mar 28, 2019

This thread has been automatically locked since it is closed and there has not been any recent activity. Please open a new issue for related bugs or feature requests. We recommend you ask TestCafe API, usage and configuration inquiries on StackOverflow.

@lock lock bot added the STATE: Auto-locked An issue has been automatically locked by the Lock bot. label Mar 28, 2019
@lock lock bot locked as resolved and limited conversation to collaborators Mar 28, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
AREA: server STATE: Auto-locked An issue has been automatically locked by the Lock bot. SYSTEM: API SYSTEM: hammerhead TYPE: enhancement The accepted proposal for future implementation.
Projects
None yet
Development

No branches or pull requests