Skip to content

Commit

Permalink
feat(plugin): accept the RegExp | string union type in the patterns (
Browse files Browse the repository at this point in the history
…#175)

It is now possible to utilize regular expressions rather than strings
when specifying the excludePaths or includeHosts options.

**Before**:

```ts
cy.recordHar({ excludePaths: ['^\\/api\\/products$', '^\\/api\\/users$'] });
```

**After**:

```ts
cy.recordHar({ excludePaths: [/^\/api\/products$/, '^\\/api\\/users$'] });
```

closes #163
  • Loading branch information
derevnjuk committed Jan 25, 2023
1 parent 90f36be commit 2e0160e
Show file tree
Hide file tree
Showing 10 changed files with 93 additions and 26 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,13 @@ cy.recordHar({ content: false });
To include only requests on specific hosts, you can pass an array of patterns specifying a list of hosts using the `includeHosts` for which to record requests:

```js
cy.recordHar({ includeHosts: ['.*.execute-api.eu-west-1.amazonaws.com'] });
cy.recordHar({ includeHosts: [/.*\.execute-api\.eu-west-1\.amazonaws\.com/] });
```

To exclude some requests, you can pass an array of patterns specifying a list of paths using the `excludePaths` to be excluded from the logs:

```js
cy.recordHar({ excludePaths: ['^/login', 'logout$'] });
cy.recordHar({ excludePaths: [/^\/login/, /logout$/] });
```

You can also pass an array of MIME types for which to record requests:
Expand Down
2 changes: 1 addition & 1 deletion cypress/e2e/record-har.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ describe('Record HAR', () => {
);

it('excludes a request by its path', () => {
cy.recordHar({ excludePaths: ['^\\/api\\/products$', '^\\/api\\/users$'] });
cy.recordHar({ excludePaths: [/^\/api\/products$/, '^\\/api\\/users$'] });

cy.get('a[href$=fetch]').click();

Expand Down
27 changes: 14 additions & 13 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@ import type { RecordOptions, SaveOptions } from './Plugin';
Cypress.Commands.add(
'recordHar',
(options?: Partial<RecordOptions>): Cypress.Chainable =>
cy.task(
'recordHar',
Object.assign(
{
content: true,
includeBlobs: true,
rootDir: StringUtils.dirname(Cypress.spec.absolute)
},
options
cy.task('recordHar', {
content: true,
includeBlobs: true,
rootDir: StringUtils.dirname(Cypress.spec.absolute),
...options,
excludePaths: options?.excludePaths?.map(x =>
StringUtils.toRegexSource(x)
),
includeHosts: options?.includeHosts?.map(x =>
StringUtils.toRegexSource(x)
)
)
})
);

Cypress.Commands.add(
Expand All @@ -23,14 +24,14 @@ Cypress.Commands.add(
const fallbackFileName = Cypress.spec.name;
const outDir = (Cypress.env('hars_folders') as string) ?? './';

options = Object.assign({ outDir }, options, {
return cy.task('saveHar', {
outDir,
...options,
fileName: StringUtils.normalizeName(
options?.fileName ?? fallbackFileName,
!options?.fileName ? { ext: '.har' } : undefined
)
});

return cy.task('saveHar', options);
}
);

Expand Down
4 changes: 2 additions & 2 deletions src/network/NetworkObserverOptions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export interface NetworkObserverOptions {
content?: boolean;
excludePaths?: string[];
includeHosts?: string[];
excludePaths?: (string | RegExp)[];
includeHosts?: (string | RegExp)[];
includeMimes?: string[];
excludeStatusCodes?: number[];
/**
Expand Down
4 changes: 2 additions & 2 deletions src/network/filters/HostFilter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ describe('HostFilter', () => {

it.each([
{ includeHosts: ['example.com'] },
{ includeHosts: ['example.com$'] },
{ includeHosts: [/example\.com$/] },
{ includeHosts: ['example.com', 'sub.example.com'] }
])(
'should return true when the filter is applicable (options: %j)',
Expand Down Expand Up @@ -66,7 +66,7 @@ describe('HostFilter', () => {
// arrange
const url = new URL('https://example.com');
when(networkRequestMock.parsedURL).thenReturn(url);
const options = { includeHosts: ['example.com$'] };
const options = { includeHosts: [/example\.com$/] };
// act
const result = sut.apply(instance(networkRequestMock), options);
// assert
Expand Down
5 changes: 3 additions & 2 deletions src/network/filters/HostFilter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { NetworkRequest } from '../NetworkRequest';
import type { RequestFilter, RequestFilterOptions } from './RequestFilter';
import { StringUtils } from '../../utils/StringUtils';

export class HostFilter implements RequestFilter {
public apply(
Expand All @@ -8,8 +9,8 @@ export class HostFilter implements RequestFilter {
): boolean {
const { host } = request.parsedURL;

return !!includeHosts?.some((pattern: string): boolean =>
new RegExp(pattern).test(host)
return !!includeHosts?.some(pattern =>
StringUtils.toRegex(pattern).test(host)
);
}

Expand Down
4 changes: 2 additions & 2 deletions src/network/filters/PathFilter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ describe('PathFilter', () => {

it.each([
{ excludePaths: ['example.com'] },
{ excludePaths: ['example.com$'] },
{ excludePaths: [/example\.com$/] },
{ excludePaths: ['example.com', 'sub.example.com'] }
])(
'should return true when the filter is applicable (options: %j)',
Expand Down Expand Up @@ -66,7 +66,7 @@ describe('PathFilter', () => {
// arrange
const url = new URL('https://example.com');
when(networkRequestMock.parsedURL).thenReturn(url);
const options = { excludePaths: ['/login$'] };
const options = { excludePaths: [/\/login$/] };
// act
const result = sut.apply(instance(networkRequestMock), options);
// assert
Expand Down
5 changes: 3 additions & 2 deletions src/network/filters/PathFilter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { NetworkRequest } from '../NetworkRequest';
import type { RequestFilter, RequestFilterOptions } from './RequestFilter';
import { StringUtils } from '../../utils/StringUtils';

export class PathFilter implements RequestFilter {
public apply(
Expand All @@ -8,8 +9,8 @@ export class PathFilter implements RequestFilter {
): boolean {
const { pathname = '/' } = request.parsedURL;

return !excludePaths?.some((pattern: string): boolean =>
new RegExp(pattern).test(pathname)
return !excludePaths?.some(pattern =>
StringUtils.toRegex(pattern).test(pathname)
);
}

Expand Down
52 changes: 52 additions & 0 deletions src/utils/StringUtils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,58 @@ import { StringUtils } from './StringUtils';
import { describe, expect, it } from '@jest/globals';

describe('StringUtils', () => {
describe('isString', () => {
it.each([
{ input: 'test', expected: true },
{ input: '', expected: true },
{ input: 0, expected: false },
{ input: undefined, expected: false },
{ input: null, expected: false },
{ input: [], expected: false },
{ input: {}, expected: false },
{ input: Symbol.search, expected: false }
])(
'should return $expected when value is $input',
({ input, expected }) => {
// act
const result = StringUtils.isString(input);
// assert
expect(result).toBe(expected);
}
);
});

describe('toRegexSource', () => {
it.each([
{ input: 'test', expected: 'test' },
{ input: '', expected: '' },
{ input: /test/, expected: 'test' },
{ input: new RegExp(''), expected: '(?:)' }
])(
'should return a copy of the text of the $input pattern',
({ input, expected }) => {
// act
const result = StringUtils.toRegexSource(input);
// assert
expect(result).toBe(expected);
}
);
});

describe('toRegex', () => {
it.each([
{ input: 'test' },
{ input: '' },
{ input: /test/ },
{ input: new RegExp('test') }
])('should return an instance of the regular expression', ({ input }) => {
// act
const result = StringUtils.toRegex(input);
// assert
expect(result).toBeInstanceOf(RegExp);
});
});

describe('dirname', () => {
it('should return the directory name of a unix path', () => {
const path = '/path/to/file.txt';
Expand Down
12 changes: 12 additions & 0 deletions src/utils/StringUtils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
export class StringUtils {
public static isString(value: unknown): value is string {
return typeof value === 'string';
}

public static toRegexSource(pattern: RegExp | string): string {
return this.isString(pattern) ? pattern : pattern.source;
}

public static toRegex(pattern: RegExp | string): RegExp {
return this.isString(pattern) ? new RegExp(pattern) : pattern;
}

public static dirname(path: string): string {
const normalizedPath = this.removeTrailingSlash(path);
const fileNameIdx = this.fileNameIdx(normalizedPath);
Expand Down

0 comments on commit 2e0160e

Please sign in to comment.