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

feat(plugin): allow to transform entries before saving a HAR #182

Merged
38 changes: 36 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,10 +242,44 @@ export default async (entry: Entry) => {
};
```

> ✴ The plugin also supports files with `.js`, `.mjs` and `.cjs` extensions.

In this example, the `filter` function will only exclude entries in the HAR where the request body contains a JSON object with a password field.

You can also modify the entries before saving the HAR by using the `transform` option. This option should be set to a path to a module that exports a function, similar to the filter option, to change the Entry object as desired.

```js
cy.recordHar({ transform: '../support/remove-sensitive-data.ts' });
```

Here's a simple example of what the `remove-sensitive-data.ts` module might look like:

```ts
import { Entry } from 'har-format';

const PASSWORD_REGEXP = /("password":\s*")\w+("\s*)/;

export default async (entry: Entry) => {
try {
// Remove sensitive information from the request body
if (entry.request.postData?.text) {
entry.request.postData.text = entry.request.postData.text.replace(
PASSWORD_REGEXP,
'$1***$2'
);
}

return entry;
} catch {
return entry;
}
};
```

The function should take an Entry object as a parameter and return the modified.

In this example, the transform function will replace any occurrences of password with `***` in the request body of each entry.

> ✴ The plugin also supports files with `.js`, `.mjs` and `.cjs` extensions.

By default, the path is relative to the spec folder. But by providing a `rootDir` it will look for the module in the provided directory:

```js
Expand Down
18 changes: 18 additions & 0 deletions cypress/e2e/record-har.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,24 @@ describe('Record HAR', () => {
})
);

['.js', '.ts', '.cjs'].forEach(ext =>
it(`transforms a request using the custom postprocessor from ${ext} file`, () => {
cy.recordHar({ transform: `../fixtures/transform${ext}` });

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

cy.saveHar({ waitForIdle: true });

cy.findHar()
.its('log.entries')
.should('contain.something.like', {
response: {
content: { text: /\{"items":\[/ }
}
});
})
);

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

Expand Down
4 changes: 2 additions & 2 deletions cypress/fixtures/filter.cjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module.exports = async req => {
module.exports = async entry => {
try {
return /\{"products":\[/.test(req.response.content.text ?? '');
return /\{"products":\[/.test(entry.response.content.text ?? '');
} catch {
return false;
}
Expand Down
4 changes: 2 additions & 2 deletions cypress/fixtures/filter.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module.exports = async req => {
module.exports = async entry => {
try {
return /\{"products":\[/.test(req.response.content.text ?? '');
return /\{"products":\[/.test(entry.response.content.text ?? '');
} catch {
return false;
}
Expand Down
4 changes: 2 additions & 2 deletions cypress/fixtures/filter.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export default async req => {
export default async entry => {
try {
return /\{"products":\[/.test(req.response.content.text ?? '');
return /\{"products":\[/.test(entry.response.content.text ?? '');
} catch {
return false;
}
Expand Down
4 changes: 2 additions & 2 deletions cypress/fixtures/filter.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Entry } from 'har-format';

export default async (req: Entry) => {
export default async (entry: Entry) => {
try {
return /\{"products":\[/.test(req.response.content.text ?? '');
return /\{"products":\[/.test(entry.response.content.text ?? '');
} catch {
return false;
}
Expand Down
19 changes: 19 additions & 0 deletions cypress/fixtures/transform.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module.exports = async entry => {
try {
if (entry.response.content?.text) {
entry.response.content = {
...entry.response.content,
text: entry.response.content.text.replace(
/"products":\s*(.+)/g,
`"items":$1`
)
};
}

return entry;
} catch {
return entry;
}
};


17 changes: 17 additions & 0 deletions cypress/fixtures/transform.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module.exports = async entry => {
try {
if (entry.response.content?.text) {
entry.response.content = {
...entry.response.content,
text: entry.response.content.text.replace(
/"products":\s*(.+)/g,
`"items":$1`
)
};
}

return entry;
} catch {
return entry;
}
};
17 changes: 17 additions & 0 deletions cypress/fixtures/transform.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export async entry => {
try {
if (entry.response.content?.text) {
entry.response.content = {
...entry.response.content,
text: entry.response.content.text.replace(
/"products":\s*(.+)/g,
`"items":$1`
)
};
}

return entry;
} catch {
return entry;
}
};
19 changes: 19 additions & 0 deletions cypress/fixtures/transform.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Entry } from 'har-format';

export default async (entry: Entry) => {
try {
if (entry.response.content?.text) {
entry.response.content = {
...entry.response.content,
text: entry.response.content.text.replace(
/"products":\s*(.+)/g,
`"items":$1`
)
};
}

return entry;
} catch {
return entry;
}
};
4 changes: 2 additions & 2 deletions src/Plugin.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { RecordOptions, SaveOptions } from './Plugin';
import { Plugin } from './Plugin';
import { Logger } from './utils/Logger';
import { FileManager } from './utils/FileManager';
import type { Logger } from './utils/Logger';
import type { FileManager } from './utils/FileManager';
import type {
Observer,
ObserverFactory,
Expand Down
13 changes: 4 additions & 9 deletions src/Plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import type {
HarExporterFactory,
NetworkObserverOptions,
Observer,
ObserverFactory
ObserverFactory,
HarExporterOptions
} from './network';
import { HarBuilder, NetworkIdleMonitor, NetworkRequest } from './network';
import { ErrorUtils } from './utils/ErrorUtils';
Expand All @@ -25,10 +26,7 @@ export interface SaveOptions {
waitForIdle?: boolean;
}

export type RecordOptions = NetworkObserverOptions & {
rootDir: string;
filter?: string;
};
export type RecordOptions = NetworkObserverOptions & HarExporterOptions;

interface Addr {
port: number;
Expand Down Expand Up @@ -81,10 +79,7 @@ export class Plugin {
);
}

this.exporter = await this.exporterFactory.create({
predicatePath: options.filter,
rootDir: options.rootDir
});
this.exporter = await this.exporterFactory.create(options);
this._connection = this.connectionFactory.create({
...this.addr,
maxRetries: 20,
Expand Down
2 changes: 1 addition & 1 deletion src/cdp/DefaultNetwork.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Network, NetworkEvent } from '../network';
import { ErrorUtils } from '../utils/ErrorUtils';
import { Logger } from '../utils/Logger';
import type { Logger } from '../utils/Logger';
import {
TARGET_OR_BROWSER_CLOSED,
UNABLE_TO_ATTACH_TO_TARGET
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const plugin = new Plugin(
FileManager.Instance,
new DefaultConnectionFactory(Logger.Instance),
new DefaultObserverFactory(Logger.Instance),
new DefaultHarExporterFactory(FileManager.Instance)
new DefaultHarExporterFactory(FileManager.Instance, Logger.Instance)
);

export const install = (on: Cypress.PluginEvents): void => {
Expand Down
Loading