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

Intercept Network calls #98

Closed
saikrishna321 opened this Issue Aug 7, 2018 · 13 comments

Comments

5 participants
@saikrishna321

saikrishna321 commented Aug 7, 2018

@zabil - In puppeteer, we can intercept API calls, continue or abort network request/response.

Can this be implemented in taiko ?

Code in puppeteer

async function automateBrowser({port}) {
	const browser = await puppeteer.launch({
		headless: false
	});

	const page = await browser.newPage();
	await page.setRequestInterceptionEnabled(true);

	page.on('request', request => {
		const URLToIntercept = 'https://forms.hubspot.com/embed/v3/form/2059467/2e1a1b5b-27bb-447d-aac4-0b87c1e88fec?callback=hs_reqwest_0&hutk=';

		if (request.url === URLToIntercept) {
			request.continue({
				url: `http://127.0.0.1:${port}/form.txt`
			});
		} else {
			request.continue();
		}
	});

	await page.goto('https://www.sitepen.com/site/contact.html');

	await browser.close();
	process.exit();
}
@zabil

This comment has been minimized.

Member

zabil commented Aug 8, 2018

It's possible right now.
Taiko exposes the CRI client with the Network object.

However it'll require some digging into CRI's docs.

To understand this better, intercepting requests to provide test data? Maybe add an easier API something like

intercept("https://example.com", "http://proxy.com")

and for finer control

intercept("https://example.com", request => {})
@saikrishna321

This comment has been minimized.

saikrishna321 commented Aug 8, 2018

intercepting requests to provide test data?

Yeah, we can mock any api call with test data.

https://github.com/cyrus-and/chrome-remote-interface/wiki/Intercept-and-block-requests-by-URL

@NivedhaSenthil

This comment has been minimized.

Member

NivedhaSenthil commented Aug 22, 2018

WIP

Example1:

intercept({ 
        request: {url:'https://taiko.gauge.org/assets/css/pilcrow.css'},
        mock: {errorReason:"Failed"}
})

Example2:

intercept({ 
        request: {url:'https://taiko.gauge.org/assets/css/pilcrow.css'},
        mock: {response:{status: 404, contentType: 'text/plain',body: 'Not Found!'}}
})

Example3:

intercept({ 
        request: {url:'https://taiko.gauge.org/assets/css/pilcrow.css'},
        callback: doOnIntercept
})

intercept api will take an object with request to intercept and action to be done on intercepted request.

  • request can be matched using any one from (url, post-data, method, resourceType, headers)
  • mock can be used to do various things like below
    • override request details like url, method, post-data, headers
      eg: mock:{ url: 'http://proxy', method: 'GET' }
    • modify response details like status, header, content-type, body
      eg: mock: {response:{status: 404, contentType: 'text/plain',body: 'Not Found!'}}
    • abort an request
      eg: mock: {errorReason:"Failed"}
  • callback can be used to do any action after interception
@nehashri

This comment has been minimized.

Contributor

nehashri commented Aug 22, 2018

A small change here would be useful,
The callback should run when the request is intercepted. It should take the request as its parameters and return a mock response object.
Example

intercept({ 
        request: {url:'https://taiko.gauge.org/assets/css/pilcrow.css'},
        callback: function doOnIntercept(request) {
                      // do something
                      return mockResponse;
                 }
})

This gives the user ability to intercept the request, process any data it may contain and return a mock response dynamically.

@zabil

This comment has been minimized.

Member

zabil commented Aug 22, 2018

For first pass we must make this very simple

If we approach this from a testing perspective

Case 1: Mock the requested url with a test data url

intercept("foo", "bar")
  • foo can be a url pattern or regex.
  • The test data url bar must get all the headers and data of foo

Case 2: Return JSON

intercept("foo", {bar: "baz"})

Case 3: Return the result of a function

intercept("foo", (request) => return "baz");
  • the request object must have details of the data, original url, headers
@saikrishna321

This comment has been minimized.

saikrishna321 commented Aug 29, 2018

@zabil i like the First approach 👍

Can i know which approach has the team decided to go with ?

@NivedhaSenthil

This comment has been minimized.

Member

