Skip to content

Commit d6056c2

Browse files
authored
W-20893569 adding b2c-cli unit tests (#55)
* W-20893569 adding b2c-cli unit tests * refactored to use common helper * fixing the failures after merging * reducing SDK method stubbing * sharing isolation config for cli and sdk tests * simplifying operations pattern
1 parent b4d62be commit d6056c2

80 files changed

Lines changed: 4143 additions & 252 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/b2c-cli/.mocharc.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"reporter": "spec",
77
"timeout": 60000,
88
"node-option": [
9-
"import=tsx"
9+
"import=tsx",
10+
"conditions=development"
1011
]
1112
}

packages/b2c-cli/eslint.config.mjs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import headerPlugin from 'eslint-plugin-header';
99
import path from 'node:path';
1010
import {fileURLToPath} from 'node:url';
1111

12-
import {copyrightHeader, sharedRules, oclifRules, prettierPlugin} from '../../eslint.config.mjs';
12+
import {copyrightHeader, sharedRules, oclifRules, chaiTestRules, prettierPlugin} from '../../eslint.config.mjs';
1313

1414
const gitignorePath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '.gitignore');
1515
headerPlugin.rules.header.meta.schema = false;
@@ -19,7 +19,7 @@ export default [
1919
// node_modules must be explicitly ignored because the .gitignore pattern only covers
2020
// packages/b2c-cli/node_modules, not the monorepo root node_modules
2121
{
22-
ignores: ['**/node_modules/**', 'test/functional/fixtures/**/*.js'],
22+
ignores: ['**/node_modules/**', 'test/functional/fixtures/**/*.js', '**/node_modules/marked-terminal/**'],
2323
},
2424
includeIgnoreFile(gitignorePath),
2525
...oclif,
@@ -39,4 +39,29 @@ export default [
3939
...oclifRules,
4040
},
4141
},
42+
{
43+
files: ['test/**/*.ts'],
44+
rules: {
45+
...chaiTestRules,
46+
// Tests use stubbing patterns that intentionally return undefined
47+
'unicorn/no-useless-undefined': 'off',
48+
// Some tests use void 0 to satisfy TS stub typings; allow it in tests
49+
'no-void': 'off',
50+
// Command tests frequently use `any` to avoid over-typing oclif command internals
51+
'@typescript-eslint/no-explicit-any': 'off',
52+
// Helper functions in tests are commonly declared within suites for clarity
53+
'unicorn/consistent-function-scoping': 'off',
54+
// Sinon default import is intentional and idiomatic in tests
55+
'import/no-named-as-default-member': 'off',
56+
// import/namespace behaves inconsistently across environments when parsing CJS modules like marked-terminal
57+
'import/namespace': 'off',
58+
},
59+
},
60+
{
61+
files: ['src/commands/docs/**/*.ts'],
62+
rules: {
63+
// marked-terminal is CJS and breaks import/namespace static analysis
64+
'import/namespace': 'off',
65+
},
66+
},
4267
];

packages/b2c-cli/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"oclif": "^4",
4646
"prettier": "^3.6.2",
4747
"shx": "^0.3.3",
48+
"sinon": "^21.0.1",
4849
"tsx": "^4.20.6",
4950
"typescript": "^5"
5051
},

packages/b2c-cli/src/commands/code/delete.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ export default class CodeDelete extends InstanceCommand<typeof CodeDelete> {
5151
}),
5252
};
5353

54+
protected operations = {
55+
confirm,
56+
deleteCodeVersion,
57+
};
58+
5459
async run(): Promise<void> {
5560
this.requireOAuthCredentials();
5661

@@ -59,7 +64,7 @@ export default class CodeDelete extends InstanceCommand<typeof CodeDelete> {
5964

6065
// Confirm deletion unless --force is used
6166
if (!this.flags.force) {
62-
const confirmed = await confirm(
67+
const confirmed = await this.operations.confirm(
6368
t(
6469
'commands.code.delete.confirm',
6570
'Are you sure you want to delete code version "{{codeVersion}}" on {{hostname}}? (y/n)',
@@ -80,7 +85,7 @@ export default class CodeDelete extends InstanceCommand<typeof CodeDelete> {
8085
}),
8186
);
8287

83-
await deleteCodeVersion(this.instance, codeVersion);
88+
await this.operations.deleteCodeVersion(this.instance, codeVersion);
8489
this.log(t('commands.code.delete.deleted', 'Code version {{codeVersion}} deleted successfully', {codeVersion}));
8590
}
8691
}

packages/b2c-cli/src/commands/code/deploy.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ export default class CodeDeploy extends CartridgeCommand<typeof CodeDeploy> {
4747
}),
4848
};
4949

