@gravity-ui/playwright-tools 1.0.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/fixtures/mock-network/har-patcher.d.ts +2 -2
- package/fixtures/mock-network/har-patcher.js +31 -6
- package/fixtures/mock-network/mock-network-fixture.d.ts +1 -1
- package/fixtures/mock-network/mock-network-fixture.js +43 -18
- package/fixtures/mock-network/types.d.ts +26 -4
- package/har/index.d.ts +1 -1
- package/har/replaceBaseUrlInEntry.d.ts +1 -1
- package/har/replaceBaseUrlInEntry.js +1 -1
- package/package.json +1 -1
- package/utils/createDuplicateIdTransform.d.ts +11 -0
- package/utils/createDuplicateIdTransform.js +32 -0
- package/utils/markIdenticalRequests.d.ts +9 -0
- package/utils/markIdenticalRequests.js +28 -0
- package/utils/requestKeyService.d.ts +30 -0
- package/utils/requestKeyService.js +79 -0
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
A library of additional utilities for writing tests using Playwright Test.
|
|
4
4
|
|
|
5
5
|
```
|
|
6
|
-
npm i -D playwright-tools
|
|
6
|
+
npm i -D @gravity-ui/playwright-tools
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
The package contains several subdirectories with utilities for different purposes. You should import from these subdirectories, for example:
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { MockNetworkFixtureBuilderParams } from './types';
|
|
2
|
-
export type HarPatcherParams = Pick<MockNetworkFixtureBuilderParams, 'headersToRemove' | 'setCookieToRemove' | 'onHarEntryWillRead' | 'onHarEntryWillWrite' | 'onTransformHarLookupParams' | 'onTransformHarLookupResult'> & {
|
|
2
|
+
export type HarPatcherParams = Pick<MockNetworkFixtureBuilderParams, 'headersToRemove' | 'setCookieToRemove' | 'onHarEntryWillRead' | 'onHarEntryWillWrite' | 'onTransformHarLookupParams' | 'onTransformHarLookupResult' | 'shouldMarkIdenticalRequests'> & {
|
|
3
3
|
/**
|
|
4
4
|
* Base url of the test
|
|
5
5
|
*/
|
|
6
6
|
baseURL: string;
|
|
7
7
|
};
|
|
8
|
-
export declare function harPatcher({ baseURL, headersToRemove: additionalHeadersToRemove, setCookieToRemove: additionalSetCookieToRemove, onHarEntryWillWrite, onHarEntryWillRead, onTransformHarLookupParams, onTransformHarLookupResult, }: HarPatcherParams): void;
|
|
8
|
+
export declare function harPatcher({ baseURL, headersToRemove: additionalHeadersToRemove, setCookieToRemove: additionalSetCookieToRemove, onHarEntryWillWrite, onHarEntryWillRead, onTransformHarLookupParams, onTransformHarLookupResult, shouldMarkIdenticalRequests, }: HarPatcherParams): void;
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.harPatcher = harPatcher;
|
|
4
4
|
const har_1 = require("../../har");
|
|
5
|
+
const createDuplicateIdTransform_1 = require("../../utils/createDuplicateIdTransform");
|
|
6
|
+
const markIdenticalRequests_1 = require("../../utils/markIdenticalRequests");
|
|
5
7
|
const DEFAULT_REMOVE_HEADERS = new Set([
|
|
6
8
|
'cookie',
|
|
7
9
|
'x-csrf-token',
|
|
@@ -10,7 +12,7 @@ const DEFAULT_REMOVE_HEADERS = new Set([
|
|
|
10
12
|
]);
|
|
11
13
|
const DEFAULT_REMOVE_SET_COOKIE_FOR = new Set(['CSRF-TOKEN']);
|
|
12
14
|
const baseUrlPLaceholder = 'https://base.url.placeholder';
|
|
13
|
-
function harPatcher({ baseURL, headersToRemove: additionalHeadersToRemove = [], setCookieToRemove: additionalSetCookieToRemove = [], onHarEntryWillWrite, onHarEntryWillRead, onTransformHarLookupParams, onTransformHarLookupResult, }) {
|
|
15
|
+
function harPatcher({ baseURL, headersToRemove: additionalHeadersToRemove = [], setCookieToRemove: additionalSetCookieToRemove = [], onHarEntryWillWrite, onHarEntryWillRead, onTransformHarLookupParams, onTransformHarLookupResult, shouldMarkIdenticalRequests = false, }) {
|
|
14
16
|
const headersToRemove = new Set([...DEFAULT_REMOVE_HEADERS, ...additionalHeadersToRemove]);
|
|
15
17
|
const setCookieToRemove = new Set([
|
|
16
18
|
...DEFAULT_REMOVE_SET_COOKIE_FOR,
|
|
@@ -27,16 +29,39 @@ function harPatcher({ baseURL, headersToRemove: additionalHeadersToRemove = [],
|
|
|
27
29
|
removeSetCookieFor: setCookieToRemove,
|
|
28
30
|
});
|
|
29
31
|
(0, har_1.replaceBaseUrlInEntry)(entry, baseURL, baseUrlPLaceholder);
|
|
30
|
-
onHarEntryWillWrite?.(entry);
|
|
32
|
+
onHarEntryWillWrite?.(entry, baseURL);
|
|
31
33
|
});
|
|
32
34
|
(0, har_1.addHarOpenTransform)((harFile) => {
|
|
33
35
|
const entries = harFile.log.entries;
|
|
34
36
|
for (const entry of entries) {
|
|
35
37
|
(0, har_1.replaceBaseUrlInEntry)(entry, baseUrlPLaceholder, baseURL);
|
|
36
|
-
onHarEntryWillRead?.(entry);
|
|
38
|
+
onHarEntryWillRead?.(entry, baseURL);
|
|
37
39
|
}
|
|
38
40
|
});
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
// Create duplicate ID transformer once to preserve state between calls
|
|
42
|
+
const duplicateIdTransform = shouldMarkIdenticalRequests ? (0, createDuplicateIdTransform_1.createDuplicateIdTransform)() : null;
|
|
43
|
+
const onTransformHarLookupParamsFinal = (params) => {
|
|
44
|
+
// Apply duplicate ID transform first (if enabled)
|
|
45
|
+
const modifiedParams = duplicateIdTransform ? duplicateIdTransform(params) : params;
|
|
46
|
+
// Then apply custom transformer (if provided)
|
|
47
|
+
return onTransformHarLookupParams
|
|
48
|
+
? onTransformHarLookupParams(modifiedParams, baseURL)
|
|
49
|
+
: modifiedParams;
|
|
50
|
+
};
|
|
51
|
+
const onTransformHarLookupResultFinal = (result, params) => {
|
|
52
|
+
return onTransformHarLookupResult
|
|
53
|
+
? onTransformHarLookupResult(result, params, baseURL)
|
|
54
|
+
: result;
|
|
55
|
+
};
|
|
56
|
+
(0, har_1.addHarLookupTransform)(onTransformHarLookupParamsFinal, onTransformHarLookupResultFinal);
|
|
57
|
+
// Transform requests before writing to har file
|
|
58
|
+
(0, har_1.addFlushTransform)((entries) => {
|
|
59
|
+
// Before writing to har file, filter out canceled requests
|
|
60
|
+
const filteredEntries = entries.filter((entry) => entry.time !== -1);
|
|
61
|
+
// Add x-tests-duplicate-id header for identical requests (if enabled)
|
|
62
|
+
if (shouldMarkIdenticalRequests) {
|
|
63
|
+
return (0, markIdenticalRequests_1.markIdenticalRequests)(filteredEntries);
|
|
64
|
+
}
|
|
65
|
+
return filteredEntries;
|
|
66
|
+
});
|
|
42
67
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { PlaywrightTestArgs, PlaywrightTestOptions, TestFixture } from '@playwright/test';
|
|
2
2
|
import type { MockNetworkFixtureBuilderParams } from './types';
|
|
3
|
-
export declare function mockNetworkFixtureBuilder({ shouldUpdate, forceUpdateIfHarMissing, updateTimeout, zip, url
|
|
3
|
+
export declare function mockNetworkFixtureBuilder<TestArgs extends PlaywrightTestArgs & PlaywrightTestOptions = PlaywrightTestArgs & PlaywrightTestOptions>({ shouldUpdate, forceUpdateIfHarMissing, updateTimeout, zip, url, dumpsFilePath, optionallyEnabled, ...harPatcherParams }: MockNetworkFixtureBuilderParams): [TestFixture<boolean, TestArgs>, {
|
|
4
4
|
auto: boolean;
|
|
5
5
|
scope: "test";
|
|
6
6
|
}];
|
|
@@ -3,34 +3,59 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.mockNetworkFixtureBuilder = mockNetworkFixtureBuilder;
|
|
4
4
|
const har_1 = require("../../har");
|
|
5
5
|
const har_patcher_1 = require("./har-patcher");
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
6
|
+
const fixtureFunction = async ({ baseURL: rawBaseURL, page, }, { shouldUpdate, forceUpdateIfHarMissing, updateTimeout, zip = true, url: urlMatcherBuilder, dumpsFilePath, ...harPatcherParams }, use, testInfo) => {
|
|
7
|
+
if (!rawBaseURL) {
|
|
8
|
+
throw new Error('baseURL should be specified in playwright config');
|
|
9
|
+
}
|
|
10
|
+
const baseURL = rawBaseURL.replace(/\/+$/, '');
|
|
11
|
+
(0, har_patcher_1.harPatcher)({
|
|
12
|
+
baseURL,
|
|
13
|
+
...harPatcherParams,
|
|
14
|
+
});
|
|
15
|
+
const update = Boolean(shouldUpdate);
|
|
16
|
+
const url = urlMatcherBuilder(baseURL);
|
|
17
|
+
await (0, har_1.initDumps)(page, testInfo, {
|
|
18
|
+
dumpsFilePath,
|
|
19
|
+
forceUpdateIfHarMissing,
|
|
20
|
+
updateTimeout,
|
|
21
|
+
update,
|
|
22
|
+
url,
|
|
23
|
+
zip,
|
|
24
|
+
});
|
|
25
|
+
await use(!update);
|
|
26
|
+
};
|
|
27
|
+
function mockNetworkFixtureBuilder({ shouldUpdate, forceUpdateIfHarMissing, updateTimeout, zip = true, url, dumpsFilePath, optionallyEnabled, ...harPatcherParams }) {
|
|
28
|
+
const mockNetworkFixture = async ({ baseURL, page }, use, testInfo) => {
|
|
29
|
+
return fixtureFunction({ baseURL, page }, {
|
|
30
|
+
shouldUpdate,
|
|
20
31
|
forceUpdateIfHarMissing,
|
|
21
32
|
updateTimeout,
|
|
22
|
-
|
|
33
|
+
zip,
|
|
23
34
|
url,
|
|
35
|
+
dumpsFilePath,
|
|
36
|
+
...harPatcherParams,
|
|
37
|
+
}, use, testInfo);
|
|
38
|
+
};
|
|
39
|
+
const mockNetworkFixtureWithOptionalParam = async ({ baseURL, page, enableNetworkMocking }, use, testInfo) => {
|
|
40
|
+
if (!enableNetworkMocking) {
|
|
41
|
+
return use(false);
|
|
42
|
+
}
|
|
43
|
+
return fixtureFunction({ baseURL, page }, {
|
|
44
|
+
shouldUpdate,
|
|
45
|
+
forceUpdateIfHarMissing,
|
|
46
|
+
updateTimeout,
|
|
24
47
|
zip,
|
|
25
|
-
|
|
26
|
-
|
|
48
|
+
url,
|
|
49
|
+
dumpsFilePath,
|
|
50
|
+
...harPatcherParams,
|
|
51
|
+
}, use, testInfo);
|
|
27
52
|
};
|
|
28
53
|
const fixtureOptions = {
|
|
29
54
|
auto: true,
|
|
30
55
|
scope: 'test',
|
|
31
56
|
};
|
|
32
57
|
const mockNetwork = [
|
|
33
|
-
mockNetworkFixture,
|
|
58
|
+
optionallyEnabled ? mockNetworkFixtureWithOptionalParam : mockNetworkFixture,
|
|
34
59
|
fixtureOptions,
|
|
35
60
|
];
|
|
36
61
|
return mockNetwork;
|
|
@@ -53,21 +53,43 @@ export type MockNetworkFixtureBuilderParams = {
|
|
|
53
53
|
* Callback for processing requests and responses by saving to .har. Useful for various post-processing of requests: cleaning, changing format, etc.
|
|
54
54
|
* By default, sensitive headers are removed + the base url of the request is changed to a stub
|
|
55
55
|
* @param entry The entry in .har that will be written
|
|
56
|
+
* @param baseURL The base URL of the test
|
|
56
57
|
*/
|
|
57
|
-
onHarEntryWillWrite?: (entry: Entry) => void;
|
|
58
|
+
onHarEntryWillWrite?: (entry: Entry, baseURL: string) => void;
|
|
58
59
|
/**
|
|
59
60
|
* Callback to process requests and responses written in .har before they are used
|
|
60
61
|
* Useful for reverting changes made in onHarEntryWillWrite
|
|
61
62
|
* By default, the base url templates are replaced with the actual baseUrl of the test
|
|
62
63
|
* @param entry The entry in .har that will be used
|
|
64
|
+
* @param baseURL The base URL of the test
|
|
63
65
|
*/
|
|
64
|
-
onHarEntryWillRead?: (entry: Entry) => void;
|
|
66
|
+
onHarEntryWillRead?: (entry: Entry, baseURL: string) => void;
|
|
65
67
|
/**
|
|
66
68
|
* Callback for changing search parameters of queries in .har
|
|
69
|
+
* @param params The lookup parameters
|
|
70
|
+
* @param baseURL The base URL of the test
|
|
67
71
|
*/
|
|
68
|
-
onTransformHarLookupParams?: HarLookupParamsTransformFunction
|
|
72
|
+
onTransformHarLookupParams?: (params: Parameters<HarLookupParamsTransformFunction>[0], baseURL: string) => ReturnType<HarLookupParamsTransformFunction>;
|
|
69
73
|
/**
|
|
70
74
|
* Callback for transforming the search query result into .har
|
|
75
|
+
* @param result The lookup result
|
|
76
|
+
* @param params The lookup parameters
|
|
77
|
+
* @param baseURL The base URL of the test
|
|
71
78
|
*/
|
|
72
|
-
onTransformHarLookupResult?: HarLookupResultTransformFunction
|
|
79
|
+
onTransformHarLookupResult?: (result: Parameters<HarLookupResultTransformFunction>[0], params: Parameters<HarLookupResultTransformFunction>[1], baseURL: string) => ReturnType<HarLookupResultTransformFunction>;
|
|
80
|
+
/**
|
|
81
|
+
* Allow optionally enable fixture.
|
|
82
|
+
* Set "enableNetworkMocking" fixture to turn on/off network mocking
|
|
83
|
+
* @defaultValue false `
|
|
84
|
+
*/
|
|
85
|
+
optionallyEnabled?: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Flag to enable or disable adding the x-tests-duplicate-id header for identical requests
|
|
88
|
+
* By default, the header is not added
|
|
89
|
+
* @defaultValue `false`
|
|
90
|
+
*/
|
|
91
|
+
shouldMarkIdenticalRequests?: boolean;
|
|
92
|
+
};
|
|
93
|
+
export type OptionallyEnabledTestArgs = {
|
|
94
|
+
enableNetworkMocking?: boolean;
|
|
73
95
|
};
|
package/har/index.d.ts
CHANGED
|
@@ -10,5 +10,5 @@ export { clearHeaders } from './clearHeaders';
|
|
|
10
10
|
export { initDumps } from './initDumps';
|
|
11
11
|
export { replaceBaseUrlInEntry } from './replaceBaseUrlInEntry';
|
|
12
12
|
export { setExtraHash } from './setExtraHash';
|
|
13
|
-
export type { HARFile, Entry } from './types';
|
|
13
|
+
export type { HARFile, Entry, Header, LocalUtilsHarLookupParams, LocalUtilsHarLookupResult, QueryParameter, } from './types';
|
|
14
14
|
export { defaultDumpsFilePathBuilder, dumpsPathBuldeWithSlugBuilder } from './dumpsFilePathBulders';
|
|
@@ -8,7 +8,7 @@ const escape_string_regexp_1 = __importDefault(require("escape-string-regexp"));
|
|
|
8
8
|
/**
|
|
9
9
|
* Replaces the base URL in the HAR file request entry
|
|
10
10
|
*
|
|
11
|
-
* @param entry
|
|
11
|
+
* @param entry Recording from HAR file
|
|
12
12
|
* @param fromUrl URL to replace
|
|
13
13
|
* @param toUrl URL to replace with
|
|
14
14
|
*/
|
package/package.json
CHANGED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { LocalUtilsHarLookupParams } from '../har';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a transformer for automatically adding the x-tests-duplicate-id header
|
|
4
|
+
* to requests when searching in a HAR file.
|
|
5
|
+
*
|
|
6
|
+
* This allows distinguishing identical requests without changing test code.
|
|
7
|
+
*
|
|
8
|
+
* The header is NOT added to the first request (for backward compatibility with old HARs),
|
|
9
|
+
* and is added starting from the second: 2, 3, 4...
|
|
10
|
+
*/
|
|
11
|
+
export declare function createDuplicateIdTransform(): (params: LocalUtilsHarLookupParams) => LocalUtilsHarLookupParams;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createDuplicateIdTransform = createDuplicateIdTransform;
|
|
4
|
+
const requestKeyService_1 = require("./requestKeyService");
|
|
5
|
+
/**
|
|
6
|
+
* Creates a transformer for automatically adding the x-tests-duplicate-id header
|
|
7
|
+
* to requests when searching in a HAR file.
|
|
8
|
+
*
|
|
9
|
+
* This allows distinguishing identical requests without changing test code.
|
|
10
|
+
*
|
|
11
|
+
* The header is NOT added to the first request (for backward compatibility with old HARs),
|
|
12
|
+
* and is added starting from the second: 2, 3, 4...
|
|
13
|
+
*/
|
|
14
|
+
function createDuplicateIdTransform() {
|
|
15
|
+
const requestCounts = new Map();
|
|
16
|
+
return (params) => {
|
|
17
|
+
const key = (0, requestKeyService_1.createRequestKeyFromLookupParams)(params);
|
|
18
|
+
// Increment the counter for this request
|
|
19
|
+
const count = (requestCounts.get(key) || 0) + 1;
|
|
20
|
+
requestCounts.set(key, count);
|
|
21
|
+
// Add header only starting from the second call
|
|
22
|
+
// This ensures backward compatibility with old HAR files,
|
|
23
|
+
// where requests did not have the x-tests-duplicate-id header
|
|
24
|
+
if (count > 1) {
|
|
25
|
+
params.headers.push({
|
|
26
|
+
name: 'x-tests-duplicate-id',
|
|
27
|
+
value: String(count),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
return params;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Entry } from '../har';
|
|
2
|
+
/**
|
|
3
|
+
* Adds the x-tests-duplicate-id header only for duplicate requests.
|
|
4
|
+
*
|
|
5
|
+
* The first request remains without a header for backward compatibility with old HAR files.
|
|
6
|
+
* Starting from the second, requests receive headers: 2, 3, 4...
|
|
7
|
+
*
|
|
8
|
+
*/
|
|
9
|
+
export declare const markIdenticalRequests: (entries: Entry[]) => Entry[];
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.markIdenticalRequests = void 0;
|
|
4
|
+
const requestKeyService_1 = require("./requestKeyService");
|
|
5
|
+
/**
|
|
6
|
+
* Adds the x-tests-duplicate-id header only for duplicate requests.
|
|
7
|
+
*
|
|
8
|
+
* The first request remains without a header for backward compatibility with old HAR files.
|
|
9
|
+
* Starting from the second, requests receive headers: 2, 3, 4...
|
|
10
|
+
*
|
|
11
|
+
*/
|
|
12
|
+
const markIdenticalRequests = (entries) => {
|
|
13
|
+
const currentCounts = new Map();
|
|
14
|
+
entries.forEach((entry) => {
|
|
15
|
+
const key = (0, requestKeyService_1.createRequestKeyFromEntry)(entry);
|
|
16
|
+
const currentCount = (currentCounts.get(key) || 0) + 1;
|
|
17
|
+
currentCounts.set(key, currentCount);
|
|
18
|
+
// Add header only if this is not the first instance (currentCount > 1)
|
|
19
|
+
if (currentCount > 1) {
|
|
20
|
+
entry.request.headers.push({
|
|
21
|
+
name: 'x-tests-duplicate-id',
|
|
22
|
+
value: String(currentCount),
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
return entries;
|
|
27
|
+
};
|
|
28
|
+
exports.markIdenticalRequests = markIdenticalRequests;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Entry, Header, LocalUtilsHarLookupParams, QueryParameter } from '../har';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a unique request key based on its parameters.
|
|
4
|
+
* Used to identify identical requests when working with HAR files.
|
|
5
|
+
*
|
|
6
|
+
* The key is formed based on:
|
|
7
|
+
* - HTTP method
|
|
8
|
+
* - URL (without query parameters)
|
|
9
|
+
* - Query parameters (sorted)
|
|
10
|
+
* - Headers (sorted, excluding service headers)
|
|
11
|
+
*/
|
|
12
|
+
export declare function createRequestKey(params: RequestKeyParams): string;
|
|
13
|
+
/**
|
|
14
|
+
* Parameters for creating a request key
|
|
15
|
+
*/
|
|
16
|
+
type RequestKeyParams = {
|
|
17
|
+
method: string;
|
|
18
|
+
url: string;
|
|
19
|
+
headers: Header[];
|
|
20
|
+
queryString?: QueryParameter[];
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Creates a key from Entry (when writing HAR)
|
|
24
|
+
*/
|
|
25
|
+
export declare function createRequestKeyFromEntry(entry: Entry): string;
|
|
26
|
+
/**
|
|
27
|
+
* Creates a key from LocalUtilsHarLookupParams (when replaying)
|
|
28
|
+
*/
|
|
29
|
+
export declare function createRequestKeyFromLookupParams(params: LocalUtilsHarLookupParams): string;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createRequestKey = createRequestKey;
|
|
4
|
+
exports.createRequestKeyFromEntry = createRequestKeyFromEntry;
|
|
5
|
+
exports.createRequestKeyFromLookupParams = createRequestKeyFromLookupParams;
|
|
6
|
+
/**
|
|
7
|
+
* Creates a unique request key based on its parameters.
|
|
8
|
+
* Used to identify identical requests when working with HAR files.
|
|
9
|
+
*
|
|
10
|
+
* The key is formed based on:
|
|
11
|
+
* - HTTP method
|
|
12
|
+
* - URL (without query parameters)
|
|
13
|
+
* - Query parameters (sorted)
|
|
14
|
+
* - Headers (sorted, excluding service headers)
|
|
15
|
+
*/
|
|
16
|
+
function createRequestKey(params) {
|
|
17
|
+
const method = params.method;
|
|
18
|
+
const url = params.url;
|
|
19
|
+
// Parse URL to extract the base part and query parameters
|
|
20
|
+
const urlObj = new URL(url);
|
|
21
|
+
// Get sorted query parameters
|
|
22
|
+
const sortedQueryString = params.queryString
|
|
23
|
+
? sortQueryStringFromArray(params.queryString)
|
|
24
|
+
: sortQueryStringFromUrl(urlObj);
|
|
25
|
+
// Get sorted headers (excluding service headers)
|
|
26
|
+
const sortedHeaders = sortHeaders(params.headers);
|
|
27
|
+
// Form the key: method + URL without query + query parameters + headers
|
|
28
|
+
return `${method}:${urlObj.origin}${urlObj.pathname}:${sortedQueryString}:${sortedHeaders}`;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Creates a key from Entry (when writing HAR)
|
|
32
|
+
*/
|
|
33
|
+
function createRequestKeyFromEntry(entry) {
|
|
34
|
+
return createRequestKey({
|
|
35
|
+
method: entry.request.method,
|
|
36
|
+
url: entry.request.url,
|
|
37
|
+
headers: entry.request.headers,
|
|
38
|
+
queryString: entry.request.queryString,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Creates a key from LocalUtilsHarLookupParams (when replaying)
|
|
43
|
+
*/
|
|
44
|
+
function createRequestKeyFromLookupParams(params) {
|
|
45
|
+
return createRequestKey({
|
|
46
|
+
method: params.method,
|
|
47
|
+
url: params.url,
|
|
48
|
+
headers: params.headers,
|
|
49
|
+
// queryString is automatically extracted from URL
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Sorts query parameters from QueryParameter array
|
|
54
|
+
*/
|
|
55
|
+
function sortQueryStringFromArray(queryString) {
|
|
56
|
+
return [...queryString]
|
|
57
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
58
|
+
.map((queryParameter) => `${queryParameter.name}=${queryParameter.value}`)
|
|
59
|
+
.join('&');
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Sorts query parameters from URL
|
|
63
|
+
*/
|
|
64
|
+
function sortQueryStringFromUrl(urlObj) {
|
|
65
|
+
return Array.from(urlObj.searchParams.entries())
|
|
66
|
+
.sort((a, b) => a[0].localeCompare(b[0]))
|
|
67
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
68
|
+
.join('&');
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Sorts headers, excluding service headers
|
|
72
|
+
*/
|
|
73
|
+
function sortHeaders(headers) {
|
|
74
|
+
return [...headers]
|
|
75
|
+
.filter((header) => header.name.toLowerCase() !== 'x-tests-duplicate-id')
|
|
76
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
77
|
+
.map((header) => `${header.name.toLowerCase()}:${header.value}`)
|
|
78
|
+
.join('|');
|
|
79
|
+
}
|