NivedhaSenthil commented Sep 6, 2018

@zabil if we match only using url user may not have flexibility to identify any request with specific method, header, or post-data.

can it be like intercept(requestObject, handler) where handler can be a mock object or callback which will return a mock object.

eg: intercept({url:'http://something.com',method:'get'}, (request) => return 'baz');

@zabil

This comment has been minimized.

Member

zabil commented Sep 6, 2018

Because the application implements the request method, setting it is redundant so {url:'http://something.com',method:'get'} just be http://something.com and there probably won't be a case where GET has to change to POST and vice versa.

For headers they can be read/modified from

(request) => return 'baz'
@NivedhaSenthil

This comment has been minimized.

Member

NivedhaSenthil commented Sep 10, 2018

We will be following below design for intercepting APIs

case 1: block url => intercept(url)
case 2: mockResponse => intercept(url,{mockObject})
case 3: override request => intercept(url,(request) => {request.continue({overrideObject})})
case 4: redirect => intercept(url,redirectUrl)
case 5: mockResponse based on request =>
intercept(url,(request) => { //someaction; request.respond({mockResponseObject})} )

@NivedhaSenthil

This comment has been minimized.

Member

NivedhaSenthil commented Sep 10, 2018

Regex to match URLs will be handle in #142

@NivedhaSenthil

This comment has been minimized.

Member

NivedhaSenthil commented Sep 11, 2018

Fix should be available in 7e4fdc3.

Below is an example script for all 5 cases mentioned in the issue. Steps to run the sample script,

#code.js

const assert = require("assert");
(async () => {
    try {
        await openBrowser({headless:false});
        
        //Case 1: Block URL (Blocks all request for people.png)
        await intercept("http://localhost:3000/assets/images/people.png");

        //Case 2: Mock response (Mocks response to modify address from "888 Central St." to "12345 Central St." )
        await intercept("http://localhost:3000/api/customers/11",{body: '{"id":11,"firstName":"ward","lastName":"bell","gender":"male","address":"12345 Central St.","city":"Los Angeles","state":{"abbreviation":"CA","name":"California"},"latitude":34.042552,"longitude":-118.266429}'});

        //Case 3: Override request (Overrides google4_hdpi.png url to return female.png can be seen in map)
        await intercept("https://maps.gstatic.com/mapfiles/api-3/images/google4_hdpi.png",(request) => {request.continue({url:"http://localhost:3000/assets/images/female.png"})})
        
        //Case 4: Redirect URL (Overrides male.png to female.png)
        await intercept("http://localhost:3000/assets/images/male.png","http://localhost:3000/assets/images/female.png")
        
        //Case 5: Mock response based on request (Mocks response to modify address from "1234 Anywhere St." to "888 Anywhere St.")
        await intercept("http://localhost:3000/api/customers/1", (request) => {request.respond({body:'{"id":1,"firstName":"ted","lastName":"james","gender":"male","address":"888 Anywhere St.","city":" Phoenix ","state":{"abbreviation":"AZ","name":"Arizona"},"orders":[{"productName":"Basketball","itemCost":7.99},{"productName":"Shoes","itemCost":199.99}],"latitude":33.299,"longitude":-111.963}'})});
        
        await goto("localhost:3000");
        await click(link("2"));
        await click(link("Ward Bell"));
        assert.equal("Exists",(await contains("12345 Central St.").exists()).description);
        await click(link("Customers"));
        await click(link("Ted James"));
        assert.equal("Exists",(await contains("888 Anywhere St.").exists()).description);
    
    } catch (e) {
        console.error(e);
    } finally {
        await closeBrowser();
    }
})();
@sguptatw

This comment has been minimized.

sguptatw commented Sep 12, 2018

Getting Error: ENOENT: no such file or directory, stat '.../Angular-JumpStart/dist/index.html' on npm install && npm start

@NivedhaSenthil

This comment has been minimized.

Member

NivedhaSenthil commented Sep 12, 2018

Have missed ng build step in between, try doing it before npm start

@sguptatw sguptatw closed this Sep 14, 2018

@sguptatw sguptatw removed the ready for QA label Sep 14, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment