@zohodesk/testinglibrary 4.1.4 → 4.1.5
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 +8 -0
- package/build/common/data-generator/steps/DataGenerator.spec.js +5 -5
- package/build/common/data-generator/steps/DataGeneratorStepsHelper.js +2 -2
- package/build/core/dataGenerator/DataGenerator.js +9 -4
- package/build/core/dataGenerator/DataGeneratorError.js +12 -2
- package/build/core/dataGenerator/authenticator/Authenticator.js +37 -0
- package/build/core/dataGenerator/authenticator/CookieAuthenticator.js +48 -0
- package/build/core/dataGenerator/authenticator/JWTauthenticator.js +64 -0
- package/build/core/dataGenerator/authenticator/OAuthAuthenticator.js +26 -0
- package/build/core/dataGenerator/authenticator/OrgOAuthAuthenticator.js +26 -0
- package/build/core/dataGenerator/authenticator/cookieAuthConfig.js +11 -0
- package/build/test/core/dataGenerator/authenticator/__tests__/Authenticator.test.js +88 -0
- package/build/test/core/dataGenerator/authenticator/__tests__/JWTauthenticator.test.js +129 -0
- package/build/test/core/dataGenerator/authenticator/__tests__/OAuthAuthenticator.test.js +39 -0
- package/build/test/core/dataGenerator/authenticator/__tests__/OrgOAuthAuthenticator.test.js +39 -0
- package/build/test/core/playwright/__tests__/validateFeature.test.js +4 -1
- package/build/test/core/playwright/runner/__tests__/RunnerHelper.test.js +3 -0
- package/build/test/core/playwright/runner/__tests__/SpawnRunner.test.js +3 -0
- package/npm-shrinkwrap.json +15 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,6 +17,14 @@
|
|
|
17
17
|
|
|
18
18
|
- npm run report
|
|
19
19
|
|
|
20
|
+
|
|
21
|
+
### v4.1.5/v3.3.3 - 19-06-2026
|
|
22
|
+
|
|
23
|
+
#### Refactor
|
|
24
|
+
- Authenticator module refactored to use static imports instead of dynamic imports
|
|
25
|
+
- Removed unnecessary `extends Authenticator` from concrete authenticator classes (`JWTauthenticator`, `OAuthAuthenticator`, `OrgOAuthAuthenticator`, `CookieAuthenticator`) to eliminate circular dependency
|
|
26
|
+
- Unit test cases updated to align with actual source behavior
|
|
27
|
+
|
|
20
28
|
### v4.1.4/v3.3.3 - 18-06-2026
|
|
21
29
|
|
|
22
30
|
#### Bugfix
|
|
@@ -4,16 +4,16 @@ import { generateAndCacheTestData } from './DataGeneratorStepsHelper';
|
|
|
4
4
|
const { Given } = createBdd();
|
|
5
5
|
|
|
6
6
|
Given('generate a {string} entity {string} with generator {string}', async ({ page, context, i18N, cacheLayer, executionContext}, module, entityName, generatorName, dataTable) => {
|
|
7
|
-
await generateAndCacheTestData(executionContext, "template", generatorName, dataTable, cacheLayer, entityName);
|
|
7
|
+
await generateAndCacheTestData({ executionContext, type: "template", identifier: generatorName, dataTable, cacheLayer, entityName, page });
|
|
8
8
|
});
|
|
9
9
|
|
|
10
10
|
Given('generate a {string} entity {string} with API {string}', async ({ page, context, i18N, cacheLayer, executionContext}, module, entityName, operationId, dataTable) => {
|
|
11
|
-
await generateAndCacheTestData(executionContext, "API", operationId, dataTable, cacheLayer, entityName);
|
|
11
|
+
await generateAndCacheTestData({ executionContext, type: "API", identifier: operationId, dataTable, cacheLayer, entityName, page });
|
|
12
12
|
});
|
|
13
13
|
Given('generate a {string} entity {string} with generator {string} using {string} profile', async ({ page, context, i18N, cacheLayer, executionContext}, module, entityName, generatorName, profile, dataTable) => {
|
|
14
|
-
await generateAndCacheTestData(executionContext, "template", generatorName, dataTable, cacheLayer, entityName, profile);
|
|
14
|
+
await generateAndCacheTestData({ executionContext, type: "template", identifier: generatorName, dataTable, cacheLayer, entityName, profile, page });
|
|
15
15
|
});
|
|
16
16
|
|
|
17
17
|
Given('generate a {string} entity {string} with API {string} using {string} profile', async ({ page, context, i18N, cacheLayer, executionContext}, module, entityName, operationId, profile, dataTable) => {
|
|
18
|
-
await generateAndCacheTestData(executionContext, "API", operationId, dataTable, cacheLayer, entityName, profile);
|
|
19
|
-
});
|
|
18
|
+
await generateAndCacheTestData({ executionContext, type: "API", identifier: operationId, dataTable, cacheLayer, entityName, profile, page });
|
|
19
|
+
});
|
|
@@ -4,7 +4,7 @@ import DataGenerator from '@zohodesk/testinglibrary/DataGenerator';
|
|
|
4
4
|
|
|
5
5
|
const dataGenerator = new DataGenerator();
|
|
6
6
|
|
|
7
|
-
export async function generateAndCacheTestData(executionContext, type, identifier, dataTable, cacheLayer, entityName, profile = null) {
|
|
7
|
+
export async function generateAndCacheTestData({ executionContext, type, identifier, dataTable, cacheLayer, entityName, profile = null, page = null }) {
|
|
8
8
|
let actorInfo;
|
|
9
9
|
const testInfo = test.info();
|
|
10
10
|
const scenarioName = testInfo.title.split('/').pop() || 'Unknown Scenario';
|
|
@@ -16,6 +16,6 @@ export async function generateAndCacheTestData(executionContext, type, identifie
|
|
|
16
16
|
actorInfo = executionContext.actorInfo;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
const generatedData = await dataGenerator.generate(testInfo, actorInfo, type, identifier, scenarioName, dataTable ? dataTable.hashes() : []);
|
|
19
|
+
const generatedData = await dataGenerator.generate(testInfo, actorInfo, type, identifier, scenarioName, dataTable ? dataTable.hashes() : [], page);
|
|
20
20
|
await cacheLayer.set(entityName, generatedData.data);
|
|
21
21
|
}
|
|
@@ -9,6 +9,7 @@ var _path = _interopRequireDefault(require("path"));
|
|
|
9
9
|
var _fs = _interopRequireDefault(require("fs"));
|
|
10
10
|
var _logger = require("../../utils/logger");
|
|
11
11
|
var _DataGeneratorHelper = require("./DataGeneratorHelper");
|
|
12
|
+
var _Authenticator = _interopRequireDefault(require("./authenticator/Authenticator.js"));
|
|
12
13
|
var _helpers = require("@zohodesk/testinglibrary/helpers");
|
|
13
14
|
var _DataGeneratorError = require("./DataGeneratorError");
|
|
14
15
|
function _classPrivateMethodInitSpec(e, a) { _checkPrivateRedeclaration(e, a), a.add(e); }
|
|
@@ -19,7 +20,7 @@ class DataGenerator {
|
|
|
19
20
|
constructor() {
|
|
20
21
|
_classPrivateMethodInitSpec(this, _DataGenerator_brand);
|
|
21
22
|
}
|
|
22
|
-
async generate(testInfo, actorInfo, generatorType, generatorName, scenarioName, dataTable) {
|
|
23
|
+
async generate(testInfo, actorInfo, generatorType, generatorName, scenarioName, dataTable, page = null) {
|
|
23
24
|
try {
|
|
24
25
|
let generators;
|
|
25
26
|
if (generatorType === 'API') {
|
|
@@ -28,7 +29,7 @@ class DataGenerator {
|
|
|
28
29
|
generators = await _assertClassBrand(_DataGenerator_brand, this, _getGenerator).call(this, testInfo, generatorName);
|
|
29
30
|
}
|
|
30
31
|
const processedGenerators = await (0, _DataGeneratorHelper.processGenerator)(generators, dataTable);
|
|
31
|
-
const apiPayload = await _assertClassBrand(_DataGenerator_brand, this, _constructApiPayload).call(this, scenarioName, processedGenerators, actorInfo);
|
|
32
|
+
const apiPayload = await _assertClassBrand(_DataGenerator_brand, this, _constructApiPayload).call(this, scenarioName, processedGenerators, actorInfo, page);
|
|
32
33
|
const response = await (0, _DataGeneratorHelper.makeRequest)(process.env.DG_SERVICE_DOMAIN + process.env.DG_SERVICE_API_PATH, apiPayload);
|
|
33
34
|
_logger.Logger.log(_logger.Logger.INFO_TYPE, `Generated response for the generator: ${generatorName} for scenario: ${scenarioName}, Response: ${JSON.stringify(response)}`);
|
|
34
35
|
return response;
|
|
@@ -72,12 +73,12 @@ async function _generateAPIGenerator(operationId) {
|
|
|
72
73
|
name: operationId
|
|
73
74
|
}];
|
|
74
75
|
}
|
|
75
|
-
async function _constructApiPayload(scenarioName, processedGenerators, actorInfo) {
|
|
76
|
+
async function _constructApiPayload(scenarioName, processedGenerators, actorInfo, page = null) {
|
|
76
77
|
const dataGeneratorObj = actorInfo['data-generator'];
|
|
77
78
|
if (!dataGeneratorObj) {
|
|
78
79
|
throw new _DataGeneratorError.DataGeneratorConfigurationError(`Data Generator configuration is missing for the profile: ${actorInfo['profile']}`);
|
|
79
80
|
}
|
|
80
|
-
|
|
81
|
+
let apiPayload = {
|
|
81
82
|
scenario_name: scenarioName,
|
|
82
83
|
data_generation_templates: processedGenerators,
|
|
83
84
|
...dataGeneratorObj
|
|
@@ -94,6 +95,10 @@ async function _constructApiPayload(scenarioName, processedGenerators, actorInfo
|
|
|
94
95
|
environmentDetails.host = domainUrl.origin;
|
|
95
96
|
}
|
|
96
97
|
apiPayload.environmentDetails = environmentDetails;
|
|
98
|
+
|
|
99
|
+
// Only perform auth augmentation via Authenticator
|
|
100
|
+
apiPayload = await new _Authenticator.default().constructPayload(apiPayload, actorInfo, page);
|
|
101
|
+
_logger.Logger.log(_logger.Logger.INFO_TYPE, `Final API Payload after authentication augmentation: ${JSON.stringify(apiPayload)}`);
|
|
97
102
|
return apiPayload;
|
|
98
103
|
}
|
|
99
104
|
var _default = exports.default = DataGenerator;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.GeneratorError = exports.DataGeneratorError = exports.DataGeneratorConfigurationError = void 0;
|
|
6
|
+
exports.GeneratorError = exports.DataGeneratorError = exports.DataGeneratorConfigurationError = exports.AuthenticationError = void 0;
|
|
7
7
|
class DataGeneratorError extends Error {
|
|
8
8
|
constructor(message) {
|
|
9
9
|
super(message);
|
|
@@ -47,4 +47,14 @@ class GeneratorError extends DataGeneratorError {
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
|
-
exports.GeneratorError = GeneratorError;
|
|
50
|
+
exports.GeneratorError = GeneratorError;
|
|
51
|
+
class AuthenticationError extends DataGeneratorError {
|
|
52
|
+
constructor(message) {
|
|
53
|
+
super(message);
|
|
54
|
+
this.name = 'AuthenticationError';
|
|
55
|
+
if (Error.captureStackTrace) {
|
|
56
|
+
Error.captureStackTrace(this, AuthenticationError);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
exports.AuthenticationError = AuthenticationError;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.default = void 0;
|
|
8
|
+
var _DataGeneratorError = require("../DataGeneratorError.js");
|
|
9
|
+
var _JWTauthenticator = _interopRequireDefault(require("./JWTauthenticator.js"));
|
|
10
|
+
var _OAuthAuthenticator = _interopRequireDefault(require("./OAuthAuthenticator.js"));
|
|
11
|
+
var _OrgOAuthAuthenticator = _interopRequireDefault(require("./OrgOAuthAuthenticator.js"));
|
|
12
|
+
var _CookieAuthenticator = _interopRequireDefault(require("./CookieAuthenticator.js"));
|
|
13
|
+
class Authenticator {
|
|
14
|
+
// Only route authentication augmentations; do not modify environment or base payload here.
|
|
15
|
+
async constructPayload(apiPayload, actorInfo, page = null) {
|
|
16
|
+
var _actorInfo$dataGener;
|
|
17
|
+
const authenticationLoaders = {
|
|
18
|
+
'jwt-auth': () => new _JWTauthenticator.default(),
|
|
19
|
+
'oauth2': () => new _OAuthAuthenticator.default(),
|
|
20
|
+
'org-oauth': () => new _OrgOAuthAuthenticator.default(),
|
|
21
|
+
'cookie-auth': () => new _CookieAuthenticator.default()
|
|
22
|
+
};
|
|
23
|
+
const authentications = (actorInfo === null || actorInfo === void 0 || (_actorInfo$dataGener = actorInfo['data-generator']) === null || _actorInfo$dataGener === void 0 || (_actorInfo$dataGener = _actorInfo$dataGener.account) === null || _actorInfo$dataGener === void 0 ? void 0 : _actorInfo$dataGener.authentications) || [];
|
|
24
|
+
const authenticationTypes = authentications.map(auth => String(auth).toLowerCase()).filter(Boolean);
|
|
25
|
+
for (const authenticationType of authenticationTypes) {
|
|
26
|
+
const authenticatorLoader = authenticationLoaders[authenticationType];
|
|
27
|
+
if (!authenticatorLoader) {
|
|
28
|
+
throw new _DataGeneratorError.AuthenticationError(`Unknown authentication type: "${authenticationType}". Supported types are: ${Object.keys(authenticationLoaders).join(', ')}`);
|
|
29
|
+
}
|
|
30
|
+
const authenticatorInstance = await authenticatorLoader();
|
|
31
|
+
apiPayload = await authenticatorInstance.constructPayload(apiPayload, actorInfo, page);
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
return apiPayload;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
exports.default = Authenticator;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _cookieAuthConfig = require("./cookieAuthConfig.js");
|
|
8
|
+
var _DataGeneratorError = require("../DataGeneratorError.js");
|
|
9
|
+
/**
|
|
10
|
+
* CookieAuthenticator prepares the payload for cookie-based authentication.
|
|
11
|
+
* It extracts cookies from the browser page context and attaches them
|
|
12
|
+
* as cookieAuthHeaders in the account block for the data generator service.
|
|
13
|
+
*/
|
|
14
|
+
class CookieAuthenticator {
|
|
15
|
+
async constructPayload(apiPayload, actorInfo, page = null) {
|
|
16
|
+
if (!page) {
|
|
17
|
+
throw new _DataGeneratorError.AuthenticationError('Cookie authentication failed: "page" is required but was not provided');
|
|
18
|
+
}
|
|
19
|
+
const {
|
|
20
|
+
csrfHeaderName
|
|
21
|
+
} = _cookieAuthConfig.COOKIE_AUTH_CONFIG;
|
|
22
|
+
const {
|
|
23
|
+
csrfToken,
|
|
24
|
+
cookieString
|
|
25
|
+
} = await _getCookies.call(CookieAuthenticator, page);
|
|
26
|
+
apiPayload.account.authHeaders = {
|
|
27
|
+
Cookie: cookieString,
|
|
28
|
+
[csrfHeaderName]: csrfToken
|
|
29
|
+
};
|
|
30
|
+
return apiPayload;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async function _getCookies(page) {
|
|
34
|
+
const {
|
|
35
|
+
csrfCookieName,
|
|
36
|
+
csrfParamName
|
|
37
|
+
} = _cookieAuthConfig.COOKIE_AUTH_CONFIG;
|
|
38
|
+
const context = page.context();
|
|
39
|
+
const pageUrl = new URL(page.url());
|
|
40
|
+
const currentDomain = pageUrl.hostname;
|
|
41
|
+
const cookies = await context.cookies([page.url()]);
|
|
42
|
+
const csrfCookies = cookies.filter(cookie => cookie.domain.includes(currentDomain) && cookie.name === csrfCookieName);
|
|
43
|
+
return {
|
|
44
|
+
csrfToken: csrfCookies.map(c => `${csrfParamName}=${c.value}`).join('; '),
|
|
45
|
+
cookieString: cookies.map(c => `${c.name}=${c.value}`).join('; ')
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
var _default = exports.default = CookieAuthenticator;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _DataGeneratorError = require("../DataGeneratorError.js");
|
|
8
|
+
/**
|
|
9
|
+
* JWTauthenticator constructs payload by fetching a JWT access token
|
|
10
|
+
* from the authenticator app and attaching it to the account.jwt config.
|
|
11
|
+
*/
|
|
12
|
+
class JWTauthenticator {
|
|
13
|
+
async constructPayload(apiPayload, actorInfo) {
|
|
14
|
+
const account = actorInfo['data-generator'].account;
|
|
15
|
+
if (!account) {
|
|
16
|
+
throw new _DataGeneratorError.AuthenticationError('JWT authentication failed: "account" is missing in actor configuration');
|
|
17
|
+
}
|
|
18
|
+
const jwtConf = account['jwt-oauth'];
|
|
19
|
+
if (!jwtConf) {
|
|
20
|
+
throw new _DataGeneratorError.AuthenticationError('JWT authentication failed: "jwt-oauth" is missing in actor configuration');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Build payload for authenticator service
|
|
24
|
+
const userToken = encodeURIComponent((actorInfo === null || actorInfo === void 0 ? void 0 : actorInfo.email) || '');
|
|
25
|
+
const jwtAuthUrl = process.env.DG_JWT_AUTH_URL;
|
|
26
|
+
const responseType = jwtConf.response_type;
|
|
27
|
+
const clientId = jwtConf.client_id;
|
|
28
|
+
const tokenUrl = jwtConf.token_url;
|
|
29
|
+
const domainUrl = process.env.DG_DOMAIN_URL;
|
|
30
|
+
const authPayload = {
|
|
31
|
+
user_token: userToken,
|
|
32
|
+
jwt_secret: jwtConf.jwt_secret,
|
|
33
|
+
jwt_auth_url: jwtAuthUrl,
|
|
34
|
+
response_type: responseType,
|
|
35
|
+
client_id: clientId,
|
|
36
|
+
token_url: tokenUrl,
|
|
37
|
+
domain_url: domainUrl
|
|
38
|
+
};
|
|
39
|
+
const authenticatorApi = process.env.DG_JWT_AUTHENTICATOR_API;
|
|
40
|
+
const resp = await fetch(authenticatorApi, {
|
|
41
|
+
method: 'POST',
|
|
42
|
+
headers: {
|
|
43
|
+
'Content-Type': 'application/json'
|
|
44
|
+
},
|
|
45
|
+
body: JSON.stringify(authPayload)
|
|
46
|
+
});
|
|
47
|
+
if (!resp.ok) {
|
|
48
|
+
const errorBody = await resp.text();
|
|
49
|
+
throw new Error(`JWT authenticator error! status: ${resp.status}, body: ${errorBody}`);
|
|
50
|
+
}
|
|
51
|
+
const json = await resp.json();
|
|
52
|
+
const accessToken = json === null || json === void 0 ? void 0 : json.access_token;
|
|
53
|
+
if (!accessToken) {
|
|
54
|
+
throw new Error('JWT authenticator did not return access_token');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Attach access token to authHeaders so downstream API calls can use it directly
|
|
58
|
+
apiPayload.account = apiPayload.account || {};
|
|
59
|
+
apiPayload.account.authHeaders = apiPayload.account.authHeaders || {};
|
|
60
|
+
apiPayload.account.authHeaders.Authorization = `Zoho-oauthtoken ${accessToken}`;
|
|
61
|
+
return apiPayload;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
var _default = exports.default = JWTauthenticator;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _DataGeneratorError = require("../DataGeneratorError.js");
|
|
8
|
+
/**
|
|
9
|
+
* OAuthAuthenticator prepares the payload for OAuth2-based authentication.
|
|
10
|
+
* It ensures oauth2 config is present in the account block so the
|
|
11
|
+
* data generator service can use it.
|
|
12
|
+
*/
|
|
13
|
+
class OAuthAuthenticator {
|
|
14
|
+
async constructPayload(apiPayload, actorInfo) {
|
|
15
|
+
const account = actorInfo['data-generator'].account;
|
|
16
|
+
if (!account) {
|
|
17
|
+
throw new _DataGeneratorError.AuthenticationError('OAuth2 authentication failed: "account" is missing in actor configuration');
|
|
18
|
+
}
|
|
19
|
+
if (!account.oauth2) {
|
|
20
|
+
throw new _DataGeneratorError.AuthenticationError('OAuth2 authentication failed: "oauth2" is missing in actor configuration');
|
|
21
|
+
}
|
|
22
|
+
apiPayload.account.oauth2 = account.oauth2;
|
|
23
|
+
return apiPayload;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
var _default = exports.default = OAuthAuthenticator;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _DataGeneratorError = require("../DataGeneratorError.js");
|
|
8
|
+
/**
|
|
9
|
+
* OrgOAuthAuthenticator prepares the payload for org_oauth authentication.
|
|
10
|
+
* It ensures org_oauth config is present in the account block so the
|
|
11
|
+
* data generator service can use it.
|
|
12
|
+
*/
|
|
13
|
+
class OrgOAuthAuthenticator {
|
|
14
|
+
async constructPayload(apiPayload, actorInfo) {
|
|
15
|
+
const account = actorInfo['data-generator'].account;
|
|
16
|
+
if (!account) {
|
|
17
|
+
throw new _DataGeneratorError.AuthenticationError('OrgOAuth authentication failed: "account" is missing in actor configuration');
|
|
18
|
+
}
|
|
19
|
+
if (!account.org_oauth) {
|
|
20
|
+
throw new _DataGeneratorError.AuthenticationError('OrgOAuth authentication failed: "org_oauth" is missing in actor configuration');
|
|
21
|
+
}
|
|
22
|
+
apiPayload.account.org_oauth = account.org_oauth;
|
|
23
|
+
return apiPayload;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
var _default = exports.default = OrgOAuthAuthenticator;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.COOKIE_AUTH_CONFIG = void 0;
|
|
7
|
+
const COOKIE_AUTH_CONFIG = exports.COOKIE_AUTH_CONFIG = {
|
|
8
|
+
csrfHeaderName: 'X-ZCSRF-TOKEN',
|
|
9
|
+
csrfCookieName: 'crmcsr',
|
|
10
|
+
csrfParamName: 'crmcsrfparam'
|
|
11
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
var _Authenticator = _interopRequireDefault(require("../../../../../../src/core/dataGenerator/authenticator/Authenticator"));
|
|
5
|
+
describe('Authenticator', () => {
|
|
6
|
+
it('should throw an error when an unknown authentication type is present', async () => {
|
|
7
|
+
const authenticator = new _Authenticator.default();
|
|
8
|
+
const apiPayload = {
|
|
9
|
+
account: {
|
|
10
|
+
name: 'sample'
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
const actorInfo = {
|
|
14
|
+
'data-generator': {
|
|
15
|
+
account: {
|
|
16
|
+
authentications: ['apikey', 'basic']
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
await expect(authenticator.constructPayload(apiPayload, actorInfo)).rejects.toThrow('Unknown authentication type: "apikey"');
|
|
21
|
+
});
|
|
22
|
+
it('should route oauth2 authentication and merge oauth2 config into payload', async () => {
|
|
23
|
+
const authenticator = new _Authenticator.default();
|
|
24
|
+
const apiPayload = {
|
|
25
|
+
account: {}
|
|
26
|
+
};
|
|
27
|
+
const actorInfo = {
|
|
28
|
+
'data-generator': {
|
|
29
|
+
account: {
|
|
30
|
+
authentications: ['oauth2'],
|
|
31
|
+
oauth2: {
|
|
32
|
+
client_id: 'client_1',
|
|
33
|
+
client_secret: 'secret_1'
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const result = await authenticator.constructPayload(apiPayload, actorInfo);
|
|
39
|
+
expect(result.account.oauth2).toEqual({
|
|
40
|
+
client_id: 'client_1',
|
|
41
|
+
client_secret: 'secret_1'
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
it('should treat authentication values case-insensitively and deduplicate them', async () => {
|
|
45
|
+
const authenticator = new _Authenticator.default();
|
|
46
|
+
const apiPayload = {
|
|
47
|
+
account: {}
|
|
48
|
+
};
|
|
49
|
+
const actorInfo = {
|
|
50
|
+
'data-generator': {
|
|
51
|
+
account: {
|
|
52
|
+
authentications: ['OAUTH2', 'oauth2', 'OAuth2'],
|
|
53
|
+
oauth2: {
|
|
54
|
+
scope: 'read'
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
const result = await authenticator.constructPayload(apiPayload, actorInfo);
|
|
60
|
+
expect(result.account.oauth2).toEqual({
|
|
61
|
+
scope: 'read'
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
it('should apply only the first supported authentication type', async () => {
|
|
65
|
+
const authenticator = new _Authenticator.default();
|
|
66
|
+
const apiPayload = {
|
|
67
|
+
account: {}
|
|
68
|
+
};
|
|
69
|
+
const actorInfo = {
|
|
70
|
+
'data-generator': {
|
|
71
|
+
account: {
|
|
72
|
+
authentications: ['oauth2', 'org-oauth'],
|
|
73
|
+
oauth2: {
|
|
74
|
+
client_id: 'oauth-client'
|
|
75
|
+
},
|
|
76
|
+
org_oauth: {
|
|
77
|
+
org_id: 'org-101'
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const result = await authenticator.constructPayload(apiPayload, actorInfo);
|
|
83
|
+
expect(result.account.oauth2).toEqual({
|
|
84
|
+
client_id: 'oauth-client'
|
|
85
|
+
});
|
|
86
|
+
expect(result.account.org_oauth).toBeUndefined();
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
var _JWTauthenticator = _interopRequireDefault(require("../../../../../../src/core/dataGenerator/authenticator/JWTauthenticator"));
|
|
5
|
+
describe('JWTauthenticator', () => {
|
|
6
|
+
let fetchMock;
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
fetchMock = jest.fn();
|
|
9
|
+
global.fetch = fetchMock;
|
|
10
|
+
process.env.DG_JWT_AUTH_URL = 'https://jwt-auth.example.com';
|
|
11
|
+
process.env.DG_DOMAIN_URL = 'https://domain.example.com';
|
|
12
|
+
process.env.DG_JWT_AUTHENTICATOR_API = 'https://authenticator.example.com/jwtToken';
|
|
13
|
+
});
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
delete global.fetch;
|
|
16
|
+
delete process.env.DG_JWT_AUTH_URL;
|
|
17
|
+
delete process.env.DG_DOMAIN_URL;
|
|
18
|
+
delete process.env.DG_JWT_AUTHENTICATOR_API;
|
|
19
|
+
});
|
|
20
|
+
it('should throw when jwt-oauth config is missing', async () => {
|
|
21
|
+
const jwtAuthenticator = new _JWTauthenticator.default();
|
|
22
|
+
const apiPayload = {
|
|
23
|
+
account: {}
|
|
24
|
+
};
|
|
25
|
+
const actorInfo = {
|
|
26
|
+
email: 'user@example.com',
|
|
27
|
+
'data-generator': {
|
|
28
|
+
account: {}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
await expect(jwtAuthenticator.constructPayload(apiPayload, actorInfo)).rejects.toThrow('JWT authentication failed: "jwt-oauth" is missing in actor configuration');
|
|
32
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
33
|
+
});
|
|
34
|
+
it('should fetch jwt access token and set it in authHeaders', async () => {
|
|
35
|
+
fetchMock.mockResolvedValue({
|
|
36
|
+
ok: true,
|
|
37
|
+
json: async () => ({
|
|
38
|
+
access_token: 'access-token-123'
|
|
39
|
+
})
|
|
40
|
+
});
|
|
41
|
+
const jwtAuthenticator = new _JWTauthenticator.default();
|
|
42
|
+
const apiPayload = {
|
|
43
|
+
account: {}
|
|
44
|
+
};
|
|
45
|
+
const actorInfo = {
|
|
46
|
+
email: 'user.one@example.com',
|
|
47
|
+
'data-generator': {
|
|
48
|
+
account: {
|
|
49
|
+
'jwt-oauth': {
|
|
50
|
+
jwt_secret: 'jwt-secret',
|
|
51
|
+
response_type: 'token',
|
|
52
|
+
client_id: '12345.client-id',
|
|
53
|
+
token_url: 'https://token.example.com/12345/oauth'
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const result = await jwtAuthenticator.constructPayload(apiPayload, actorInfo);
|
|
59
|
+
expect(fetchMock).toHaveBeenCalledTimes(1);
|
|
60
|
+
expect(fetchMock).toHaveBeenCalledWith('https://authenticator.example.com/jwtToken', expect.objectContaining({
|
|
61
|
+
method: 'POST',
|
|
62
|
+
headers: {
|
|
63
|
+
'Content-Type': 'application/json'
|
|
64
|
+
}
|
|
65
|
+
}));
|
|
66
|
+
const requestBody = JSON.parse(fetchMock.mock.calls[0][1].body);
|
|
67
|
+
expect(requestBody).toEqual(expect.objectContaining({
|
|
68
|
+
user_token: encodeURIComponent('user.one@example.com'),
|
|
69
|
+
jwt_secret: 'jwt-secret',
|
|
70
|
+
jwt_auth_url: 'https://jwt-auth.example.com',
|
|
71
|
+
response_type: 'token',
|
|
72
|
+
client_id: '12345.client-id',
|
|
73
|
+
token_url: 'https://token.example.com/12345/oauth',
|
|
74
|
+
domain_url: 'https://domain.example.com'
|
|
75
|
+
}));
|
|
76
|
+
expect(result.account.authHeaders.Authorization).toBe('Zoho-oauthtoken access-token-123');
|
|
77
|
+
});
|
|
78
|
+
it('should throw a descriptive error when authenticator API returns non-ok response', async () => {
|
|
79
|
+
fetchMock.mockResolvedValue({
|
|
80
|
+
ok: false,
|
|
81
|
+
status: 500,
|
|
82
|
+
text: async () => 'internal error'
|
|
83
|
+
});
|
|
84
|
+
const jwtAuthenticator = new _JWTauthenticator.default();
|
|
85
|
+
const apiPayload = {
|
|
86
|
+
account: {}
|
|
87
|
+
};
|
|
88
|
+
const actorInfo = {
|
|
89
|
+
email: 'user@example.com',
|
|
90
|
+
'data-generator': {
|
|
91
|
+
account: {
|
|
92
|
+
'jwt-oauth': {
|
|
93
|
+
jwt_secret: 'jwt-secret',
|
|
94
|
+
response_type: 'token',
|
|
95
|
+
client_id: '12345.client-id',
|
|
96
|
+
token_url: 'https://token.example.com/12345/oauth'
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
await expect(jwtAuthenticator.constructPayload(apiPayload, actorInfo)).rejects.toThrow('JWT authenticator error! status: 500, body: internal error');
|
|
102
|
+
});
|
|
103
|
+
it('should throw when authenticator response has no access_token', async () => {
|
|
104
|
+
fetchMock.mockResolvedValue({
|
|
105
|
+
ok: true,
|
|
106
|
+
json: async () => ({
|
|
107
|
+
token_type: 'Bearer'
|
|
108
|
+
})
|
|
109
|
+
});
|
|
110
|
+
const jwtAuthenticator = new _JWTauthenticator.default();
|
|
111
|
+
const apiPayload = {
|
|
112
|
+
account: {}
|
|
113
|
+
};
|
|
114
|
+
const actorInfo = {
|
|
115
|
+
email: 'user@example.com',
|
|
116
|
+
'data-generator': {
|
|
117
|
+
account: {
|
|
118
|
+
'jwt-oauth': {
|
|
119
|
+
jwt_secret: 'jwt-secret',
|
|
120
|
+
response_type: 'token',
|
|
121
|
+
client_id: '12345.client-id',
|
|
122
|
+
token_url: 'https://token.example.com/12345/oauth'
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
await expect(jwtAuthenticator.constructPayload(apiPayload, actorInfo)).rejects.toThrow('JWT authenticator did not return access_token');
|
|
128
|
+
});
|
|
129
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
var _OAuthAuthenticator = _interopRequireDefault(require("../../../../../../src/core/dataGenerator/authenticator/OAuthAuthenticator"));
|
|
5
|
+
describe('OAuthAuthenticator', () => {
|
|
6
|
+
it('should assign oauth2 from actor info into payload account', async () => {
|
|
7
|
+
const oauthAuthenticator = new _OAuthAuthenticator.default();
|
|
8
|
+
const apiPayload = {
|
|
9
|
+
account: {}
|
|
10
|
+
};
|
|
11
|
+
const actorInfo = {
|
|
12
|
+
'data-generator': {
|
|
13
|
+
account: {
|
|
14
|
+
oauth2: {
|
|
15
|
+
client_id: 'new-client',
|
|
16
|
+
scope: 'tickets.read'
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
const result = await oauthAuthenticator.constructPayload(apiPayload, actorInfo);
|
|
22
|
+
expect(result.account.oauth2).toEqual({
|
|
23
|
+
client_id: 'new-client',
|
|
24
|
+
scope: 'tickets.read'
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
it('should throw when oauth2 is absent in actor configuration', async () => {
|
|
28
|
+
const oauthAuthenticator = new _OAuthAuthenticator.default();
|
|
29
|
+
const apiPayload = {
|
|
30
|
+
account: {}
|
|
31
|
+
};
|
|
32
|
+
const actorInfo = {
|
|
33
|
+
'data-generator': {
|
|
34
|
+
account: {}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
await expect(oauthAuthenticator.constructPayload(apiPayload, actorInfo)).rejects.toThrow('OAuth2 authentication failed: "oauth2" is missing in actor configuration');
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
var _OrgOAuthAuthenticator = _interopRequireDefault(require("../../../../../../src/core/dataGenerator/authenticator/OrgOAuthAuthenticator"));
|
|
5
|
+
describe('OrgOAuthAuthenticator', () => {
|
|
6
|
+
it('should assign org_oauth from actor info into payload account', async () => {
|
|
7
|
+
const orgOAuthAuthenticator = new _OrgOAuthAuthenticator.default();
|
|
8
|
+
const apiPayload = {
|
|
9
|
+
account: {}
|
|
10
|
+
};
|
|
11
|
+
const actorInfo = {
|
|
12
|
+
'data-generator': {
|
|
13
|
+
account: {
|
|
14
|
+
org_oauth: {
|
|
15
|
+
org_id: 'org-1001',
|
|
16
|
+
region: 'us'
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
const result = await orgOAuthAuthenticator.constructPayload(apiPayload, actorInfo);
|
|
22
|
+
expect(result.account.org_oauth).toEqual({
|
|
23
|
+
org_id: 'org-1001',
|
|
24
|
+
region: 'us'
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
it('should throw when org_oauth is absent in actor configuration', async () => {
|
|
28
|
+
const orgOAuthAuthenticator = new _OrgOAuthAuthenticator.default();
|
|
29
|
+
const apiPayload = {
|
|
30
|
+
account: {}
|
|
31
|
+
};
|
|
32
|
+
const actorInfo = {
|
|
33
|
+
'data-generator': {
|
|
34
|
+
account: {}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
await expect(orgOAuthAuthenticator.constructPayload(apiPayload, actorInfo)).rejects.toThrow('OrgOAuth authentication failed: "org_oauth" is missing in actor configuration');
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -13,7 +13,10 @@ jest.mock('../../../../core/playwright/helpers/parseUserArgs', () => ({
|
|
|
13
13
|
}));
|
|
14
14
|
jest.mock('../../../../core/playwright/readConfigFile');
|
|
15
15
|
jest.mock('../../../../core/playwright/tagProcessor');
|
|
16
|
-
jest.mock('../../../../core/playwright/test-runner')
|
|
16
|
+
jest.mock('../../../../core/playwright/test-runner', () => ({
|
|
17
|
+
__esModule: true,
|
|
18
|
+
runPreprocessing: jest.fn()
|
|
19
|
+
}));
|
|
17
20
|
jest.mock('../../../../utils/logger', () => ({
|
|
18
21
|
__esModule: true,
|
|
19
22
|
Logger: {
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
4
|
var _RunnerHelper = _interopRequireDefault(require("../../../../../core/playwright/runner/RunnerHelper"));
|
|
5
5
|
var _SpawnRunner = _interopRequireDefault(require("../../../../../core/playwright/runner/SpawnRunner"));
|
|
6
|
+
jest.mock('../../../../../core/playwright/setup/config-utils', () => ({
|
|
7
|
+
getBrowsersList: jest.fn().mockReturnValue([])
|
|
8
|
+
}));
|
|
6
9
|
describe('RunnerHelper', () => {
|
|
7
10
|
describe('createRunner', () => {
|
|
8
11
|
it('should throw error on invalid runner type', () => {
|
|
@@ -6,6 +6,9 @@ var _Runner = _interopRequireDefault(require("../../../../../core/playwright/run
|
|
|
6
6
|
var _Configuration = _interopRequireDefault(require("../../../../../core/playwright/configuration/Configuration"));
|
|
7
7
|
jest.mock('child_process');
|
|
8
8
|
jest.mock('../../../../../utils/logger');
|
|
9
|
+
jest.mock('../../../../../core/playwright/setup/config-utils', () => ({
|
|
10
|
+
getBrowsersList: jest.fn().mockReturnValue([])
|
|
11
|
+
}));
|
|
9
12
|
describe('SpawnRunner', () => {
|
|
10
13
|
let spawnRunner;
|
|
11
14
|
const runnerObj = new _Runner.default();
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zohodesk/testinglibrary",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.5",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@zohodesk/testinglibrary",
|
|
9
|
-
"version": "4.1.
|
|
9
|
+
"version": "4.1.5",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "ISC",
|
|
12
12
|
"dependencies": {
|
|
@@ -4265,12 +4265,12 @@
|
|
|
4265
4265
|
"peer": true
|
|
4266
4266
|
},
|
|
4267
4267
|
"node_modules/@types/node": {
|
|
4268
|
-
"version": "
|
|
4269
|
-
"resolved": "https://registry.npmjs.org/@types/node/-/node-
|
|
4270
|
-
"integrity": "sha512-
|
|
4268
|
+
"version": "26.0.0",
|
|
4269
|
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.0.0.tgz",
|
|
4270
|
+
"integrity": "sha512-vf2YFi1iY9lHGwNJMs01biZFbKJkrZR1T6/MlzjhJLPdntOHLhTrDSnSVcdtvjihi4VQNlrFRIxLsDBlQpAipA==",
|
|
4271
4271
|
"license": "MIT",
|
|
4272
4272
|
"dependencies": {
|
|
4273
|
-
"undici-types": "
|
|
4273
|
+
"undici-types": "~8.3.0"
|
|
4274
4274
|
}
|
|
4275
4275
|
},
|
|
4276
4276
|
"node_modules/@types/prop-types": {
|
|
@@ -6996,9 +6996,9 @@
|
|
|
6996
6996
|
}
|
|
6997
6997
|
},
|
|
6998
6998
|
"node_modules/electron-to-chromium": {
|
|
6999
|
-
"version": "1.5.
|
|
7000
|
-
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.
|
|
7001
|
-
"integrity": "sha512-
|
|
6999
|
+
"version": "1.5.376",
|
|
7000
|
+
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.376.tgz",
|
|
7001
|
+
"integrity": "sha512-cUVA7/RvbFTEuw/i3obUwDTRIXojaxkResf+ibByPFxjc6XK3VNtcQXV0NSbAlJ0FMjcJGgftVVB4Qo184EXvA==",
|
|
7002
7002
|
"license": "ISC"
|
|
7003
7003
|
},
|
|
7004
7004
|
"node_modules/emittery": {
|
|
@@ -14537,9 +14537,9 @@
|
|
|
14537
14537
|
"license": "MIT"
|
|
14538
14538
|
},
|
|
14539
14539
|
"node_modules/undici-types": {
|
|
14540
|
-
"version": "
|
|
14541
|
-
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-
|
|
14542
|
-
"integrity": "sha512-
|
|
14540
|
+
"version": "8.3.0",
|
|
14541
|
+
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz",
|
|
14542
|
+
"integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==",
|
|
14543
14543
|
"license": "MIT"
|
|
14544
14544
|
},
|
|
14545
14545
|
"node_modules/unicode-canonical-property-names-ecmascript": {
|
|
@@ -15021,9 +15021,9 @@
|
|
|
15021
15021
|
"license": "ISC"
|
|
15022
15022
|
},
|
|
15023
15023
|
"node_modules/yargs": {
|
|
15024
|
-
"version": "17.7.
|
|
15025
|
-
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.
|
|
15026
|
-
"integrity": "sha512-
|
|
15024
|
+
"version": "17.7.3",
|
|
15025
|
+
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz",
|
|
15026
|
+
"integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==",
|
|
15027
15027
|
"license": "MIT",
|
|
15028
15028
|
"dependencies": {
|
|
15029
15029
|
"cliui": "^8.0.1",
|