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

Fill textarea with response from backend #109

Merged
merged 30 commits into from
May 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
4b598db
Setup redux-thunk, actions to fetch response on button click
amitmbee Apr 24, 2018
4efb204
Define action creators and reducer for data fetch success and failure
amitmbee Apr 24, 2018
5464d12
Modify reducer to store response data for each tabId and each request
amitmbee Apr 24, 2018
c3e3663
Use aliases, requestId instead of tabId
amitmbee Apr 30, 2018
2bcba47
Setup redux-thunk, actions to fetch response on button click
amitmbee Apr 24, 2018
b130936
Define action creators and reducer for data fetch success and failure
amitmbee Apr 24, 2018
52391ef
Modify reducer to store response data for each tabId and each request
amitmbee Apr 24, 2018
24c909a
Use aliases, requestId instead of tabId
amitmbee Apr 30, 2018
9910bec
Add key attr to force re-render of textarea after seting defaultValue…
amitmbee May 2, 2018
7c729be
Add key attr to force re-render of textarea after seting defaultValue…
amitmbee May 2, 2018
18a3836
Send axios request to fetch response with request-headers, Fix fetch …
amitmbee May 2, 2018
6c2b626
Specify proper TS types for mouseEvents
amitmbee May 2, 2018
ccf1a7b
Use a single value from store to update textarea defaultValue
amitmbee May 3, 2018
d8cf415
Use aliases to dispatch async actions instead of in the component
amitmbee May 3, 2018
e1318f2
Log whenever the compoent re-renders
amitmbee May 4, 2018
d8146e9
Send stringified data to the reducer instead of checking in the compo…
amitmbee May 7, 2018
32297e9
Empty the responseData and responseText values from store, before upd…
amitmbee May 7, 2018
b366eef
Style fetch response button
amitmbee May 8, 2018
e5ba3f1
Merge branch 'master' into Fill-textarea-with-response
amitmbee May 8, 2018
c4891d6
Update yarn lockfile
amitmbee May 8, 2018
4ff5509
Add bg-icon to fetch-response button
amitmbee May 9, 2018
28e2f3f
Show a error message if if axios returns a error for fetch call
amitmbee May 9, 2018
17c0717
Use fetch icon as bg within fetch response button
amitmbee May 9, 2018
06a161f
Remove used icons from images folder
amitmbee May 9, 2018
b8f6963
Update readme : Add credits to The Noun Project for the icons used
amitmbee May 9, 2018
41808b3
Display Attribution title in readme properly
amitmbee May 9, 2018
ceec4a8
Update Readme - Add fetch response section
amitmbee May 10, 2018
11d9ece
Remove un-needed package from actions.ts
amitmbee May 10, 2018
57c6999
Minify readme images
amitmbee May 11, 2018
04f8f6b
use icon only for fetch response button
revathskumar May 14, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 27 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ $ npm run watch

## How to use

### Listening to requests made by the browser

Once you open the extension popup, it shows a UI as seen below. By default, `Intercept Mode` is `ON`.

<img src="images/interceptor_ui.png" alt="Interceptor extension default popup">
Expand All @@ -26,11 +28,21 @@ in the popup like this:

<img src="images/interceptor_ui_xhr_list.png" alt="Interceptor extension popup showing a list of AJAX requests">

You can click the small arrow beside the URL and specify a response to mock, when the same request is encountered next:
Request headers for listened requests are listed below the response form as shown in the screenshot.

<img src="images/interceptor_textfields.png" alt="A screenshot of Listened requests with request headers">

#### Response data returned by backend server

Many a times, before defining you mock response text, you would want to look at the response data returned by the server when the request is made. On clicking `Fetch Response` button, the `textarea` gets filled with the response data from the real server as shown below. However, You can completely skip this step and move on to typing out mock response text.

<img src="images/interceptor_showresponse.png" alt="Fetching data from backend server">

<img src="images/interceptor_textfields.png" alt="Specify mock responses using Interceptor as shown">
#### Specifying mock response data

You can specify the [Content-Type header][content-type] field and HTTP response [status code][status-code] through the dropdown available.
You can click the small arrow beside the URL, which shows a form in which you can specify a response to mock, when the same request is encountered next. You also need to specify the [Content-Type header][content-type] field and [status code][status-code] for the mock response through the dropdown available as shown below.

<img src="images/interceptor_modifyresponse.png" alt="Specify mock responses using Interceptor as shown">

Once the above fields are filled and checkbox is checked, click the `INTERCEPT` button. If the interception is successfull, it shows a success message as below:

