Skip to content

Commit

Permalink
integrated changes from feature/allow_mocking_by_method and updated t…
Browse files Browse the repository at this point in the history
…ests, typings, documentation and naming
  • Loading branch information
Tabueeee committed Jan 19, 2019
1 parent 3463384 commit 9e5a31a
Show file tree
Hide file tree
Showing 52 changed files with 476 additions and 95 deletions.
1 change: 1 addition & 0 deletions .idea/puppeteer-request-spy.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,4 @@ build/**/*.js.map
custom-typings/
.travis.yml
.coveralls.yml
CONTRIBUTING.md
18 changes: 18 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
This Project uses Semantic Versioning. More information can be found [here](https://semver.org/).

The branch and tag names also follow the following convention:
- branches: ```v-x.y.z```
- tags: ```v.x.y.z```

You only need to follow this convention when creating a Pull Request for a full npm release.

Please make sure your branch passes the build process by running ```npm test```.
You can check the code coverage by generating a html report using ```npm run test-coverage```.

The tslint setting may seem harsh but they are usually useful to determine problems.
Try to fix as much as possible but I am not contempt on keeping every rule.
Some are a matter of choice after all.

If you want to ensure a proper release, bump the version in the package.json and run ```npm run release-dry```.
This will run all required steps for a successful release like ts-lint, build, test, generating types
and creates a preview of the final package pushed to npm.
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,10 @@ Clears all registered patterns in `urlsToBlock`.
#### RequestSpy.getMatchCount()
- returns: \<number> number of urls that matched the `pattern`

#### RequestSpy.isMatch(matcher: Matcher, request: Request)
- returns: \<Array\<string\>\> return the `pattern` list of the spy
#### RequestSpy.isMatchingRequest(matcher: Matcher, request: Request)
- returns: \<boolean\> returns true if any pattern provided to the RequestSpy matches the request url

The `RequestInterceptor` calls this method to determine if an interceptedRequest matches the RequestSpy.

#### RequestSpy.addMatch(matchedRequest)
- matchedRequest: \<Request> request that was matched
Expand All @@ -183,6 +185,11 @@ The `RequestInterceptor` calls this method when an interceptedRequest matches th
- returns: \<Response\> return the fake response

The `RequestInterceptor` calls this method when an interceptedUrl matches the pattern.

#### RequestSpy.isMatchingRequest(matcher: Matcher, request: Request)
- returns: \<boolean\> returns true if any pattern provided to the RequestFaker matches the request url

The `RequestInterceptor` calls this method to determine if an interceptedRequest matches the RequestFaker.

# Examples

Expand Down
35 changes: 35 additions & 0 deletions examples/CustomFaker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {Request} from 'puppeteer';
import {RespondOptions} from 'puppeteer';
import {IResponseFaker, RequestMatcher} from 'puppeteer-request-spy';

export class CustomFaker implements IResponseFaker {

private patterns: Array<string> = [];
private fakesMap = {
'GET': 'some text',
'POST': 'Not Found!'
};

public constructor(patterns: Array<string>) {
this.patterns = patterns;
}

public getResponseFake(request: Request): RespondOptions {
return {
status: 200,
contentType: 'text/plain',
body: this.fakesMap[request.method()]
};
}

public isMatchingRequest(request: Request, matcher: RequestMatcher): boolean {
for (let pattern of this.patterns) {
if(matcher(request.url(), pattern)){

return true;
}
}

return false;
}
}
27 changes: 27 additions & 0 deletions examples/CustomSpy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {IRequestSpy, RequestMatcher} from 'puppeteer-request-spy';
import {Request, Response} from 'puppeteer';
import * as assert from "assert";


export class CustomSpy implements IRequestSpy {
private matches: Array<Request> = [];

public isMatchingRequest(request: Request, matcher: RequestMatcher): boolean {

return matcher(request.url(), '**/*');
}

public addMatch(matchedRequest: Request): void {

this.matches.push(matchedRequest);
}

public assertRequestsOk() {
for (let match of this.matches) {
let response: Response | null = match.response();
if (response !== null) {
assert.ok(response.ok());
}
}
}
}
8 changes: 4 additions & 4 deletions src/UrlRequestBlocker.ts → src/RequestBlocker.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import {Request} from 'puppeteer';
import {UrlAccessor} from './common/urlAccessor/UrlAccessor';
import {UrlAccessorResolver} from './common/urlAccessor/UrlAccessorResolver';
import {Matcher} from './interface/Matcher';
import {RequestBlocker} from './interface/RequestBlocker';
import {IRequestBlocker} from './interface/IRequestBlocker';
import {RequestMatcher} from './interface/RequestMatcher';

export class UrlRequestBlocker implements RequestBlocker {
export class RequestBlocker implements IRequestBlocker {

private urlsToBlock: Array<string> = [];

public shouldBlockRequest(matcher: Matcher, request: Request): boolean {
public shouldBlockRequest(request: Request, matcher: RequestMatcher): boolean {
let urlAccessor: UrlAccessor = UrlAccessorResolver.getUrlAccessor(request);

for (let urlToBlock of this.urlsToBlock) {
Expand Down
62 changes: 36 additions & 26 deletions src/RequestInterceptor.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,46 @@
import {Request} from 'puppeteer';
import {instanceOfRequestBlocker} from './common/InterfaceValidators/instanceOfRequestBlocker';
import {Logger} from './common/Logger';
import {instanceOfRequestBlocker} from './common/interfaceValidators/instanceOfRequestBlocker';
import {instanceOfRequestFaker} from './common/interfaceValidators/instanceOfRequestFaker';
import {instanceOfRequestSpy} from './common/interfaceValidators/instanceOfRequestSpy';
import {ILogger} from './common/Logger';
import {UrlAccessor} from './common/urlAccessor/UrlAccessor';
import {UrlAccessorResolver} from './common/urlAccessor/UrlAccessorResolver';
import {VoidLogger} from './common/VoidLogger';
import {Faker} from './interface/Faker';
import {Matcher} from './interface/Matcher';
import {RequestBlocker} from './interface/RequestBlocker';
import {Spy} from './interface/Spy';
import {UrlRequestBlocker} from './UrlRequestBlocker';
import {IRequestBlocker} from './interface/IRequestBlocker';
import {IResponseFaker} from './interface/IRequestFaker';
import {IRequestSpy} from './interface/IRequestSpy';
import {RequestMatcher} from './interface/RequestMatcher';
import {RequestBlocker} from './RequestBlocker';

export class RequestInterceptor {

private requestSpies: Array<Spy> = [];
private responseFakers: Array<Faker> = [];
private matcher: Matcher;
private logger: Logger;
private requestSpies: Array<IRequestSpy> = [];
private responseFakers: Array<IResponseFaker> = [];
private matcher: RequestMatcher;
private logger: ILogger;
private urlAccessor: UrlAccessor | undefined;
private requestBlocker: RequestBlocker;
private requestBlocker: IRequestBlocker;

public constructor(matcher: Matcher, logger?: Logger) {
public constructor(matcher: RequestMatcher, logger?: ILogger) {
if (typeof logger === 'undefined') {
logger = new VoidLogger();
}

this.logger = logger;
this.matcher = matcher;
this.requestBlocker = new UrlRequestBlocker();
this.requestBlocker = new RequestBlocker();
}

public async intercept(interceptedRequest: Request): Promise<void> {
this.matchSpies(interceptedRequest);

if (this.requestBlocker.shouldBlockRequest(this.matcher, interceptedRequest)) {
if (this.requestBlocker.shouldBlockRequest(interceptedRequest, this.matcher)) {
await this.blockUrl(interceptedRequest);

return;
}

let responseFaker: undefined | Faker = this.getMatchingFaker(interceptedRequest);
let responseFaker: undefined | IResponseFaker = this.getMatchingFaker(interceptedRequest);

if (typeof responseFaker !== 'undefined') {
await interceptedRequest.respond(responseFaker.getResponseFake(interceptedRequest));
Expand All @@ -49,11 +51,19 @@ export class RequestInterceptor {
await this.acceptUrl(interceptedRequest);
}

public addSpy(requestSpy: Spy): void {
public addSpy(requestSpy: IRequestSpy): void {
if (!instanceOfRequestSpy(requestSpy)) {
throw new Error('invalid RequestSpy provided. Please make sure to match the interface provided.');
}

this.requestSpies.push(requestSpy);
}

public addFaker(responseFaker: Faker): void {
public addFaker(responseFaker: IResponseFaker): void {
if (!instanceOfRequestFaker(responseFaker)) {
throw new Error('invalid RequestFaker provided. Please make sure to match the interface provided.');
}

this.responseFakers.push(responseFaker);
}

Expand All @@ -78,12 +88,12 @@ export class RequestInterceptor {
this.requestBlocker.addUrlsToBlock(urlsToBlock);
}

public setRequestBlocker(requestBlocker: RequestBlocker): void {
if (instanceOfRequestBlocker(requestBlocker)) {
this.requestBlocker = requestBlocker;
} else {
throw new Error('invalid requestBlocker. make sure to implement the interface correctly.');
public setRequestBlocker(requestBlocker: IRequestBlocker): void {
if (!instanceOfRequestBlocker(requestBlocker)) {
throw new Error('invalid RequestBlocker provided. Please make sure to match the interface provided.');
}

this.requestBlocker = requestBlocker;
}

private getUrlAccessor(interceptedRequest: Request): UrlAccessor {
Expand All @@ -94,9 +104,9 @@ export class RequestInterceptor {
return this.urlAccessor;
}

private getMatchingFaker(interceptedRequest: Request): Faker | undefined {
private getMatchingFaker(interceptedRequest: Request): IResponseFaker | undefined {
for (let faker of this.responseFakers) {
if (faker.isMatch(this.matcher, interceptedRequest)) {
if (faker.isMatchingRequest(interceptedRequest, this.matcher)) {
return faker;
}
}
Expand All @@ -106,7 +116,7 @@ export class RequestInterceptor {

private matchSpies(interceptedRequest: Request): void {
for (let spy of this.requestSpies) {
if (spy.isMatch(this.matcher, interceptedRequest)) {
if (spy.isMatchingRequest(interceptedRequest, this.matcher)) {
spy.addMatch(interceptedRequest);
}
}
Expand Down
12 changes: 8 additions & 4 deletions src/RequestSpy.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {Request} from 'puppeteer';
import {UrlAccessor} from './common/urlAccessor/UrlAccessor';
import {UrlAccessorResolver} from './common/urlAccessor/UrlAccessorResolver';
import {Matcher} from './interface/Matcher';
import {Spy} from './interface/Spy';
import {IRequestSpy} from './interface/IRequestSpy';
import {RequestMatcher} from './interface/RequestMatcher';

export class RequestSpy implements Spy {
export class RequestSpy implements IRequestSpy {

private hasMatchingUrl: boolean = false;
private matchCount: number = 0;
Expand All @@ -23,6 +23,10 @@ export class RequestSpy implements Spy {
this.patterns = patterns;
}

public getPatterns(): Array<string> {
return this.patterns;
}

public getMatchedRequests(): Array<Request> {
return this.matchedRequests;
}
Expand All @@ -37,7 +41,7 @@ export class RequestSpy implements Spy {
this.matchCount++;
}

public isMatch(matcher: Matcher, request: Request): boolean {
public isMatchingRequest(request: Request, matcher: RequestMatcher): boolean {
let urlAccessor: UrlAccessor = UrlAccessorResolver.getUrlAccessor(request);

for (let pattern of this.patterns) {
Expand Down
12 changes: 8 additions & 4 deletions src/ResponseFaker.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {Request, RespondOptions} from 'puppeteer';
import {UrlAccessor} from './common/urlAccessor/UrlAccessor';
import {UrlAccessorResolver} from './common/urlAccessor/UrlAccessorResolver';
import {Faker} from './interface/Faker';
import {Matcher} from './interface/Matcher';
import {IResponseFaker} from './interface/IRequestFaker';
import {RequestMatcher} from './interface/RequestMatcher';

export class ResponseFaker implements Faker {
export class ResponseFaker implements IResponseFaker {

private responseFake: RespondOptions;
private patterns: Array<string> = [];
Expand All @@ -26,7 +26,7 @@ export class ResponseFaker implements Faker {
return this.responseFake;
}

public isMatch(matcher: Matcher, request: Request): boolean {
public isMatchingRequest(request: Request, matcher: RequestMatcher): boolean {
let urlAccessor: UrlAccessor = UrlAccessorResolver.getUrlAccessor(request);
for (let pattern of this.patterns) {
if (matcher(urlAccessor.getUrlFromRequest(request), pattern)) {
Expand All @@ -36,4 +36,8 @@ export class ResponseFaker implements Faker {

return false;
}

public getPatterns(): Array<string> {
return this.patterns;
}
}
7 changes: 0 additions & 7 deletions src/common/InterfaceValidators/instanceOfRequestBlocker.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/common/Logger.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export interface Logger {
export interface ILogger {
log(message: string): void;
}
7 changes: 7 additions & 0 deletions src/common/interfaceValidators/instanceOfRequestBlocker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {IRequestBlocker} from '../../interface/IRequestBlocker';

export function instanceOfRequestBlocker(object: object): object is IRequestBlocker {
return typeof (<IRequestBlocker>object).shouldBlockRequest === 'function'
&& typeof (<IRequestBlocker>object).clearUrlsToBlock === 'function'
&& typeof (<IRequestBlocker>object).addUrlsToBlock === 'function';
}
6 changes: 6 additions & 0 deletions src/common/interfaceValidators/instanceOfRequestFaker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import {IResponseFaker} from '../../';

export function instanceOfRequestFaker(object: object): object is IResponseFaker {
return typeof (<IResponseFaker>object).getResponseFake === 'function'
&& typeof (<IResponseFaker>object).isMatchingRequest === 'function';
}
6 changes: 6 additions & 0 deletions src/common/interfaceValidators/instanceOfRequestSpy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import {IRequestSpy} from '../../';

export function instanceOfRequestSpy(object: object): object is IRequestSpy {
return typeof (<IRequestSpy>object).addMatch === 'function'
&& typeof (<IRequestSpy>object).isMatchingRequest === 'function';
}
16 changes: 8 additions & 8 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {Faker} from './interface/Faker';
import {Matcher} from './interface/Matcher';
import {RequestBlocker} from './interface/RequestBlocker';
import {Spy} from './interface/Spy';
import {IRequestBlocker} from './interface/IRequestBlocker';
import {IResponseFaker} from './interface/IRequestFaker';
import {IRequestSpy} from './interface/IRequestSpy';
import {RequestMatcher} from './interface/RequestMatcher';
import {RequestInterceptor} from './RequestInterceptor';
import {RequestSpy} from './RequestSpy';
import {ResponseFaker} from './ResponseFaker';
Expand All @@ -10,8 +10,8 @@ export {
RequestInterceptor,
RequestSpy,
ResponseFaker,
Faker,
Spy,
RequestBlocker,
Matcher
IResponseFaker,
IRequestSpy,
IRequestBlocker,
RequestMatcher
};
Loading

0 comments on commit 9e5a31a

Please sign in to comment.