50+
protected operations = {
51+
uploadCartridges,
52+
deleteCartridges,
53+
getActiveCodeVersion,
54+
reloadCodeVersion,
55+
};
56+
5057
async run(): Promise<DeployResult> {
5158
this.requireWebDavCredentials();
5259
this.requireOAuthCredentials();
@@ -59,7 +66,7 @@ export default class CodeDeploy extends CartridgeCommand<typeof CodeDeploy> {
5966
this.warn(
6067
t('commands.code.deploy.noCodeVersion', 'No code version specified, discovering active code version...'),
6168
);
62-
const activeVersion = await getActiveCodeVersion(this.instance);
69+
const activeVersion = await this.operations.getActiveCodeVersion(this.instance);
6370
if (!activeVersion?.id) {
6471
this.error(
6572
t('commands.code.deploy.noActiveVersion', 'No active code version found. Specify one with --code-version.'),
@@ -119,17 +126,17 @@ export default class CodeDeploy extends CartridgeCommand<typeof CodeDeploy> {
119126
try {
120127
// Optionally delete existing cartridges first
121128
if (this.flags.delete) {
122-
await deleteCartridges(this.instance, cartridges);
129+
await this.operations.deleteCartridges(this.instance, cartridges);
123130
}
124131

125132
// Upload cartridges
126-
await uploadCartridges(this.instance, cartridges);
133+
await this.operations.uploadCartridges(this.instance, cartridges);
127134

128135
// Optionally reload code version
129136
let reloaded = false;
130137
if (this.flags.reload) {
131138
try {
132-
await reloadCodeVersion(this.instance, version);
139+
await this.operations.reloadCodeVersion(this.instance, version);
133140
reloaded = true;
134141
} catch (error) {
135142
this.logger?.debug(`Could not reload code version: ${error instanceof Error ? error.message : error}`);

packages/b2c-cli/src/commands/code/watch.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ export default class CodeWatch extends CartridgeCommand<typeof CodeWatch> {
2727
...CartridgeCommand.cartridgeFlags,
2828
};
2929

30+
protected operations = {
31+
watchCartridges,
32+
};
33+
3034
async run(): Promise<void> {
3135
this.requireWebDavCredentials();
3236
this.requireOAuthCredentials();
@@ -41,7 +45,7 @@ export default class CodeWatch extends CartridgeCommand<typeof CodeWatch> {
4145
}
4246

4347
try {
44-
const result = await watchCartridges(this.instance, this.cartridgePath, {
48+
const result = await this.operations.watchCartridges(this.instance, this.cartridgePath, {
4549
...this.cartridgeOptions,
4650
onUpload: (files) => {
4751
this.log(t('commands.code.watch.uploaded', '[UPLOAD] {{count}} file(s)', {count: files.length}));

packages/b2c-cli/src/commands/docs/download.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ export default class DocsDownload extends InstanceCommand<typeof DocsDownload> {
3737
}),
3838
};
3939

40+
protected operations = {
41+
downloadDocs,
42+
};
43+
4044
async run(): Promise<DownloadDocsResult> {
4145
this.requireServer();
4246
this.requireWebDavCredentials();
@@ -50,7 +54,7 @@ export default class DocsDownload extends InstanceCommand<typeof DocsDownload> {
5054
}),
5155
);
5256

53-
const result = await downloadDocs(this.instance, {
57+
const result = await this.operations.downloadDocs(this.instance, {
5458
outputDir,
5559
keepArchive,
5660
});

packages/b2c-cli/src/commands/docs/read.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66
import {Args, Flags} from '@oclif/core';
77
import {marked} from 'marked';
8-
// eslint-disable-next-line import/namespace
8+
99
import {markedTerminal} from 'marked-terminal';
1010
import {BaseCommand} from '@salesforce/b2c-tooling-sdk/cli';
1111
import {readDocByQuery, type DocEntry} from '@salesforce/b2c-tooling-sdk/operations/docs';
@@ -57,11 +57,15 @@ export default class DocsRead extends BaseCommand<typeof DocsRead> {
5757
}),
5858
};
5959

60+
protected operations = {
61+
readDocByQuery,
62+
};
63+
6064
async run(): Promise<ReadDocsResult> {
6165
const {query} = this.args;
6266
const {raw} = this.flags;
6367

64-
const result = readDocByQuery(query);
68+
const result = this.operations.readDocByQuery(query);
6569

6670
if (!result) {
6771
this.error(t('commands.docs.read.notFound', 'No documentation found matching: {{query}}', {query}), {

packages/b2c-cli/src/commands/docs/schema.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,18 @@ export default class DocsSchema extends BaseCommand<typeof DocsSchema> {
4545
}),
4646
};
4747

48+
protected operations = {
49+
listSchemas,
50+
readSchemaByQuery,
51+
};
52+
4853
async run(): Promise<ListResult | SchemaResult> {
4954
const {query} = this.args;
5055
const {list} = this.flags;
5156

5257
// List mode
5358
if (list) {
54-
const entries = listSchemas();
59+
const entries = this.operations.listSchemas();
5560

5661
if (this.jsonEnabled()) {
5762
return {entries};
@@ -72,7 +77,7 @@ export default class DocsSchema extends BaseCommand<typeof DocsSchema> {
7277
this.error(t('commands.docs.schema.queryRequired', 'Schema name is required. Use --list to see all schemas.'));
7378
}
7479

75-
const result = readSchemaByQuery(query);
80+
const result = this.operations.readSchemaByQuery(query);
7681

7782
if (!result) {
7883
this.error(t('commands.docs.schema.notFound', 'No schema found matching: {{query}}', {query}), {

packages/b2c-cli/src/commands/docs/search.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,18 @@ export default class DocsSearch extends BaseCommand<typeof DocsSearch> {
7070
}),
7171
};
7272

73+
protected operations = {
74+
listDocs,
75+
searchDocs,
76+
};
77+
7378
async run(): Promise<ListDocsResponse | SearchDocsResponse> {
7479
const {query} = this.args;
7580
const {limit, list} = this.flags;
7681

7782
// List mode
7883
if (list) {
79-
const entries = listDocs();
84+
const entries = this.operations.listDocs();
8085

8186
if (this.jsonEnabled()) {
8287
return {entries};
@@ -109,7 +114,7 @@ export default class DocsSearch extends BaseCommand<typeof DocsSearch> {
109114
);
110115
}
111116

112-
const results = searchDocs(query, limit);
117+
const results = this.operations.searchDocs(query, limit);
113118

114119
const response: SearchDocsResponse = {
115120
query,

0 commit comments

Comments
 (0)