Expand All @@ -40,20 +52,22 @@ You can intercept/mock multiple calls by checking as many checkboxes as you want

<img src="images/intercept_multiple_xhr.png" alt="Success message shown by Interceptor upon sucessful interception">

Henceforth the same AJAX request is requested by the browser, the browser is given a fake/mock response instead of the real one.
Henceforth the same AJAX request is made by the browser, the browser is given a fake/mock response instead of the real one.

You can also stop listening for `AJAX calls` by clicking the `STOP LISTENING`
You can also stop listening for `AJAX calls` by clicking the `STOP LISTENING` button. Requests made henceforth won't be listed on UI.

The toggle switch is used to disable `INTERCEPTOR`. If the toggle is switched to `OFF` state, it displays a message saying `Interception Disabled` as below.

<img src="images/interceptor_disabled.png" alt="Message shown by Interceptor on disabling">

In the `disabled` state, the extension won't mock any previously intercepted calls.
Also, the extension's icon beside the url address bar turns red for that particular tab as in screenshot above.
In the `disabled` state, the extension won't mock any previously intercepted calls. Instead all `XHR's` are routed to the server.
The extension's icon beside the url address bar turns red for that particular tab as in screenshot above.


To mock the calls again, just toggle the switch to `ON` state, check the requests that are to be mocked and click `INTERCEPT` button.

This [blogpost](https://crypt.codemancers.com/posts/2018-04-24-intro-to-interceptor/) makes things much more clear.

## TODO

* ~~A user should be able to click on the extension button and see a popup with a list of all AJAX requests.~~
Expand All @@ -64,6 +78,12 @@ To mock the calls again, just toggle the switch to `ON` state, check the request
* ~~Mocked requests should hit a sinon fakeServer.~~
* ~~User should be able to disable/enable mocking for a page without clearing persisted settings for the URL.~~

## Attribution

Icons for this projects are used from [The Noun Project](https://thenounproject.com/)

* [Download](https://thenounproject.com/prosymbols/collection/set-of-line-essentials-icons-2/?i=790723) icon used is by [ProSymbols](https://thenounproject.com/prosymbols/) from the [Noun Project](http://thenounproject.com/).

## License

MIT
Expand Down
17 changes: 16 additions & 1 deletion app/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ export const CONTENT_TYPE_CHANGE = "CONTENT_TYPE_CHANGE"
export const PAGINATION_CHANGE = "PAGINATION_CHANGE"
export const UPDATE_MESSAGE = "UPDATE_MESSAGE"
export const UPDATE_INTERCEPTOR_STATUS = "UPDATE_INTERCEPTOR_STATUS"
export const FETCH_DATA = "FETCH_DATA"
export const FETCH_DATA_SUCCESS = "FETCH_DATA_SUCCESS"
export const FETCH_DATA_FAILURE = "FETCH_DATA_FAILURE"

// Action Creators
export function startListening (enabledStatus:boolean){
Expand Down Expand Up @@ -58,4 +61,16 @@ export function sendMessageToUI(message:string){
}
export function updateInterceptorStatus(tabId:number, value:boolean){
return {type : UPDATE_INTERCEPTOR_STATUS, payload : {tabId, value}}
}
}
export function fetchResponse(requestDetails:object) {
return {
type: FETCH_DATA,
requestDetails
}
}
export function fetchSuccess(data:string, requestId:number){
return {type: FETCH_DATA_SUCCESS, payload : {response : data, requestId } }
}
export function fetchFailure(error:string, requestId:number){
return {type : FETCH_DATA_FAILURE, payload : {error : error, requestId } }
}
202 changes: 111 additions & 91 deletions app/components/Intercept_Components/InterceptTextBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,109 +2,129 @@ import * as React from "react";
import {RequestHeaderList} from "./RequestHeaderList";

export const InterceptTextBox = props => {
const requestId = props.rowProps.checkbox.requestId;
const defaultResponseText = "";
const defaultStatusCode = "200";
const defaultContentType = "application/json";
const responseTextValue = props.responseText[props.rowProps.checkbox.requestId] || defaultResponseText;
const statusCodeValue = props.statusCodes[props.rowProps.checkbox.requestId] || defaultStatusCode;
const contentTypeValue = props.contentType[props.rowProps.checkbox.requestId] || defaultContentType;
const responseTextValue = props.responseText[requestId] || defaultResponseText;
const statusCodeValue = props.statusCodes[requestId] || defaultStatusCode;
const contentTypeValue = props.contentType[requestId] || defaultContentType;

return (
<div className="grid-container form">
<div className="full-url">
<label htmlFor="">URL</label>
<a href={props.rowProps.checkbox.url} className="urlText">
{props.rowProps.checkbox.url}
</a>
<div className="form-container">
<div>
{props.responseError[requestId] ? (
<p className="popup-error-message popup-error"> {props.responseError[requestId]} </p>
) : null}
</div>
<div className="response">
<label htmlFor="">Response Text</label>
<textarea
name="responseText"
className="responseText"
defaultValue={responseTextValue}
//value={responseTextValue}
onChange={event => props.handleRespTextChange(event.target.value, props.rowProps.checkbox.requestId)}
/>
<div className="grid-container form">
<div className="full-url">
<label htmlFor="">URL</label>
<a href={props.rowProps.checkbox.url} className="urlText">
{props.rowProps.checkbox.url}
</a>
</div>
<div className="response">
<label className="responseTextlabel">Response Text</label>
<button
title="Fetch Response"
className="fetch-responsetext btn-sm btn-primary"
onClick={() => {
props.fetchResponse(props.rowProps.checkbox);
}}
/>
<textarea
name="responseText"
className="responseText"
defaultValue={responseTextValue}
key={props.responseData[requestId]}
//value={textAreaValue}
onChange={event => {
props.handleRespTextChange(event.target.value, requestId);
}}
/>
</div>
<div className="status">
<label htmlFor="">Select Status</label>
<select
value={statusCodeValue}
className="select-status"
onChange={event => {
props.handleStatusCodeChange(event.target.value, requestId);
}}
>
<option value="100">100 - Continue</option>
<option value="101">101 - Switching Protocols</option>
<option value="200">200 - OK</option>
<option value="201">201 - Created</option>
<option value="202">202 - Accepted</option>
<option value="203">203 - Non-Authoritative Information</option>
<option value="204">204 - No Content</option>
<option value="205">205 - Reset Content</option>
<option value="206">206 - Partial Content</option>
<option value="207">207 - Multi-Status</option>
<option value="300">300 - Multiple Choice</option>
<option value="301">301 - Moved Permenantly</option>
<option value="302">302 - Found</option>
<option value="303">303 - See Other</option>
<option value="304">304 - Not Modified</option>
<option value="305">305 - Use Proxy</option>
<option value="307">307 - Temporary Redirect</option>
<option value="400">400 - Bad Request</option>
<option value="401">401 - Unauthorized</option>
<option value="402">402 - Payment Required</option>
<option value="403">403 - Forbidden</option>
<option value="404">404 - Not Found</option>
<option value="405">405 - Method Not Allowed</option>
<option value="406">406 - Not Acceptable</option>
<option value="407">407 - Proxy Authentication Required</option>
<option value="408">408 - Request Timeout</option>
<option value="409">409 - Conflict</option>
<option value="410">410 - Gone</option>
<option value="411">411 - Length Required</option>
<option value="412">412 - Precondition Failed</option>
<option value="413">413 - Request Entity Too Large</option>
<option value="414">414 - Request-URI Too Long</option>
<option value="415">415 - Unsupported Media Type</option>
<option value="416">416 - Requested Range Not Satisfiable</option>
<option value="417">417 - Expectation Failed</option>
<option value="422">422 - Unprocessable Entity</option>
<option value="500">500 - Internal Server Error</option>
<option value="501">501 - Not Implemented</option>
<option value="502">502 - Bad Gateway</option>
<option value="503">503 - Service Unavailable</option>
<option value="504">504 - Gateway Timeout</option>
<option value="505">505 - HTTP Version Not Supported</option>
</select>
</div>
<div className="content">
<label htmlFor="">Content Type</label>
<select
value={contentTypeValue}
className="content-type-select"
onChange={event => {
props.handleContentTypeChange(event.target.value, requestId);
}}
>
<option value="application/json">application/json</option>
<option value="text/html">text/html</option>
<option value="text/csv">text/csv</option>
<option value="application/javascript">application/javascript</option>
<option value="text/css">text/css</option>
<option value="text/plain">text/plain</option>
<option value="application/pdf">application/pdf</option>
</select>
</div>
<RequestHeaderList requestHeaders={props.rowProps.checkbox.requestHeaders} />
</div>
<div className="status">
<label htmlFor="">Select Status</label>
<select
value={statusCodeValue}
className="select-status"
onChange={event => {
props.handleStatusCodeChange(event.target.value, props.rowProps.checkbox.requestId);
}}
>
<option value="100">100 - Continue</option>
<option value="101">101 - Switching Protocols</option>
<option value="200">200 - OK</option>
<option value="201">201 - Created</option>
<option value="202">202 - Accepted</option>
<option value="203">203 - Non-Authoritative Information</option>
<option value="204">204 - No Content</option>
<option value="205">205 - Reset Content</option>
<option value="206">206 - Partial Content</option>
<option value="207">207 - Multi-Status</option>
<option value="300">300 - Multiple Choice</option>
<option value="301">301 - Moved Permenantly</option>
<option value="302">302 - Found</option>
<option value="303">303 - See Other</option>
<option value="304">304 - Not Modified</option>
<option value="305">305 - Use Proxy</option>
<option value="307">307 - Temporary Redirect</option>
<option value="400">400 - Bad Request</option>
<option value="401">401 - Unauthorized</option>
<option value="402">402 - Payment Required</option>
<option value="403">403 - Forbidden</option>
<option value="404">404 - Not Found</option>
<option value="405">405 - Method Not Allowed</option>
<option value="406">406 - Not Acceptable</option>
<option value="407">407 - Proxy Authentication Required</option>
<option value="408">408 - Request Timeout</option>
<option value="409">409 - Conflict</option>
<option value="410">410 - Gone</option>
<option value="411">411 - Length Required</option>
<option value="412">412 - Precondition Failed</option>
<option value="413">413 - Request Entity Too Large</option>
<option value="414">414 - Request-URI Too Long</option>
<option value="415">415 - Unsupported Media Type</option>
<option value="416">416 - Requested Range Not Satisfiable</option>
<option value="417">417 - Expectation Failed</option>
<option value="422">422 - Unprocessable Entity</option>
<option value="500">500 - Internal Server Error</option>
<option value="501">501 - Not Implemented</option>
<option value="502">502 - Bad Gateway</option>
<option value="503">503 - Service Unavailable</option>
<option value="504">504 - Gateway Timeout</option>
<option value="505">505 - HTTP Version Not Supported</option>
</select>
</div>
<div className="content">
<label htmlFor="">Content Type</label>
<select
value={contentTypeValue}
className="content-type-select"
onChange={event => {
props.handleContentTypeChange(event.target.value, props.rowProps.checkbox.requestId);
}}
>
<option value="application/json">application/json</option>
<option value="text/html">text/html</option>
<option value="text/csv">text/csv</option>
<option value="application/javascript">application/javascript</option>
<option value="text/css">text/css</option>
<option value="text/plain">text/plain</option>
<option value="application/pdf">application/pdf</option>
</select>
</div>
<RequestHeaderList requestHeaders={props.rowProps.checkbox.requestHeaders} />
</div>
);
};

InterceptTextBox.defaultProps = {
responseText: [],
statusCodes: [],
contentType: []
contentType: [],
responseData: {},
responseError: {}
};
4 changes: 4 additions & 0 deletions app/components/Intercept_Components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ export const InterceptForm = props => {
statusCodes={props.statusCodes}
handleContentTypeChange={props.handleContentTypeChange}
contentType={props.contentType}
tabId={props.tabId}
fetchResponse={props.fetchResponse}
responseData={props.responseData}
responseError={props.responseError}
/>
</div>
);
Expand Down
9 changes: 9 additions & 0 deletions app/components/request_list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {InterceptAllButton} from './../components/InterceptAllButton'
import {Switch} from './Switch'
export interface RequestObj {
requests: Array<chrome.webRequest.WebRequestDetails>;
requestId?: number;
url?: string;
handleCheckToggle: React.ChangeEvent<HTMLInputElement>;
handleCheckedRequests: React.MouseEventHandler<HTMLButtonElement>;
handleRespTextChange: React.FormEvent<HTMLInputElement>;
Expand All @@ -22,6 +24,9 @@ export interface RequestObj {
disableInterceptor:React.ChangeEvent<HTMLButtonElement>;
updateInterceptorStatus:React.ChangeEvent<HTMLButtonElement>;
isInterceptorOn:object;
fetchResponse:React.MouseEvent<HTMLSpanElement>
responseData : object;
responseError: object;
}
const RequestList = (props: RequestObj) => {
const columns = [
Expand Down Expand Up @@ -126,6 +131,10 @@ const RequestList = (props: RequestObj) => {
statusCodes={props.statusCodes}
handleContentTypeChange={props.handleContentTypeChange}
contentType={props.contentType}
tabId={props.tabId}
fetchResponse={props.fetchResponse}
responseData = {props.responseData}
responseError= {props.responseError}
/>
)}
/>
Expand Down
Loading