@salesforce/cli-plugins-testkit 2.5.2 → 3.0.0
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/lib/execCmd.d.ts +25 -66
- package/lib/execCmd.js +23 -35
- package/lib/genUniqueString.d.ts +1 -1
- package/lib/hubAuth.d.ts +4 -8
- package/lib/hubAuth.js +12 -21
- package/lib/index.js +5 -1
- package/lib/testSession.d.ts +24 -15
- package/lib/testSession.js +70 -72
- package/package.json +3 -3
package/lib/execCmd.d.ts
CHANGED
|
@@ -4,30 +4,39 @@ import { SfError } from '@salesforce/core';
|
|
|
4
4
|
import { Duration } from '@salesforce/kit';
|
|
5
5
|
import { AnyJson, Many } from '@salesforce/ts-types';
|
|
6
6
|
import { ExecOptions, ShellString } from 'shelljs';
|
|
7
|
-
declare type
|
|
8
|
-
|
|
7
|
+
export declare type CLI = 'inherit' | 'sfdx' | 'sf';
|
|
8
|
+
declare type BaseExecOptions = {
|
|
9
9
|
/**
|
|
10
10
|
* Throws if this exit code is not returned by the child process.
|
|
11
11
|
*/
|
|
12
12
|
ensureExitCode?: number;
|
|
13
13
|
/**
|
|
14
|
-
* The
|
|
15
|
-
*
|
|
14
|
+
* The executable that should be used for execCmd.
|
|
15
|
+
* - inherit uses TESTKIT_EXECUTABLE_PATH to determine the executable. If it's not set it defaults to the local bin/dev
|
|
16
|
+
* - sfdx refers to the globally installed sfdx executable
|
|
17
|
+
* - sf refers to the globally installed sfdx executable
|
|
16
18
|
*/
|
|
17
|
-
cli?:
|
|
19
|
+
cli?: CLI;
|
|
20
|
+
};
|
|
21
|
+
export declare type ExecCmdOptions = ExecOptions & BaseExecOptions;
|
|
22
|
+
declare type ExcludeMethods<T> = Pick<T, NonNullable<{
|
|
23
|
+
[K in keyof T]: T[K] extends (_: any) => any ? never : K;
|
|
24
|
+
}[keyof T]>>;
|
|
25
|
+
declare type JsonOutput<T> = {
|
|
26
|
+
status: number;
|
|
27
|
+
result: T;
|
|
28
|
+
} & Partial<ExcludeMethods<SfError>>;
|
|
29
|
+
export interface ExecCmdResult<T> {
|
|
18
30
|
/**
|
|
19
|
-
*
|
|
31
|
+
* Command output parsed as JSON, if `--json` param present.
|
|
20
32
|
*/
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
export interface ExecCmdResult {
|
|
33
|
+
jsonOutput?: JsonOutput<T>;
|
|
24
34
|
/**
|
|
25
35
|
* Command output from the shell.
|
|
26
36
|
*
|
|
27
37
|
* @see https://www.npmjs.com/package/shelljs#execcommand--options--callback
|
|
28
38
|
*/
|
|
29
39
|
shellOutput: ShellString;
|
|
30
|
-
jsonOutput?: unknown;
|
|
31
40
|
/**
|
|
32
41
|
* The JsonParseError if parsing failed.
|
|
33
42
|
*/
|
|
@@ -37,27 +46,6 @@ export interface ExecCmdResult {
|
|
|
37
46
|
*/
|
|
38
47
|
execCmdDuration: Duration;
|
|
39
48
|
}
|
|
40
|
-
declare type ExcludeMethods<T> = Pick<T, NonNullable<{
|
|
41
|
-
[K in keyof T]: T[K] extends (_: any) => any ? never : K;
|
|
42
|
-
}[keyof T]>>;
|
|
43
|
-
export interface SfdxExecCmdResult<T = Collection> extends ExecCmdResult {
|
|
44
|
-
/**
|
|
45
|
-
* Command output parsed as JSON, if `--json` param present.
|
|
46
|
-
*/
|
|
47
|
-
jsonOutput?: {
|
|
48
|
-
status: number;
|
|
49
|
-
result: T;
|
|
50
|
-
} & Partial<ExcludeMethods<SfError>>;
|
|
51
|
-
}
|
|
52
|
-
export interface SfExecCmdResult<T = Collection> extends ExecCmdResult {
|
|
53
|
-
/**
|
|
54
|
-
* Command output parsed as JSON, if `--json` param present.
|
|
55
|
-
*/
|
|
56
|
-
jsonOutput?: {
|
|
57
|
-
status: number;
|
|
58
|
-
result: T;
|
|
59
|
-
} & Partial<ExcludeMethods<SfError>>;
|
|
60
|
-
}
|
|
61
49
|
/**
|
|
62
50
|
* Synchronously execute a command with the provided options in a child process.
|
|
63
51
|
*
|
|
@@ -66,6 +54,7 @@ export interface SfExecCmdResult<T = Collection> extends ExecCmdResult {
|
|
|
66
54
|
* 2. `timeout` = 3,600,000 (1 hour)
|
|
67
55
|
* 3. `env` = process.env
|
|
68
56
|
* 4. `silent` = true (child process output not written to the console)
|
|
57
|
+
* 5. `cli` = 'inherit' (use the TESTKIT_EXECUTABLE_PATH env var or `bin/dev` if not set for executing commands)
|
|
69
58
|
*
|
|
70
59
|
* Other defaults:
|
|
71
60
|
*
|
|
@@ -76,40 +65,12 @@ export interface SfExecCmdResult<T = Collection> extends ExecCmdResult {
|
|
|
76
65
|
* @param options The options used to run the command.
|
|
77
66
|
* @returns The child process exit code, stdout, stderr, cmd run time, and the parsed JSON if `--json` param present.
|
|
78
67
|
*/
|
|
79
|
-
export declare function execCmd<T =
|
|
68
|
+
export declare function execCmd<T = AnyJson>(cmd: string, options?: ExecCmdOptions & {
|
|
80
69
|
async?: false;
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
export declare function execCmd<T = Collection>(cmd: string, options?: ExecCmdOptions & {
|
|
84
|
-
async?: false;
|
|
85
|
-
cli?: 'sf';
|
|
86
|
-
}): SfExecCmdResult<T>;
|
|
87
|
-
/**
|
|
88
|
-
* Asynchronously execute a command with the provided options in a child process.
|
|
89
|
-
*
|
|
90
|
-
* Option defaults:
|
|
91
|
-
* 1. `cwd` = process.cwd()
|
|
92
|
-
* 2. `timeout` = 3,600,000 (1 hour)
|
|
93
|
-
* 3. `env` = process.env
|
|
94
|
-
* 4. `silent` = true (child process output not written to the console)
|
|
95
|
-
*
|
|
96
|
-
* Other defaults:
|
|
97
|
-
*
|
|
98
|
-
* @see www.npmjs.com/package/shelljs#execcommand--options--callback
|
|
99
|
-
* @see www.nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback
|
|
100
|
-
*
|
|
101
|
-
* @param cmd The command string to be executed by a child process.
|
|
102
|
-
* @param options The options used to run the command.
|
|
103
|
-
* @returns The child process exit code, stdout, stderr, cmd run time, and the parsed JSON if `--json` param present.
|
|
104
|
-
*/
|
|
105
|
-
export declare function execCmd<T = Collection>(cmd: string, options: ExecCmdOptions & {
|
|
70
|
+
}): ExecCmdResult<T>;
|
|
71
|
+
export declare function execCmd<T = AnyJson>(cmd: string, options: ExecCmdOptions & {
|
|
106
72
|
async: true;
|
|
107
|
-
|
|
108
|
-
}): Promise<SfdxExecCmdResult<T>>;
|
|
109
|
-
export declare function execCmd<T = Collection>(cmd: string, options: ExecCmdOptions & {
|
|
110
|
-
async: true;
|
|
111
|
-
cli?: 'sf';
|
|
112
|
-
}): Promise<SfExecCmdResult<T>>;
|
|
73
|
+
}): Promise<ExecCmdResult<T>>;
|
|
113
74
|
export declare enum Interaction {
|
|
114
75
|
DOWN = "\u001B[B",
|
|
115
76
|
UP = "\u001B[A",
|
|
@@ -125,9 +86,7 @@ export declare type InteractiveCommandExecutionResult = {
|
|
|
125
86
|
stderr: string;
|
|
126
87
|
duration: Duration;
|
|
127
88
|
};
|
|
128
|
-
export declare type InteractiveCommandExecutionOptions =
|
|
129
|
-
ensureExitCode?: number;
|
|
130
|
-
} & SpawnOptionsWithoutStdio;
|
|
89
|
+
export declare type InteractiveCommandExecutionOptions = BaseExecOptions & SpawnOptionsWithoutStdio;
|
|
131
90
|
/**
|
|
132
91
|
* A map of questions and answers to be used in an interactive command.
|
|
133
92
|
*
|
package/lib/execCmd.js
CHANGED
|
@@ -17,15 +17,13 @@ const debug_1 = require("debug");
|
|
|
17
17
|
const shelljs = require("shelljs");
|
|
18
18
|
const shelljs_1 = require("shelljs");
|
|
19
19
|
const stripAnsi = require("strip-ansi");
|
|
20
|
-
const DEFAULT_EXEC_OPTIONS = {
|
|
21
|
-
cli: 'sfdx',
|
|
22
|
-
};
|
|
23
20
|
const buildCmdOptions = (options) => {
|
|
24
21
|
const defaults = {
|
|
25
|
-
env:
|
|
22
|
+
env: { ...process.env, ...options?.env },
|
|
26
23
|
cwd: process.cwd(),
|
|
27
24
|
timeout: kit_1.Duration.hours(1).milliseconds,
|
|
28
25
|
silent: true,
|
|
26
|
+
cli: 'inherit',
|
|
29
27
|
};
|
|
30
28
|
const shellOverride = kit_1.env.getString('TESTKIT_EXEC_SHELL');
|
|
31
29
|
if (shellOverride) {
|
|
@@ -56,19 +54,20 @@ const getExitCodeError = (cmd, expectedCode, output) => {
|
|
|
56
54
|
return Error(`Unexpected exit code for command: ${cmd}. Expected: ${expectedCode} Actual: ${output.code} ${errorDetails}`);
|
|
57
55
|
};
|
|
58
56
|
/**
|
|
59
|
-
*
|
|
57
|
+
* Determine the executable path for use by `execCmd`.
|
|
60
58
|
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
59
|
+
* If the cli is 'inherit', the executable preference order is:
|
|
60
|
+
* 1. TESTKIT_EXECUTABLE_PATH env var
|
|
61
|
+
* 2. `bin/dev` (default)
|
|
64
62
|
*
|
|
65
|
-
* @param cmdArgs The command name, args, and param as a string. E.g., `"force:user:create -a testuser1"`
|
|
66
63
|
* @returns The command string with CLI executable. E.g., `"node_modules/bin/sfdx force:user:create -a testuser1"`
|
|
67
64
|
*/
|
|
68
|
-
const
|
|
69
|
-
const debug = (0, debug_1.default)('testkit:
|
|
70
|
-
const bin =
|
|
71
|
-
|
|
65
|
+
const determineExecutable = (cli = 'inherit') => {
|
|
66
|
+
const debug = (0, debug_1.default)('testkit:determineExecutable');
|
|
67
|
+
const bin = cli === 'inherit'
|
|
68
|
+
? kit_1.env.getString('TESTKIT_EXECUTABLE_PATH') ??
|
|
69
|
+
(0, path_1.join)(process.cwd(), 'bin', process.platform === 'win32' ? 'dev.cmd' : 'dev')
|
|
70
|
+
: cli;
|
|
72
71
|
const which = shelljs.which(bin);
|
|
73
72
|
let resolvedPath = (0, path_1.resolve)(bin);
|
|
74
73
|
// If which finds the path in the system path, use that.
|
|
@@ -78,14 +77,13 @@ const buildCmd = (cmdArgs, options) => {
|
|
|
78
77
|
else if (!fs.existsSync(bin)) {
|
|
79
78
|
throw new Error(`Cannot find specified executable path: ${bin}`);
|
|
80
79
|
}
|
|
81
|
-
debug(`Using executable path: ${bin}`);
|
|
82
80
|
debug(`Resolved executable path: ${resolvedPath}`);
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
81
|
+
debug(`Using executable path: ${bin}`);
|
|
82
|
+
return bin;
|
|
83
|
+
};
|
|
84
|
+
const buildCmd = (cmdArgs, options) => {
|
|
85
|
+
const bin = determineExecutable(options?.cli);
|
|
86
|
+
return `${bin} ${cmdArgs}`;
|
|
89
87
|
};
|
|
90
88
|
const execCmdSync = (cmd, options) => {
|
|
91
89
|
const debug = (0, debug_1.default)('testkit:execCmd');
|
|
@@ -142,22 +140,12 @@ const execCmdAsync = async (cmd, options) => {
|
|
|
142
140
|
});
|
|
143
141
|
return resultPromise;
|
|
144
142
|
};
|
|
145
|
-
function execCmd(cmd, options
|
|
146
|
-
if (options
|
|
147
|
-
|
|
148
|
-
return execCmdAsync(cmd, options);
|
|
149
|
-
}
|
|
150
|
-
else {
|
|
151
|
-
return execCmdSync(cmd, options);
|
|
152
|
-
}
|
|
143
|
+
function execCmd(cmd, options) {
|
|
144
|
+
if (options?.async) {
|
|
145
|
+
return execCmdAsync(cmd, options);
|
|
153
146
|
}
|
|
154
147
|
else {
|
|
155
|
-
|
|
156
|
-
return execCmdAsync(cmd, options);
|
|
157
|
-
}
|
|
158
|
-
else {
|
|
159
|
-
return execCmdSync(cmd, options);
|
|
160
|
-
}
|
|
148
|
+
return execCmdSync(cmd, options);
|
|
161
149
|
}
|
|
162
150
|
}
|
|
163
151
|
exports.execCmd = execCmd;
|
|
@@ -210,7 +198,7 @@ var Interaction;
|
|
|
210
198
|
async function execInteractiveCmd(command, answers, options = {}) {
|
|
211
199
|
const debug = (0, debug_1.default)('testkit:execInteractiveCmd');
|
|
212
200
|
return new Promise((resolve, reject) => {
|
|
213
|
-
const bin =
|
|
201
|
+
const bin = determineExecutable(options?.cli).trim();
|
|
214
202
|
const startTime = process.hrtime();
|
|
215
203
|
const opts = process.platform === 'win32'
|
|
216
204
|
? { shell: true, cwd: process.cwd(), ...options }
|
package/lib/genUniqueString.d.ts
CHANGED
package/lib/hubAuth.d.ts
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
export declare
|
|
2
|
-
JWT = "JWT",
|
|
3
|
-
AUTH_URL = "AUTH_URL",
|
|
4
|
-
REUSE = "REUSE",
|
|
5
|
-
NONE = "NONE"
|
|
6
|
-
}
|
|
1
|
+
export declare type DevhubAuthStrategy = 'AUTO' | 'JWT' | 'AUTH_URL' | 'REUSE' | 'NONE';
|
|
7
2
|
export declare const prepareForJwt: (homeDir: string) => string;
|
|
8
3
|
export declare const prepareForAuthUrl: (homeDir: string) => string;
|
|
9
4
|
/**
|
|
@@ -18,7 +13,8 @@ export declare const prepareForAuthUrl: (homeDir: string) => string;
|
|
|
18
13
|
* optional but recommended: TESTKIT_HUB_INSTANCE
|
|
19
14
|
* required for AuthUrl: TESTKIT_AUTH_URL
|
|
20
15
|
*/
|
|
21
|
-
export declare const testkitHubAuth: (homeDir: string, authStrategy?:
|
|
16
|
+
export declare const testkitHubAuth: (homeDir: string, authStrategy?: DevhubAuthStrategy) => void;
|
|
17
|
+
export declare const getAuthStrategy: () => DevhubAuthStrategy;
|
|
22
18
|
/**
|
|
23
19
|
* For scenarios where a hub has already been authenticated in the environment and the username is provided,
|
|
24
20
|
* set the environment variables from the existing hub's information.
|
|
@@ -31,4 +27,4 @@ export declare const testkitHubAuth: (homeDir: string, authStrategy?: AuthStrate
|
|
|
31
27
|
* TESTKIT_JWT_KEY,TESTKIT_JWT_CLIENT_ID,TESTKIT_HUB_INSTANCE (if using jwt)
|
|
32
28
|
*
|
|
33
29
|
*/
|
|
34
|
-
export declare const transferExistingAuthToEnv: (authStrategy
|
|
30
|
+
export declare const transferExistingAuthToEnv: (authStrategy: DevhubAuthStrategy) => void;
|
package/lib/hubAuth.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.transferExistingAuthToEnv = exports.
|
|
3
|
+
exports.transferExistingAuthToEnv = exports.getAuthStrategy = exports.testkitHubAuth = exports.prepareForAuthUrl = exports.prepareForJwt = void 0;
|
|
4
4
|
/*
|
|
5
5
|
* Copyright (c) 2020, salesforce.com, inc.
|
|
6
6
|
* All rights reserved.
|
|
@@ -13,15 +13,6 @@ const fs = require("fs");
|
|
|
13
13
|
const shell = require("shelljs");
|
|
14
14
|
const debug_1 = require("debug");
|
|
15
15
|
const kit_1 = require("@salesforce/kit");
|
|
16
|
-
// this seems to be a known eslint error for enums
|
|
17
|
-
// eslint-disable-next-line no-shadow
|
|
18
|
-
var AuthStrategy;
|
|
19
|
-
(function (AuthStrategy) {
|
|
20
|
-
AuthStrategy["JWT"] = "JWT";
|
|
21
|
-
AuthStrategy["AUTH_URL"] = "AUTH_URL";
|
|
22
|
-
AuthStrategy["REUSE"] = "REUSE";
|
|
23
|
-
AuthStrategy["NONE"] = "NONE";
|
|
24
|
-
})(AuthStrategy = exports.AuthStrategy || (exports.AuthStrategy = {}));
|
|
25
16
|
const DEFAULT_INSTANCE_URL = 'https://login.salesforce.com';
|
|
26
17
|
/**
|
|
27
18
|
* Function examines the env var TESTKIT_JWT_KEY to determine if it needs to be
|
|
@@ -75,14 +66,14 @@ exports.prepareForAuthUrl = prepareForAuthUrl;
|
|
|
75
66
|
* optional but recommended: TESTKIT_HUB_INSTANCE
|
|
76
67
|
* required for AuthUrl: TESTKIT_AUTH_URL
|
|
77
68
|
*/
|
|
78
|
-
const testkitHubAuth = (homeDir, authStrategy = getAuthStrategy()) => {
|
|
69
|
+
const testkitHubAuth = (homeDir, authStrategy = (0, exports.getAuthStrategy)()) => {
|
|
79
70
|
const logger = (0, debug_1.debug)('testkit:authFromStubbedHome');
|
|
80
71
|
const execOpts = { silent: true };
|
|
81
72
|
const shellOverride = kit_1.env.getString('TESTKIT_EXEC_SHELL');
|
|
82
73
|
if (shellOverride) {
|
|
83
74
|
execOpts.shell = shellOverride;
|
|
84
75
|
}
|
|
85
|
-
if (authStrategy ===
|
|
76
|
+
if (authStrategy === 'JWT') {
|
|
86
77
|
logger('trying jwt auth');
|
|
87
78
|
const jwtKey = (0, exports.prepareForJwt)(homeDir);
|
|
88
79
|
const results = shell.exec(`sfdx auth:jwt:grant -d -u ${kit_1.env.getString('TESTKIT_HUB_USERNAME', '')} -i ${kit_1.env.getString('TESTKIT_JWT_CLIENT_ID', '')} -f ${jwtKey} -r ${kit_1.env.getString('TESTKIT_HUB_INSTANCE', DEFAULT_INSTANCE_URL)}`, execOpts);
|
|
@@ -91,7 +82,7 @@ const testkitHubAuth = (homeDir, authStrategy = getAuthStrategy()) => {
|
|
|
91
82
|
}
|
|
92
83
|
return;
|
|
93
84
|
}
|
|
94
|
-
if (authStrategy ===
|
|
85
|
+
if (authStrategy === 'AUTH_URL') {
|
|
95
86
|
logger('trying to authenticate with AuthUrl');
|
|
96
87
|
const tmpUrl = (0, exports.prepareForAuthUrl)(homeDir);
|
|
97
88
|
const shellOutput = shell.exec(`sfdx auth:sfdxurl:store -d -f ${tmpUrl}`, execOpts);
|
|
@@ -108,18 +99,19 @@ const getAuthStrategy = () => {
|
|
|
108
99
|
if (kit_1.env.getString('TESTKIT_JWT_CLIENT_ID') &&
|
|
109
100
|
kit_1.env.getString('TESTKIT_HUB_USERNAME') &&
|
|
110
101
|
kit_1.env.getString('TESTKIT_JWT_KEY')) {
|
|
111
|
-
return
|
|
102
|
+
return 'JWT';
|
|
112
103
|
}
|
|
113
104
|
// you provided a username but not an auth url, so you must already have an auth that you want to re-use
|
|
114
105
|
if (kit_1.env.getString('TESTKIT_HUB_USERNAME') && !kit_1.env.getString('TESTKIT_AUTH_URL')) {
|
|
115
|
-
return
|
|
106
|
+
return 'REUSE';
|
|
116
107
|
}
|
|
117
108
|
// auth url alone
|
|
118
109
|
if (kit_1.env.getString('TESTKIT_AUTH_URL')) {
|
|
119
|
-
return
|
|
110
|
+
return 'AUTH_URL';
|
|
120
111
|
}
|
|
121
|
-
return
|
|
112
|
+
return 'NONE';
|
|
122
113
|
};
|
|
114
|
+
exports.getAuthStrategy = getAuthStrategy;
|
|
123
115
|
/**
|
|
124
116
|
* For scenarios where a hub has already been authenticated in the environment and the username is provided,
|
|
125
117
|
* set the environment variables from the existing hub's information.
|
|
@@ -132,11 +124,10 @@ const getAuthStrategy = () => {
|
|
|
132
124
|
* TESTKIT_JWT_KEY,TESTKIT_JWT_CLIENT_ID,TESTKIT_HUB_INSTANCE (if using jwt)
|
|
133
125
|
*
|
|
134
126
|
*/
|
|
135
|
-
const transferExistingAuthToEnv = (authStrategy
|
|
136
|
-
|
|
137
|
-
if (authStrategy !== AuthStrategy.REUSE)
|
|
127
|
+
const transferExistingAuthToEnv = (authStrategy) => {
|
|
128
|
+
if (authStrategy !== 'REUSE')
|
|
138
129
|
return;
|
|
139
|
-
const logger = (0, debug_1.debug)('testkit:
|
|
130
|
+
const logger = (0, debug_1.debug)('testkit:transferExistingAuthToEnv');
|
|
140
131
|
const devhub = kit_1.env.getString('TESTKIT_HUB_USERNAME', '');
|
|
141
132
|
logger(`reading ${devhub}.json`);
|
|
142
133
|
const authFileName = `${devhub}.json`;
|
package/lib/index.js
CHANGED
|
@@ -7,7 +7,11 @@
|
|
|
7
7
|
*/
|
|
8
8
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
9
|
if (k2 === undefined) k2 = k;
|
|
10
|
-
Object.
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
11
15
|
}) : (function(o, m, k, k2) {
|
|
12
16
|
if (k2 === undefined) k2 = k;
|
|
13
17
|
o[k2] = m[k];
|
package/lib/testSession.d.ts
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import { RetryConfig } from 'ts-retry-promise';
|
|
2
2
|
import { AsyncOptionalCreatable } from '@salesforce/kit';
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
3
|
+
import { Optional } from '@salesforce/ts-types';
|
|
4
|
+
import { AuthFields } from '@salesforce/core';
|
|
5
5
|
import { TestProject, TestProjectOptions } from './testProject';
|
|
6
|
-
import {
|
|
6
|
+
import { DevhubAuthStrategy } from './hubAuth';
|
|
7
|
+
export declare type ScratchOrgConfig = {
|
|
8
|
+
executable?: 'sfdx' | 'sf';
|
|
9
|
+
config?: string;
|
|
10
|
+
duration?: number;
|
|
11
|
+
alias?: string;
|
|
12
|
+
setDefault?: boolean;
|
|
13
|
+
edition?: 'developer' | 'enterprise' | 'group' | 'professional' | 'partner-developer' | 'partner-enterprise' | 'partner-group' | 'partner-professional';
|
|
14
|
+
username?: string;
|
|
15
|
+
wait?: number;
|
|
16
|
+
};
|
|
7
17
|
export interface TestSessionOptions {
|
|
8
18
|
/**
|
|
9
19
|
* Specify a different location for the test session.
|
|
@@ -14,17 +24,17 @@ export interface TestSessionOptions {
|
|
|
14
24
|
*/
|
|
15
25
|
project?: TestProjectOptions;
|
|
16
26
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
27
|
+
* Scratch orgs to create as part of setup. All must be created successfully or session
|
|
28
|
+
* create will throw. Scratch orgs created as part of setup will be deleted as part of
|
|
29
|
+
* `TestSession.clean()`.
|
|
20
30
|
*/
|
|
21
|
-
|
|
31
|
+
scratchOrgs?: ScratchOrgConfig[];
|
|
22
32
|
/**
|
|
23
33
|
* The preferred auth method to use
|
|
24
34
|
*/
|
|
25
|
-
|
|
35
|
+
devhubAuthStrategy?: DevhubAuthStrategy;
|
|
26
36
|
/**
|
|
27
|
-
* The number of times to retry the
|
|
37
|
+
* The number of times to retry the scratch org create after the initial attempt if it fails. Will be overridden by TESTKIT_SETUP_RETRIES environment variable.
|
|
28
38
|
*/
|
|
29
39
|
retries?: number;
|
|
30
40
|
}
|
|
@@ -44,8 +54,8 @@ export interface TestSessionOptions {
|
|
|
44
54
|
* TESTKIT_PROJECT_DIR = a SFDX project to use for testing. the tests will use this project directly.
|
|
45
55
|
* TESTKIT_SAVE_ARTIFACTS = prevents a test session from deleting orgs, projects, and test sessions.
|
|
46
56
|
* TESTKIT_ENABLE_ZIP = allows zipping the session dir when this is true
|
|
47
|
-
* TESTKIT_SETUP_RETRIES = number of times to retry the
|
|
48
|
-
* TESTKIT_SETUP_RETRIES_TIMEOUT = milliseconds to wait before the next retry of
|
|
57
|
+
* TESTKIT_SETUP_RETRIES = number of times to retry the org creates after the initial attempt before throwing an error
|
|
58
|
+
* TESTKIT_SETUP_RETRIES_TIMEOUT = milliseconds to wait before the next retry of scratch org creations. Defaults to 5000
|
|
49
59
|
* TESTKIT_EXEC_SHELL = the shell to use for all testkit shell executions rather than the shelljs default.
|
|
50
60
|
*
|
|
51
61
|
* TESTKIT_HUB_USERNAME = username of an existing hub (authenticated before creating a session)
|
|
@@ -60,14 +70,13 @@ export declare class TestSession extends AsyncOptionalCreatable<TestSessionOptio
|
|
|
60
70
|
dir: string;
|
|
61
71
|
homeDir: string;
|
|
62
72
|
project?: TestProject;
|
|
63
|
-
setup?: AnyJson[] | shell.ShellString;
|
|
64
73
|
rmRetryConfig: Partial<RetryConfig<void>>;
|
|
74
|
+
orgs: Map<string, AuthFields>;
|
|
65
75
|
private debug;
|
|
66
76
|
private cwdStub?;
|
|
67
77
|
private overriddenDir?;
|
|
68
78
|
private sandbox;
|
|
69
|
-
private
|
|
70
|
-
private setupRetries;
|
|
79
|
+
private retries;
|
|
71
80
|
private zipDir;
|
|
72
81
|
private options;
|
|
73
82
|
private shelljsExecOptions;
|
|
@@ -108,6 +117,6 @@ export declare class TestSession extends AsyncOptionalCreatable<TestSessionOptio
|
|
|
108
117
|
protected init(): Promise<void>;
|
|
109
118
|
private deleteOrgs;
|
|
110
119
|
private rmSessionDir;
|
|
111
|
-
private
|
|
120
|
+
private createOrgs;
|
|
112
121
|
private sleep;
|
|
113
122
|
}
|
package/lib/testSession.js
CHANGED
|
@@ -11,7 +11,6 @@ const fs = require("fs");
|
|
|
11
11
|
const path = require("path");
|
|
12
12
|
const debug_1 = require("debug");
|
|
13
13
|
const kit_1 = require("@salesforce/kit");
|
|
14
|
-
const ts_types_1 = require("@salesforce/ts-types");
|
|
15
14
|
const sinon_1 = require("sinon");
|
|
16
15
|
const shell = require("shelljs");
|
|
17
16
|
const stripAnsi = require("strip-ansi");
|
|
@@ -35,8 +34,8 @@ const hubAuth_1 = require("./hubAuth");
|
|
|
35
34
|
* TESTKIT_PROJECT_DIR = a SFDX project to use for testing. the tests will use this project directly.
|
|
36
35
|
* TESTKIT_SAVE_ARTIFACTS = prevents a test session from deleting orgs, projects, and test sessions.
|
|
37
36
|
* TESTKIT_ENABLE_ZIP = allows zipping the session dir when this is true
|
|
38
|
-
* TESTKIT_SETUP_RETRIES = number of times to retry the
|
|
39
|
-
* TESTKIT_SETUP_RETRIES_TIMEOUT = milliseconds to wait before the next retry of
|
|
37
|
+
* TESTKIT_SETUP_RETRIES = number of times to retry the org creates after the initial attempt before throwing an error
|
|
38
|
+
* TESTKIT_SETUP_RETRIES_TIMEOUT = milliseconds to wait before the next retry of scratch org creations. Defaults to 5000
|
|
40
39
|
* TESTKIT_EXEC_SHELL = the shell to use for all testkit shell executions rather than the shelljs default.
|
|
41
40
|
*
|
|
42
41
|
* TESTKIT_HUB_USERNAME = username of an existing hub (authenticated before creating a session)
|
|
@@ -50,8 +49,8 @@ class TestSession extends kit_1.AsyncOptionalCreatable {
|
|
|
50
49
|
super(options);
|
|
51
50
|
// this is stored on the class so that tests can set it to something much lower than default
|
|
52
51
|
this.rmRetryConfig = { retries: 12, delay: 5000 };
|
|
52
|
+
this.orgs = new Map();
|
|
53
53
|
this.sandbox = (0, sinon_1.createSandbox)();
|
|
54
|
-
this.orgs = [];
|
|
55
54
|
this.shelljsExecOptions = {
|
|
56
55
|
silent: true,
|
|
57
56
|
};
|
|
@@ -60,7 +59,7 @@ class TestSession extends kit_1.AsyncOptionalCreatable {
|
|
|
60
59
|
this.zipDir = zip_1.zipDir;
|
|
61
60
|
this.createdDate = new Date();
|
|
62
61
|
this.id = (0, genUniqueString_1.genUniqueString)(`${this.createdDate.valueOf()}%s`);
|
|
63
|
-
this.
|
|
62
|
+
this.retries = kit_1.env.getNumber('TESTKIT_SETUP_RETRIES', this.options.retries) || 0;
|
|
64
63
|
const shellOverride = kit_1.env.getString('TESTKIT_EXEC_SHELL');
|
|
65
64
|
if (shellOverride) {
|
|
66
65
|
this.shelljsExecOptions.shell = shellOverride;
|
|
@@ -88,8 +87,9 @@ class TestSession extends kit_1.AsyncOptionalCreatable {
|
|
|
88
87
|
}
|
|
89
88
|
// Write the test session options used to create this session
|
|
90
89
|
fs.writeFileSync(path.join(this.dir, 'testSessionOptions.json'), JSON.stringify(JSON.parse(JSON.stringify(this.options))));
|
|
91
|
-
const authStrategy = this.options.
|
|
92
|
-
|
|
90
|
+
const authStrategy = !this.options.devhubAuthStrategy || this.options.devhubAuthStrategy === 'AUTO'
|
|
91
|
+
? (0, hubAuth_1.getAuthStrategy)()
|
|
92
|
+
: this.options.devhubAuthStrategy;
|
|
93
93
|
(0, hubAuth_1.transferExistingAuthToEnv)(authStrategy);
|
|
94
94
|
// Set the homedir used by this test, on the TestSession and the process
|
|
95
95
|
process.env.USERPROFILE = process.env.HOME = this.homeDir = kit_1.env.getString('TESTKIT_HOMEDIR', this.dir);
|
|
@@ -153,13 +153,13 @@ class TestSession extends kit_1.AsyncOptionalCreatable {
|
|
|
153
153
|
}
|
|
154
154
|
async init() {
|
|
155
155
|
// Run all setup commands
|
|
156
|
-
await this.
|
|
156
|
+
await this.createOrgs(this.options.scratchOrgs);
|
|
157
157
|
this.debug('Created testkit session:');
|
|
158
158
|
this.debug(` ID: ${this.id}`);
|
|
159
159
|
this.debug(` Created Date: ${this.createdDate}`);
|
|
160
160
|
this.debug(` Dir: ${this.dir}`);
|
|
161
161
|
this.debug(` Home Dir: ${this.homeDir}`);
|
|
162
|
-
if (this.orgs
|
|
162
|
+
if (this.orgs.size > 0) {
|
|
163
163
|
this.debug(' Orgs: ', this.orgs);
|
|
164
164
|
}
|
|
165
165
|
if (this.project) {
|
|
@@ -167,12 +167,13 @@ class TestSession extends kit_1.AsyncOptionalCreatable {
|
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
async deleteOrgs() {
|
|
170
|
-
if (!kit_1.env.getString('TESTKIT_ORG_USERNAME') && this.orgs
|
|
171
|
-
const
|
|
172
|
-
|
|
170
|
+
if (!kit_1.env.getString('TESTKIT_ORG_USERNAME') && this.orgs.size > 0) {
|
|
171
|
+
for (const org of [...this.orgs.keys()]) {
|
|
172
|
+
if (org === 'default')
|
|
173
|
+
continue;
|
|
173
174
|
this.debug(`Deleting test org: ${org}`);
|
|
174
|
-
const rv = shell.exec(`
|
|
175
|
-
this.orgs
|
|
175
|
+
const rv = shell.exec(`sf env delete scratch -o ${org} -p`, this.shelljsExecOptions);
|
|
176
|
+
this.orgs.delete(org);
|
|
176
177
|
if (rv.code !== 0) {
|
|
177
178
|
// Must still delete the session dir if org:delete fails
|
|
178
179
|
await this.rmSessionDir();
|
|
@@ -192,77 +193,74 @@ class TestSession extends kit_1.AsyncOptionalCreatable {
|
|
|
192
193
|
}
|
|
193
194
|
// Executes commands and keeps track of any orgs created.
|
|
194
195
|
// Throws if any commands return a non-zero exitCode.
|
|
195
|
-
async
|
|
196
|
-
const dbug = (0, debug_1.debug)('testkit:
|
|
196
|
+
async createOrgs(orgs = []) {
|
|
197
|
+
const dbug = (0, debug_1.debug)('testkit:createOrgs');
|
|
197
198
|
const setup = () => {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if (
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
else {
|
|
249
|
-
this.setup.push(rv);
|
|
250
|
-
}
|
|
199
|
+
for (const org of orgs) {
|
|
200
|
+
// Don't create orgs if we are supposed to reuse one from the env
|
|
201
|
+
const orgUsername = kit_1.env.getString('TESTKIT_ORG_USERNAME');
|
|
202
|
+
if (orgUsername) {
|
|
203
|
+
dbug(`Not creating a new org. Reusing TESTKIT_ORG_USERNAME of: ${org}`);
|
|
204
|
+
this.orgs.set(orgUsername, { username: orgUsername });
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
const executable = org.executable ?? 'sf';
|
|
208
|
+
if (!shell.which(executable)) {
|
|
209
|
+
throw new Error(`${executable} executable not found for creating scratch orgs`);
|
|
210
|
+
}
|
|
211
|
+
let baseCmd = executable === 'sf' ? `${executable} env create scratch --json` : `${executable} force:org:create --json`;
|
|
212
|
+
if (org.config) {
|
|
213
|
+
baseCmd += ` -f ${org.config}`;
|
|
214
|
+
}
|
|
215
|
+
if (org.alias) {
|
|
216
|
+
baseCmd += ` -a ${org.alias}`;
|
|
217
|
+
}
|
|
218
|
+
if (org.duration) {
|
|
219
|
+
baseCmd += executable === 'sf' ? ` -y ${org.duration}` : ` -d ${org.duration}`;
|
|
220
|
+
}
|
|
221
|
+
if (org.setDefault) {
|
|
222
|
+
baseCmd += executable === 'sf' ? ' -d' : ' -s';
|
|
223
|
+
}
|
|
224
|
+
if (org.username) {
|
|
225
|
+
if (org.executable === 'sfdx')
|
|
226
|
+
baseCmd += ` username=${org.username}`;
|
|
227
|
+
else
|
|
228
|
+
throw new Error('username property is not supported by sf env create scratch');
|
|
229
|
+
}
|
|
230
|
+
if (org.edition) {
|
|
231
|
+
baseCmd += executable === 'sf' ? ` -e ${org.edition}` : ` edition=${org.edition}`;
|
|
232
|
+
}
|
|
233
|
+
if (org.wait) {
|
|
234
|
+
baseCmd += `-w ${org.wait}`;
|
|
235
|
+
}
|
|
236
|
+
const rv = shell.exec(baseCmd, this.shelljsExecOptions);
|
|
237
|
+
rv.stdout = stripAnsi(rv.stdout);
|
|
238
|
+
rv.stderr = stripAnsi(rv.stderr);
|
|
239
|
+
if (rv.code !== 0) {
|
|
240
|
+
throw Error(`${baseCmd} failed due to: ${rv.stdout}`);
|
|
241
|
+
}
|
|
242
|
+
dbug(`Output for ${baseCmd} is:\n${rv.stdout}`);
|
|
243
|
+
const jsonOutput = (0, kit_1.parseJson)(rv.stdout);
|
|
244
|
+
const username = jsonOutput.result.username;
|
|
245
|
+
dbug(`Saving org username: ${username} from ${baseCmd}`);
|
|
246
|
+
this.orgs.set(username, jsonOutput.result.authFields);
|
|
247
|
+
if (org.setDefault) {
|
|
248
|
+
this.orgs.set('default', jsonOutput.result.authFields);
|
|
251
249
|
}
|
|
252
250
|
}
|
|
253
251
|
};
|
|
254
252
|
let attempts = 0;
|
|
255
253
|
let completed = false;
|
|
256
254
|
const timeout = new kit_1.Duration(kit_1.env.getNumber('TESTKIT_SETUP_RETRIES_TIMEOUT') ?? 5000, kit_1.Duration.Unit.MILLISECONDS);
|
|
257
|
-
while (!completed && attempts <= this.
|
|
255
|
+
while (!completed && attempts <= this.retries) {
|
|
258
256
|
try {
|
|
259
|
-
dbug(`Executing
|
|
257
|
+
dbug(`Executing org create(s) (attempt ${attempts + 1} of ${this.retries + 1})`);
|
|
260
258
|
setup();
|
|
261
259
|
completed = true;
|
|
262
260
|
}
|
|
263
261
|
catch (err) {
|
|
264
262
|
attempts += 1;
|
|
265
|
-
if (attempts > this.
|
|
263
|
+
if (attempts > this.retries) {
|
|
266
264
|
throw err;
|
|
267
265
|
}
|
|
268
266
|
dbug(`Setup failed. waiting ${timeout.seconds} seconds before next attempt...`);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/cli-plugins-testkit",
|
|
3
3
|
"description": "Provides test utilities to assist Salesforce CLI plug-in authors with writing non-unit tests (NUT).",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "3.0.0",
|
|
5
5
|
"author": "Salesforce",
|
|
6
6
|
"license": "BSD-3-Clause",
|
|
7
7
|
"main": "lib/index.js",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"!lib/**/*.map"
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@salesforce/core": "^3.30.
|
|
45
|
+
"@salesforce/core": "^3.30.9",
|
|
46
46
|
"@salesforce/kit": "^1.6.1",
|
|
47
47
|
"@salesforce/ts-types": "^1.5.21",
|
|
48
48
|
"@types/shelljs": "^0.8.11",
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"pretty-quick": "^3.1.0",
|
|
80
80
|
"sinon": "10.0.0",
|
|
81
81
|
"ts-node": "^10.0.0",
|
|
82
|
-
"typescript": "^4.
|
|
82
|
+
"typescript": "^4.8.4"
|
|
83
83
|
},
|
|
84
84
|
"config": {
|
|
85
85
|
"commitizen": {
|