@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 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 Collection = Record<string, AnyJson> | Array<Record<string, AnyJson>>;
8
- export interface ExecCmdOptions extends ExecOptions {
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 base CLI that the plugin is used in. This is used primarily for changing the behavior
15
- * of JSON parsing and types.
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?: 'sfdx' | 'sf';
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
- * Answers to supply to any prompts. This does NOT work on windows.
31
+ * Command output parsed as JSON, if `--json` param present.
20
32
  */
21
- answers?: string[];
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 = Collection>(cmd: string, options?: ExecCmdOptions & {
68
+ export declare function execCmd<T = AnyJson>(cmd: string, options?: ExecCmdOptions & {
80
69
  async?: false;
81
- cli?: 'sfdx';
82
- }): SfdxExecCmdResult<T>;
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
- cli?: 'sfdx';
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: Object.assign({}, process.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
- * Build a command string using an optional executable path for use by `execCmd`.
57
+ * Determine the executable path for use by `execCmd`.
60
58
  *
61
- * The executable preference order is:
62
- * 2. TESTKIT_EXECUTABLE_PATH env var
63
- * 3. `bin/dev` (default)
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 buildCmd = (cmdArgs, options) => {
69
- const debug = (0, debug_1.default)('testkit:buildCmd');
70
- const bin = kit_1.env.getString('TESTKIT_EXECUTABLE_PATH') ??
71
- (0, path_1.join)(process.cwd(), 'bin', process.platform === 'win32' ? 'dev.cmd' : 'dev');
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
- if (options?.answers && process.platform !== 'win32') {
84
- return `printf "${options.answers.join('\\n')}" | ${bin} ${cmdArgs}`;
85
- }
86
- else {
87
- return `${bin} ${cmdArgs}`;
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 = DEFAULT_EXEC_OPTIONS) {
146
- if (options.cli === 'sf') {
147
- if (options.async) {
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
- if (options.async) {
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 = buildCmd('').trim();
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 }
@@ -6,4 +6,4 @@
6
6
  * @param {string} template - can contain a replaceable string (%s)
7
7
  * @returns {string}
8
8
  */
9
- export declare const genUniqueString: (template?: string | undefined) => string;
9
+ export declare const genUniqueString: (template?: string) => string;
package/lib/hubAuth.d.ts CHANGED
@@ -1,9 +1,4 @@
1
- export declare enum AuthStrategy {
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?: AuthStrategy) => void;
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?: AuthStrategy) => void;
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.testkitHubAuth = exports.prepareForAuthUrl = exports.prepareForJwt = exports.AuthStrategy = void 0;
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 === AuthStrategy.JWT) {
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 === AuthStrategy.AUTH_URL) {
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 AuthStrategy.JWT;
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 AuthStrategy.REUSE;
106
+ return 'REUSE';
116
107
  }
117
108
  // auth url alone
118
109
  if (kit_1.env.getString('TESTKIT_AUTH_URL')) {
119
- return AuthStrategy.AUTH_URL;
110
+ return 'AUTH_URL';
120
111
  }
121
- return AuthStrategy.NONE;
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 = getAuthStrategy()) => {
136
- // nothing to do if the variables are already provided
137
- if (authStrategy !== AuthStrategy.REUSE)
127
+ const transferExistingAuthToEnv = (authStrategy) => {
128
+ if (authStrategy !== 'REUSE')
138
129
  return;
139
- const logger = (0, debug_1.debug)('testkit:AuthReuse');
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.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
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];
@@ -1,9 +1,19 @@
1
1
  import { RetryConfig } from 'ts-retry-promise';
2
2
  import { AsyncOptionalCreatable } from '@salesforce/kit';
3
- import { AnyJson, Optional } from '@salesforce/ts-types';
4
- import * as shell from 'shelljs';
3
+ import { Optional } from '@salesforce/ts-types';
4
+ import { AuthFields } from '@salesforce/core';
5
5
  import { TestProject, TestProjectOptions } from './testProject';
6
- import { AuthStrategy } from './hubAuth';
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
- * Commands to run as setup for tests. All must have exitCode == 0 or session
18
- * creation will throw. Any org:create commands run as setup commands will
19
- * be deleted as part of `TestSession.clean()`.
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
- setupCommands?: string[];
31
+ scratchOrgs?: ScratchOrgConfig[];
22
32
  /**
23
33
  * The preferred auth method to use
24
34
  */
25
- authStrategy?: keyof typeof AuthStrategy;
35
+ devhubAuthStrategy?: DevhubAuthStrategy;
26
36
  /**
27
- * The number of times to retry the setupCommands after the initial attempt if it fails. Will be overridden by TESTKIT_SETUP_RETRIES environment variable.
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 setupCommands after the initial attempt before throwing an error
48
- * TESTKIT_SETUP_RETRIES_TIMEOUT = milliseconds to wait before the next retry of setupCommands. Defaults to 5000
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 orgs;
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 setupCommands;
120
+ private createOrgs;
112
121
  private sleep;
113
122
  }
@@ -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 setupCommands after the initial attempt before throwing an error
39
- * TESTKIT_SETUP_RETRIES_TIMEOUT = milliseconds to wait before the next retry of setupCommands. Defaults to 5000
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.setupRetries = kit_1.env.getNumber('TESTKIT_SETUP_RETRIES', this.options.retries) || 0;
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.authStrategy ? hubAuth_1.AuthStrategy[this.options.authStrategy] : undefined;
92
- // have to grab this before we change the home
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.setupCommands(this.options.setupCommands);
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?.length) {
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?.length) {
171
- const orgs = this.orgs.slice();
172
- for (const org of orgs) {
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(`sfdx force:org:delete -u ${org} -p`, this.shelljsExecOptions);
175
- this.orgs = this.orgs.filter((o) => o !== org);
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 setupCommands(cmds) {
196
- const dbug = (0, debug_1.debug)('testkit:setupCommands');
196
+ async createOrgs(orgs = []) {
197
+ const dbug = (0, debug_1.debug)('testkit:createOrgs');
197
198
  const setup = () => {
198
- if (cmds) {
199
- this.setup = [];
200
- for (let cmd of cmds) {
201
- if (cmd.includes('org:create')) {
202
- // Don't create orgs if we are supposed to reuse one from the env
203
- const org = kit_1.env.getString('TESTKIT_ORG_USERNAME');
204
- if (org) {
205
- dbug(`Not creating a new org. Reusing TESTKIT_ORG_USERNAME of: ${org}`);
206
- this.setup.push({ result: { username: org } });
207
- continue;
208
- }
209
- }
210
- // Detect when running sfdx cli commands
211
- if (cmd.split(' ')[0].includes('sfdx')) {
212
- if (!shell.which('sfdx')) {
213
- throw new Error('sfdx executable not found for running sfdx setup commands');
214
- }
215
- // Add the json flag if it looks like an sfdx command so we can return
216
- // parsed json in the command return.
217
- if (!cmd.includes('--json')) {
218
- cmd += ' --json';
219
- }
220
- }
221
- const rv = shell.exec(cmd, this.shelljsExecOptions);
222
- rv.stdout = stripAnsi(rv.stdout);
223
- rv.stderr = stripAnsi(rv.stderr);
224
- if (rv.code !== 0) {
225
- const io = cmd.includes('--json') ? rv.stdout : rv.stderr;
226
- throw Error(`Setup command ${cmd} failed due to: ${io}`);
227
- }
228
- dbug(`Output for setup cmd ${cmd} is:\n${rv.stdout}`);
229
- // Automatically parse json results
230
- if (cmd.includes('--json')) {
231
- try {
232
- const jsonOutput = (0, kit_1.parseJson)(rv.stdout);
233
- // keep track of all org creates
234
- if (cmd.includes('org:create')) {
235
- const username = (0, ts_types_1.getString)(jsonOutput, 'result.username');
236
- if (username) {
237
- dbug(`Saving org username: ${username} from ${cmd}`);
238
- this.orgs.push(username);
239
- }
240
- }
241
- this.setup.push(jsonOutput);
242
- }
243
- catch (err) {
244
- dbug(`Failed command output JSON parsing due to:\n${err.message}`);
245
- this.setup.push(rv);
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.setupRetries) {
255
+ while (!completed && attempts <= this.retries) {
258
256
  try {
259
- dbug(`Executing setup commands (attempt ${attempts + 1} of ${this.setupRetries + 1})`);
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.setupRetries) {
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": "2.5.2",
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.8",
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.1.3"
82
+ "typescript": "^4.8.4"
83
83
  },
84
84
  "config": {
85
85
  "commitizen": {