Skip to content

Commit 5d986fb

Browse files
committed
W-20893569 adding b2c-cli unit tests
1 parent d9b8ddf commit 5d986fb

69 files changed

Lines changed: 4016 additions & 62 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/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
@@ -52,6 +52,7 @@
5252
"oclif": "^4",
5353
"prettier": "^3.6.2",
5454
"shx": "^0.3.3",
55+
"sinon": "^21.0.1",
5556
"tsx": "^4.20.6",
5657
"typescript": "^5"
5758
},

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

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

54+
protected async confirm(message: string): Promise<boolean> {
55+
return confirm(message);
56+
}
57+
5458
async run(): Promise<void> {
5559
this.requireOAuthCredentials();
5660

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

6064
// Confirm deletion unless --force is used
6165
if (!this.flags.force) {
62-
const confirmed = await confirm(
66+
const confirmed = await this.confirm(
6367
t(
6468
'commands.code.delete.confirm',
6569
'Are you sure you want to delete code version "{{codeVersion}}" on {{hostname}}? (y/n)',

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

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

50+
protected async deleteCartridges(cartridges: Parameters<typeof deleteCartridges>[1]) {
51+
return deleteCartridges(this.instance, cartridges);
52+
}
53+
54+
protected async getActiveCodeVersion() {
55+
return getActiveCodeVersion(this.instance);
56+
}
57+
58+
protected async reloadCodeVersion(codeVersion: string) {
59+
return reloadCodeVersion(this.instance, codeVersion);
60+
}
61+
5062
async run(): Promise<DeployResult> {
5163
this.requireWebDavCredentials();
5264
this.requireOAuthCredentials();
@@ -59,7 +71,7 @@ export default class CodeDeploy extends CartridgeCommand<typeof CodeDeploy> {
5971
this.warn(
6072
t('commands.code.deploy.noCodeVersion', 'No code version specified, discovering active code version...'),
6173
);
62-
const activeVersion = await getActiveCodeVersion(this.instance);
74+
const activeVersion = await this.getActiveCodeVersion();
6375
if (!activeVersion?.id) {
6476
this.error(
6577
t('commands.code.deploy.noActiveVersion', 'No active code version found. Specify one with --code-version.'),
@@ -118,17 +130,17 @@ export default class CodeDeploy extends CartridgeCommand<typeof CodeDeploy> {
118130
try {
119131
// Optionally delete existing cartridges first
120132
if (this.flags.delete) {
121-
await deleteCartridges(this.instance, cartridges);
133+
await this.deleteCartridges(cartridges);
122134
}
123135

124136
// Upload cartridges
125-
await uploadCartridges(this.instance, cartridges);
137+
await this.uploadCartridges(cartridges);
126138

127139
// Optionally reload code version
128140
let reloaded = false;
129141
if (this.flags.reload) {
130142
try {
131-
await reloadCodeVersion(this.instance, version);
143+
await this.reloadCodeVersion(version);
132144
reloaded = true;
133145
} catch (error) {
134146
this.logger?.debug(`Could not reload code version: ${error instanceof Error ? error.message : error}`);
@@ -174,4 +186,8 @@ export default class CodeDeploy extends CartridgeCommand<typeof CodeDeploy> {
174186
throw error;
175187
}
176188
}
189+
190+
protected async uploadCartridges(cartridges: Parameters<typeof uploadCartridges>[1]) {
191+
return uploadCartridges(this.instance, cartridges);
192+
}
177193
}

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

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,7 @@ export default class CodeWatch extends CartridgeCommand<typeof CodeWatch> {
4141
}
4242

4343
try {
44-
const result = await watchCartridges(this.instance, this.cartridgePath, {
45-
...this.cartridgeOptions,
46-
onUpload: (files) => {
47-
this.log(t('commands.code.watch.uploaded', '[UPLOAD] {{count}} file(s)', {count: files.length}));
48-
},
49-
onDelete: (files) => {
50-
this.log(t('commands.code.watch.deleted', '[DELETE] {{count}} file(s)', {count: files.length}));
51-
},
52-
onError: (error) => {
53-
this.warn(t('commands.code.watch.error', 'Error: {{message}}', {message: error.message}));
54-
},
55-
});
44+
const result = await this.watchCartridges();
5645

5746
this.log(
5847
t('commands.code.watch.watching', 'Watching {{count}} cartridge(s)...', {count: result.cartridges.length}),
@@ -78,4 +67,19 @@ export default class CodeWatch extends CartridgeCommand<typeof CodeWatch> {
7867
throw error;
7968
}
8069
}
70+
71+
protected async watchCartridges() {
72+
return watchCartridges(this.instance, this.cartridgePath, {
73+
...this.cartridgeOptions,
74+
onUpload: (files) => {
75+
this.log(t('commands.code.watch.uploaded', '[UPLOAD] {{count}} file(s)', {count: files.length}));
76+
},
77+
onDelete: (files) => {
78+
this.log(t('commands.code.watch.deleted', '[DELETE] {{count}} file(s)', {count: files.length}));
79+
},
80+
onError: (error) => {
81+
this.warn(t('commands.code.watch.error', 'Error: {{message}}', {message: error.message}));
82+
},
83+
});
84+
}
8185
}

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 async downloadDocs(input: Parameters<typeof downloadDocs>[1]) {
41+
return downloadDocs(this.instance, input);
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.downloadDocs({
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 readDocByQuery(query: string) {
61+
return readDocByQuery(query);
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.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: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,21 @@ export default class DocsSchema extends BaseCommand<typeof DocsSchema> {
4545
}),
4646
};
4747

48+
protected listSchemas() {
49+
return listSchemas();
50+
}
51+
52+
protected readSchemaByQuery(query: string) {
53+
return readSchemaByQuery(query);
54+
}
55+
4856
async run(): Promise<ListResult | SchemaResult> {
4957
const {query} = this.args;
5058
const {list} = this.flags;
5159

5260
// List mode
5361
if (list) {
54-
const entries = listSchemas();
62+
const entries = this.listSchemas();
5563

5664
if (this.jsonEnabled()) {
5765
return {entries};
@@ -72,7 +80,7 @@ export default class DocsSchema extends BaseCommand<typeof DocsSchema> {
7280
this.error(t('commands.docs.schema.queryRequired', 'Schema name is required. Use --list to see all schemas.'));
7381
}
7482

75-
const result = readSchemaByQuery(query);
83+
const result = this.readSchemaByQuery(query);
7684

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

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

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

73+
protected listDocs() {
74+
return listDocs();
75+
}
76+
7377
async run(): Promise<ListDocsResponse | SearchDocsResponse> {
7478
const {query} = this.args;
7579
const {limit, list} = this.flags;
7680

7781
// List mode
7882
if (list) {
79-
const entries = listDocs();
83+
const entries = this.listDocs();
8084

8185
if (this.jsonEnabled()) {
8286
return {entries};
@@ -109,7 +113,7 @@ export default class DocsSearch extends BaseCommand<typeof DocsSearch> {
109113
);
110114
}
111115

112-
const results = searchDocs(query, limit);
116+
const results = this.searchDocs(query, limit);
113117

114118
const response: SearchDocsResponse = {
115119
query,
@@ -136,4 +140,8 @@ export default class DocsSearch extends BaseCommand<typeof DocsSearch> {
136140

137141
return response;
138142
}
143+
144+
protected searchDocs(query: string, limit: number) {
145+
return searchDocs(query, limit);
146+
}
139147
}

packages/b2c-cli/src/commands/job/export.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ export default class JobExport extends JobCommand<typeof JobExport> {
114114
'no-download': noDownload,
115115
'zip-only': zipOnly,
116116
timeout,
117-
'show-log': showLog,
117+
'show-log': showLog = true,
118118
} = this.flags;
119119

120120
const hostname = this.resolvedConfig.hostname!;
@@ -173,7 +173,7 @@ export default class JobExport extends JobCommand<typeof JobExport> {
173173
this.log(t('commands.job.export.dataUnits', 'Data units: {{dataUnits}}', {dataUnits: JSON.stringify(dataUnits)}));
174174

175175
try {
176-
const result = await siteArchiveExportToPath(this.instance, dataUnits, output, {
176+
const result = await this.siteArchiveExportToPath(dataUnits, output, {
177177
keepArchive: keepArchive || noDownload,
178178
extractZip: !zipOnly,
179179
waitOptions: {
@@ -254,6 +254,14 @@ export default class JobExport extends JobCommand<typeof JobExport> {
254254
}
255255
}
256256

257+
protected async siteArchiveExportToPath(
258+
dataUnits: Parameters<typeof siteArchiveExportToPath>[1],
259+
output: Parameters<typeof siteArchiveExportToPath>[2],
260+
options: Parameters<typeof siteArchiveExportToPath>[3],
261+
) {
262+
return siteArchiveExportToPath(this.instance, dataUnits, output, options);
263+
}
264+
257265
private buildDataUnits(params: {
258266
dataUnitsJson?: string;
259267
site?: string[];

0 commit comments

Comments
 (0)