@sanity/cli-test 3.0.1 → 4.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.
Files changed (49) hide show
  1. package/dist/index.d.ts +7 -4
  2. package/dist/index.js +3 -1
  3. package/dist/index.js.map +1 -1
  4. package/dist/mocks/cli-core/SanityCommand.d.ts +50 -0
  5. package/dist/mocks/cli-core/SanityCommand.js +75 -0
  6. package/dist/mocks/cli-core/SanityCommand.js.map +1 -0
  7. package/dist/mocks/cli-core/apiClient.d.ts +9 -0
  8. package/dist/mocks/cli-core/apiClient.js +5 -0
  9. package/dist/mocks/cli-core/apiClient.js.map +1 -0
  10. package/dist/mocks/cli-core/browser.d.ts +6 -0
  11. package/dist/mocks/cli-core/browser.js +4 -0
  12. package/dist/mocks/cli-core/browser.js.map +1 -0
  13. package/dist/mocks/cli-core/config.d.ts +66 -0
  14. package/dist/mocks/cli-core/config.js +30 -0
  15. package/dist/mocks/cli-core/config.js.map +1 -0
  16. package/dist/mocks/cli-core/package-manager.d.ts +30 -0
  17. package/dist/mocks/cli-core/package-manager.js +12 -0
  18. package/dist/mocks/cli-core/package-manager.js.map +1 -0
  19. package/dist/mocks/cli-core/request.d.ts +42 -0
  20. package/dist/mocks/cli-core/request.js +40 -0
  21. package/dist/mocks/cli-core/request.js.map +1 -0
  22. package/dist/mocks/cli-core/tasks.d.ts +12 -0
  23. package/dist/mocks/cli-core/tasks.js +6 -0
  24. package/dist/mocks/cli-core/tasks.js.map +1 -0
  25. package/dist/mocks/cli-core/telemetry.d.ts +31 -0
  26. package/dist/mocks/cli-core/telemetry.js +22 -0
  27. package/dist/mocks/cli-core/telemetry.js.map +1 -0
  28. package/dist/mocks/cli-core/ux.d.ts +77 -0
  29. package/dist/mocks/cli-core/ux.js +56 -0
  30. package/dist/mocks/cli-core/ux.js.map +1 -0
  31. package/dist/test/captureOutput.js +1 -1
  32. package/dist/test/captureOutput.js.map +1 -1
  33. package/dist/test/createMockSpinner.js +1 -0
  34. package/dist/test/createMockSpinner.js.map +1 -1
  35. package/dist/test/createTestClient.d.ts +57 -0
  36. package/dist/test/mockApi.d.ts +56 -0
  37. package/dist/test/mockSanityCommand.js.map +1 -1
  38. package/dist/test/mockTelemetry.js.map +1 -1
  39. package/dist/test/oclif.d.ts +179 -0
  40. package/dist/test/oclif.js +7 -0
  41. package/dist/test/oclif.js.map +1 -0
  42. package/dist/test/setupFixtures.d.ts +90 -0
  43. package/dist/test/setupFixtures.js +8 -10
  44. package/dist/test/setupFixtures.js.map +1 -1
  45. package/dist/test/util.d.ts +203 -0
  46. package/dist/test/util.js +11 -0
  47. package/dist/test/util.js.map +1 -0
  48. package/dist/utils/paths.d.ts +65 -0
  49. package/package.json +63 -4
@@ -0,0 +1,77 @@
1
+ import { Mock } from "vitest";
2
+
3
+ /** @internal */
4
+ export declare const boxen: Mock;
5
+
6
+ /** @internal */
7
+ export declare const checkbox: Mock;
8
+
9
+ /** @internal */
10
+ export declare const colorizeJson: Mock;
11
+
12
+ /** @internal */
13
+ export declare const confirm: Mock;
14
+
15
+ /** @internal */
16
+ export declare const editor: Mock;
17
+
18
+ /** @internal */
19
+ export declare const expand: Mock;
20
+
21
+ /** @internal */
22
+ export declare const getTimer: Mock;
23
+
24
+ /** @internal */
25
+ export declare const input: Mock;
26
+
27
+ /** @internal */
28
+ export declare const logSymbols: {
29
+ error: string;
30
+ info: string;
31
+ success: string;
32
+ warning: string;
33
+ };
34
+
35
+ /** @internal */
36
+ export declare const number: Mock;
37
+
38
+ /** @internal */
39
+ export declare const password: Mock;
40
+
41
+ /** @internal */
42
+ export declare const rawlist: Mock;
43
+
44
+ /** @internal */
45
+ export declare const search: Mock;
46
+
47
+ /** @internal */
48
+ export declare const select: Mock;
49
+
50
+ /** @internal */
51
+ export declare class Separator {}
52
+
53
+ /** @internal */
54
+ export declare const spinner: Mock;
55
+
56
+ /** @internal */
57
+ export declare const spinnerPromise: Mock;
58
+
59
+ /**
60
+ * Spy for asserting on spinner start.
61
+ * @internal
62
+ */
63
+ export declare const spinnerStart: Mock;
64
+
65
+ /**
66
+ * Spy for asserting on spinner succeed.
67
+ * @internal
68
+ */
69
+ export declare const spinnerSucceed: Mock;
70
+
71
+ /**
72
+ * Spy for asserting on what text is assigned to a terminal spinner.
73
+ * @internal
74
+ */
75
+ export declare const spinnerText: Mock;
76
+
77
+ export {};
@@ -0,0 +1,56 @@
1
+ import { vi } from 'vitest';
2
+ /** @internal */ export const boxen = vi.fn((str)=>str);
3
+ /** @internal */ export const colorizeJson = vi.fn();
4
+ /** @internal */ export const logSymbols = {
5
+ error: 'e',
6
+ info: 'i',
7
+ success: 's',
8
+ warning: 'w'
9
+ };
10
+ /** @internal */ export const checkbox = vi.fn();
11
+ /** @internal */ export const confirm = vi.fn();
12
+ /** @internal */ export const editor = vi.fn();
13
+ /** @internal */ export const expand = vi.fn();
14
+ /** @internal */ export const input = vi.fn();
15
+ /** @internal */ export const number = vi.fn();
16
+ /** @internal */ export const password = vi.fn();
17
+ /** @internal */ export const rawlist = vi.fn();
18
+ /** @internal */ export const search = vi.fn();
19
+ /** @internal */ export const select = vi.fn();
20
+ /**
21
+ * Spy for asserting on what text is assigned to a terminal spinner.
22
+ * @internal
23
+ */ export const spinnerText = vi.fn();
24
+ /**
25
+ * Spy for asserting on spinner start.
26
+ * @internal
27
+ */ export const spinnerStart = vi.fn().mockReturnThis();
28
+ /**
29
+ * Spy for asserting on spinner succeed.
30
+ * @internal
31
+ */ export const spinnerSucceed = vi.fn();
32
+ /** @internal */ export const spinner = vi.fn(()=>{
33
+ const mockSpin = {
34
+ clear: vi.fn(),
35
+ fail: vi.fn(),
36
+ info: vi.fn(),
37
+ render: vi.fn(),
38
+ start: spinnerStart,
39
+ stop: vi.fn(),
40
+ succeed: spinnerSucceed
41
+ };
42
+ Object.defineProperty(mockSpin, 'text', {
43
+ configurable: true,
44
+ set: spinnerText
45
+ });
46
+ return mockSpin;
47
+ });
48
+ /** @internal */ export const spinnerPromise = vi.fn();
49
+ /** @internal */ export const getTimer = vi.fn().mockReturnValue({
50
+ end: vi.fn().mockReturnValue(0),
51
+ start: vi.fn()
52
+ });
53
+ /** @internal */ export class Separator {
54
+ }
55
+
56
+ //# sourceMappingURL=ux.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/mocks/cli-core/ux.ts"],"sourcesContent":["import {type Mock, vi} from 'vitest'\n/** @internal */\nexport const boxen: Mock = vi.fn((str: string) => str)\n/** @internal */\nexport const colorizeJson: Mock = vi.fn()\n/** @internal */\nexport const logSymbols = {error: 'e', info: 'i', success: 's', warning: 'w'}\n/** @internal */\nexport const checkbox: Mock = vi.fn()\n/** @internal */\nexport const confirm: Mock = vi.fn()\n/** @internal */\nexport const editor: Mock = vi.fn()\n/** @internal */\nexport const expand: Mock = vi.fn()\n/** @internal */\nexport const input: Mock = vi.fn()\n/** @internal */\nexport const number: Mock = vi.fn()\n/** @internal */\nexport const password: Mock = vi.fn()\n/** @internal */\nexport const rawlist: Mock = vi.fn()\n/** @internal */\nexport const search: Mock = vi.fn()\n/** @internal */\nexport const select: Mock = vi.fn()\n/**\n * Spy for asserting on what text is assigned to a terminal spinner.\n * @internal\n */\nexport const spinnerText: Mock = vi.fn()\n/**\n * Spy for asserting on spinner start.\n * @internal\n */\nexport const spinnerStart: Mock = vi.fn().mockReturnThis()\n/**\n * Spy for asserting on spinner succeed.\n * @internal\n */\nexport const spinnerSucceed: Mock = vi.fn()\n/** @internal */\nexport const spinner: Mock = vi.fn(() => {\n const mockSpin = {\n clear: vi.fn(),\n fail: vi.fn(),\n info: vi.fn(),\n render: vi.fn(),\n start: spinnerStart,\n stop: vi.fn(),\n succeed: spinnerSucceed,\n }\n Object.defineProperty(mockSpin, 'text', {\n configurable: true,\n set: spinnerText,\n })\n return mockSpin\n})\n/** @internal */\nexport const spinnerPromise: Mock = vi.fn()\n/** @internal */\nexport const getTimer: Mock = vi\n .fn()\n .mockReturnValue({end: vi.fn().mockReturnValue(0), start: vi.fn()})\n/** @internal */\nexport class Separator {}\n"],"names":["vi","boxen","fn","str","colorizeJson","logSymbols","error","info","success","warning","checkbox","confirm","editor","expand","input","number","password","rawlist","search","select","spinnerText","spinnerStart","mockReturnThis","spinnerSucceed","spinner","mockSpin","clear","fail","render","start","stop","succeed","Object","defineProperty","configurable","set","spinnerPromise","getTimer","mockReturnValue","end","Separator"],"mappings":"AAAA,SAAmBA,EAAE,QAAO,SAAQ;AACpC,cAAc,GACd,OAAO,MAAMC,QAAcD,GAAGE,EAAE,CAAC,CAACC,MAAgBA,KAAI;AACtD,cAAc,GACd,OAAO,MAAMC,eAAqBJ,GAAGE,EAAE,GAAE;AACzC,cAAc,GACd,OAAO,MAAMG,aAAa;IAACC,OAAO;IAAKC,MAAM;IAAKC,SAAS;IAAKC,SAAS;AAAG,EAAC;AAC7E,cAAc,GACd,OAAO,MAAMC,WAAiBV,GAAGE,EAAE,GAAE;AACrC,cAAc,GACd,OAAO,MAAMS,UAAgBX,GAAGE,EAAE,GAAE;AACpC,cAAc,GACd,OAAO,MAAMU,SAAeZ,GAAGE,EAAE,GAAE;AACnC,cAAc,GACd,OAAO,MAAMW,SAAeb,GAAGE,EAAE,GAAE;AACnC,cAAc,GACd,OAAO,MAAMY,QAAcd,GAAGE,EAAE,GAAE;AAClC,cAAc,GACd,OAAO,MAAMa,SAAef,GAAGE,EAAE,GAAE;AACnC,cAAc,GACd,OAAO,MAAMc,WAAiBhB,GAAGE,EAAE,GAAE;AACrC,cAAc,GACd,OAAO,MAAMe,UAAgBjB,GAAGE,EAAE,GAAE;AACpC,cAAc,GACd,OAAO,MAAMgB,SAAelB,GAAGE,EAAE,GAAE;AACnC,cAAc,GACd,OAAO,MAAMiB,SAAenB,GAAGE,EAAE,GAAE;AACnC;;;CAGC,GACD,OAAO,MAAMkB,cAAoBpB,GAAGE,EAAE,GAAE;AACxC;;;CAGC,GACD,OAAO,MAAMmB,eAAqBrB,GAAGE,EAAE,GAAGoB,cAAc,GAAE;AAC1D;;;CAGC,GACD,OAAO,MAAMC,iBAAuBvB,GAAGE,EAAE,GAAE;AAC3C,cAAc,GACd,OAAO,MAAMsB,UAAgBxB,GAAGE,EAAE,CAAC;IACjC,MAAMuB,WAAW;QACfC,OAAO1B,GAAGE,EAAE;QACZyB,MAAM3B,GAAGE,EAAE;QACXK,MAAMP,GAAGE,EAAE;QACX0B,QAAQ5B,GAAGE,EAAE;QACb2B,OAAOR;QACPS,MAAM9B,GAAGE,EAAE;QACX6B,SAASR;IACX;IACAS,OAAOC,cAAc,CAACR,UAAU,QAAQ;QACtCS,cAAc;QACdC,KAAKf;IACP;IACA,OAAOK;AACT,GAAE;AACF,cAAc,GACd,OAAO,MAAMW,iBAAuBpC,GAAGE,EAAE,GAAE;AAC3C,cAAc,GACd,OAAO,MAAMmC,WAAiBrC,GAC3BE,EAAE,GACFoC,eAAe,CAAC;IAACC,KAAKvC,GAAGE,EAAE,GAAGoC,eAAe,CAAC;IAAIT,OAAO7B,GAAGE,EAAE;AAAE,GAAE;AACrE,cAAc,GACd,OAAO,MAAMsC;AAAW"}
@@ -5,7 +5,7 @@ import ansis from 'ansis';
5
5
  * @param fn - The function to capture the output of
6
6
  * @param opts - The options for the capture
7
7
  * @returns The result of the command
8
- * @internal
8
+ * @public
9
9
  *
10
10
  * Credits to oclif for the original implementation:
11
11
  * https://github.com/oclif/test/blob/2a5407e6fc80d388043d10f6b7b8eaa586483015/src/index.ts
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/test/captureOutput.ts"],"sourcesContent":["import {type Errors} from '@oclif/core'\nimport ansis from 'ansis'\n\nexport interface CaptureOptions {\n /**\n * Whether to print the output to the console\n */\n print?: boolean\n /**\n * Whether to strip ANSI escape codes from the output\n */\n stripAnsi?: boolean\n testNodeEnv?: string\n}\n\nexport interface CaptureResult<T = unknown> {\n stderr: string\n stdout: string\n\n error?: Error & Partial<Errors.CLIError>\n result?: T\n}\n\n/**\n * Capture the output of a command and return the result\n *\n * @param fn - The function to capture the output of\n * @param opts - The options for the capture\n * @returns The result of the command\n * @internal\n *\n * Credits to oclif for the original implementation:\n * https://github.com/oclif/test/blob/2a5407e6fc80d388043d10f6b7b8eaa586483015/src/index.ts\n *\n * We are not using the library directly since it does not support mocking code inside of the command\n * possibly because the commands run in a different thread\n */\nexport async function captureOutput<T>(\n fn: () => Promise<T>,\n opts?: CaptureOptions,\n): Promise<CaptureResult<T>> {\n const print = opts?.print ?? false\n const stripAnsi = opts?.stripAnsi ?? true\n const testNodeEnv = opts?.testNodeEnv || 'test'\n\n const originals = {\n NODE_ENV: process.env.NODE_ENV,\n stderrWrite: process.stderr.write,\n stdoutWrite: process.stdout.write,\n }\n\n const output: Record<'stderr' | 'stdout', string[]> = {\n stderr: [],\n stdout: [],\n }\n\n const toString = (str: string | Uint8Array): string =>\n stripAnsi ? ansis.strip(str.toString()) : str.toString()\n\n const getStderr = (): string => output.stderr.map((b) => toString(b)).join('')\n const getStdout = (): string => output.stdout.map((b) => toString(b)).join('')\n\n const mockWrite =\n (std: 'stderr' | 'stdout'): typeof process.stderr.write =>\n (\n chunk: string | Uint8Array,\n encodingOrCb?: ((err?: Error | null) => void) | BufferEncoding,\n cb?: (err?: Error | null) => void,\n ) => {\n output[std].push(chunk.toString())\n\n if (print) {\n let callback: ((err?: Error | null) => void) | undefined = cb\n let encoding: BufferEncoding | undefined\n if (typeof encodingOrCb === 'function') {\n callback = encodingOrCb\n } else {\n encoding = encodingOrCb\n }\n originals[`${std}Write`].apply(process[std], [chunk, encoding, callback])\n } else if (typeof cb === 'function') {\n cb()\n } else if (typeof encodingOrCb === 'function') {\n encodingOrCb()\n }\n return true\n }\n\n process.stdout.write = mockWrite('stdout')\n process.stderr.write = mockWrite('stderr')\n process.env.NODE_ENV = testNodeEnv\n\n try {\n const result = await fn()\n return {\n result,\n stderr: getStderr(),\n stdout: getStdout(),\n }\n } catch (error) {\n // Check if it's an oclif CLIError or a regular error\n const processedError =\n error instanceof Error // Check if it's an Error (this includes CLIError)\n ? Object.assign(error, {message: toString(error.message)}) // If so, process its message\n : new Error(toString(String(error))) // Otherwise, create a new Error from string representation\n\n return {\n error: processedError,\n stderr: getStderr(),\n stdout: getStdout(),\n }\n } finally {\n process.stdout.write = originals.stdoutWrite\n process.stderr.write = originals.stderrWrite\n process.env.NODE_ENV = originals.NODE_ENV\n }\n}\n"],"names":["ansis","captureOutput","fn","opts","print","stripAnsi","testNodeEnv","originals","NODE_ENV","process","env","stderrWrite","stderr","write","stdoutWrite","stdout","output","toString","str","strip","getStderr","map","b","join","getStdout","mockWrite","std","chunk","encodingOrCb","cb","push","callback","encoding","apply","result","error","processedError","Error","Object","assign","message","String"],"mappings":"AACA,OAAOA,WAAW,QAAO;AAsBzB;;;;;;;;;;;;;CAaC,GACD,OAAO,eAAeC,cACpBC,EAAoB,EACpBC,IAAqB;IAErB,MAAMC,QAAQD,MAAMC,SAAS;IAC7B,MAAMC,YAAYF,MAAME,aAAa;IACrC,MAAMC,cAAcH,MAAMG,eAAe;IAEzC,MAAMC,YAAY;QAChBC,UAAUC,QAAQC,GAAG,CAACF,QAAQ;QAC9BG,aAAaF,QAAQG,MAAM,CAACC,KAAK;QACjCC,aAAaL,QAAQM,MAAM,CAACF,KAAK;IACnC;IAEA,MAAMG,SAAgD;QACpDJ,QAAQ,EAAE;QACVG,QAAQ,EAAE;IACZ;IAEA,MAAME,WAAW,CAACC,MAChBb,YAAYL,MAAMmB,KAAK,CAACD,IAAID,QAAQ,MAAMC,IAAID,QAAQ;IAExD,MAAMG,YAAY,IAAcJ,OAAOJ,MAAM,CAACS,GAAG,CAAC,CAACC,IAAML,SAASK,IAAIC,IAAI,CAAC;IAC3E,MAAMC,YAAY,IAAcR,OAAOD,MAAM,CAACM,GAAG,CAAC,CAACC,IAAML,SAASK,IAAIC,IAAI,CAAC;IAE3E,MAAME,YACJ,CAACC,MACD,CACEC,OACAC,cACAC;YAEAb,MAAM,CAACU,IAAI,CAACI,IAAI,CAACH,MAAMV,QAAQ;YAE/B,IAAIb,OAAO;gBACT,IAAI2B,WAAuDF;gBAC3D,IAAIG;gBACJ,IAAI,OAAOJ,iBAAiB,YAAY;oBACtCG,WAAWH;gBACb,OAAO;oBACLI,WAAWJ;gBACb;gBACArB,SAAS,CAAC,GAAGmB,IAAI,KAAK,CAAC,CAAC,CAACO,KAAK,CAACxB,OAAO,CAACiB,IAAI,EAAE;oBAACC;oBAAOK;oBAAUD;iBAAS;YAC1E,OAAO,IAAI,OAAOF,OAAO,YAAY;gBACnCA;YACF,OAAO,IAAI,OAAOD,iBAAiB,YAAY;gBAC7CA;YACF;YACA,OAAO;QACT;IAEFnB,QAAQM,MAAM,CAACF,KAAK,GAAGY,UAAU;IACjChB,QAAQG,MAAM,CAACC,KAAK,GAAGY,UAAU;IACjChB,QAAQC,GAAG,CAACF,QAAQ,GAAGF;IAEvB,IAAI;QACF,MAAM4B,SAAS,MAAMhC;QACrB,OAAO;YACLgC;YACAtB,QAAQQ;YACRL,QAAQS;QACV;IACF,EAAE,OAAOW,OAAO;QACd,qDAAqD;QACrD,MAAMC,iBACJD,iBAAiBE,MAAM,kDAAkD;WACrEC,OAAOC,MAAM,CAACJ,OAAO;YAACK,SAASvB,SAASkB,MAAMK,OAAO;QAAC,GAAG,6BAA6B;WACtF,IAAIH,MAAMpB,SAASwB,OAAON,SAAS,2DAA2D;;QAEpG,OAAO;YACLA,OAAOC;YACPxB,QAAQQ;YACRL,QAAQS;QACV;IACF,SAAU;QACRf,QAAQM,MAAM,CAACF,KAAK,GAAGN,UAAUO,WAAW;QAC5CL,QAAQG,MAAM,CAACC,KAAK,GAAGN,UAAUI,WAAW;QAC5CF,QAAQC,GAAG,CAACF,QAAQ,GAAGD,UAAUC,QAAQ;IAC3C;AACF"}
1
+ {"version":3,"sources":["../../src/test/captureOutput.ts"],"sourcesContent":["import {type Errors} from '@oclif/core'\nimport ansis from 'ansis'\n\n/** @public */\nexport interface CaptureOptions {\n /**\n * Whether to print the output to the console\n */\n print?: boolean\n /**\n * Whether to strip ANSI escape codes from the output\n */\n stripAnsi?: boolean\n testNodeEnv?: string\n}\n\n/** @public */\nexport interface CaptureResult<T = unknown> {\n stderr: string\n stdout: string\n\n error?: Error & Partial<Errors.CLIError>\n result?: T\n}\n\n/**\n * Capture the output of a command and return the result\n *\n * @param fn - The function to capture the output of\n * @param opts - The options for the capture\n * @returns The result of the command\n * @public\n *\n * Credits to oclif for the original implementation:\n * https://github.com/oclif/test/blob/2a5407e6fc80d388043d10f6b7b8eaa586483015/src/index.ts\n *\n * We are not using the library directly since it does not support mocking code inside of the command\n * possibly because the commands run in a different thread\n */\nexport async function captureOutput<T>(\n fn: () => Promise<T>,\n opts?: CaptureOptions,\n): Promise<CaptureResult<T>> {\n const print = opts?.print ?? false\n const stripAnsi = opts?.stripAnsi ?? true\n const testNodeEnv = opts?.testNodeEnv || 'test'\n\n const originals = {\n NODE_ENV: process.env.NODE_ENV,\n stderrWrite: process.stderr.write,\n stdoutWrite: process.stdout.write,\n }\n\n const output: Record<'stderr' | 'stdout', string[]> = {\n stderr: [],\n stdout: [],\n }\n\n const toString = (str: string | Uint8Array): string =>\n stripAnsi ? ansis.strip(str.toString()) : str.toString()\n\n const getStderr = (): string => output.stderr.map((b) => toString(b)).join('')\n const getStdout = (): string => output.stdout.map((b) => toString(b)).join('')\n\n const mockWrite =\n (std: 'stderr' | 'stdout'): typeof process.stderr.write =>\n (\n chunk: string | Uint8Array,\n encodingOrCb?: ((err?: Error | null) => void) | BufferEncoding,\n cb?: (err?: Error | null) => void,\n ) => {\n output[std].push(chunk.toString())\n\n if (print) {\n let callback: ((err?: Error | null) => void) | undefined = cb\n let encoding: BufferEncoding | undefined\n if (typeof encodingOrCb === 'function') {\n callback = encodingOrCb\n } else {\n encoding = encodingOrCb\n }\n originals[`${std}Write`].apply(process[std], [chunk, encoding, callback])\n } else if (typeof cb === 'function') {\n cb()\n } else if (typeof encodingOrCb === 'function') {\n encodingOrCb()\n }\n return true\n }\n\n process.stdout.write = mockWrite('stdout')\n process.stderr.write = mockWrite('stderr')\n process.env.NODE_ENV = testNodeEnv\n\n try {\n const result = await fn()\n return {\n result,\n stderr: getStderr(),\n stdout: getStdout(),\n }\n } catch (error) {\n // Check if it's an oclif CLIError or a regular error\n const processedError =\n error instanceof Error // Check if it's an Error (this includes CLIError)\n ? Object.assign(error, {message: toString(error.message)}) // If so, process its message\n : new Error(toString(String(error))) // Otherwise, create a new Error from string representation\n\n return {\n error: processedError,\n stderr: getStderr(),\n stdout: getStdout(),\n }\n } finally {\n process.stdout.write = originals.stdoutWrite\n process.stderr.write = originals.stderrWrite\n process.env.NODE_ENV = originals.NODE_ENV\n }\n}\n"],"names":["ansis","captureOutput","fn","opts","print","stripAnsi","testNodeEnv","originals","NODE_ENV","process","env","stderrWrite","stderr","write","stdoutWrite","stdout","output","toString","str","strip","getStderr","map","b","join","getStdout","mockWrite","std","chunk","encodingOrCb","cb","push","callback","encoding","apply","result","error","processedError","Error","Object","assign","message","String"],"mappings":"AACA,OAAOA,WAAW,QAAO;AAwBzB;;;;;;;;;;;;;CAaC,GACD,OAAO,eAAeC,cACpBC,EAAoB,EACpBC,IAAqB;IAErB,MAAMC,QAAQD,MAAMC,SAAS;IAC7B,MAAMC,YAAYF,MAAME,aAAa;IACrC,MAAMC,cAAcH,MAAMG,eAAe;IAEzC,MAAMC,YAAY;QAChBC,UAAUC,QAAQC,GAAG,CAACF,QAAQ;QAC9BG,aAAaF,QAAQG,MAAM,CAACC,KAAK;QACjCC,aAAaL,QAAQM,MAAM,CAACF,KAAK;IACnC;IAEA,MAAMG,SAAgD;QACpDJ,QAAQ,EAAE;QACVG,QAAQ,EAAE;IACZ;IAEA,MAAME,WAAW,CAACC,MAChBb,YAAYL,MAAMmB,KAAK,CAACD,IAAID,QAAQ,MAAMC,IAAID,QAAQ;IAExD,MAAMG,YAAY,IAAcJ,OAAOJ,MAAM,CAACS,GAAG,CAAC,CAACC,IAAML,SAASK,IAAIC,IAAI,CAAC;IAC3E,MAAMC,YAAY,IAAcR,OAAOD,MAAM,CAACM,GAAG,CAAC,CAACC,IAAML,SAASK,IAAIC,IAAI,CAAC;IAE3E,MAAME,YACJ,CAACC,MACD,CACEC,OACAC,cACAC;YAEAb,MAAM,CAACU,IAAI,CAACI,IAAI,CAACH,MAAMV,QAAQ;YAE/B,IAAIb,OAAO;gBACT,IAAI2B,WAAuDF;gBAC3D,IAAIG;gBACJ,IAAI,OAAOJ,iBAAiB,YAAY;oBACtCG,WAAWH;gBACb,OAAO;oBACLI,WAAWJ;gBACb;gBACArB,SAAS,CAAC,GAAGmB,IAAI,KAAK,CAAC,CAAC,CAACO,KAAK,CAACxB,OAAO,CAACiB,IAAI,EAAE;oBAACC;oBAAOK;oBAAUD;iBAAS;YAC1E,OAAO,IAAI,OAAOF,OAAO,YAAY;gBACnCA;YACF,OAAO,IAAI,OAAOD,iBAAiB,YAAY;gBAC7CA;YACF;YACA,OAAO;QACT;IAEFnB,QAAQM,MAAM,CAACF,KAAK,GAAGY,UAAU;IACjChB,QAAQG,MAAM,CAACC,KAAK,GAAGY,UAAU;IACjChB,QAAQC,GAAG,CAACF,QAAQ,GAAGF;IAEvB,IAAI;QACF,MAAM4B,SAAS,MAAMhC;QACrB,OAAO;YACLgC;YACAtB,QAAQQ;YACRL,QAAQS;QACV;IACF,EAAE,OAAOW,OAAO;QACd,qDAAqD;QACrD,MAAMC,iBACJD,iBAAiBE,MAAM,kDAAkD;WACrEC,OAAOC,MAAM,CAACJ,OAAO;YAACK,SAASvB,SAASkB,MAAMK,OAAO;QAAC,GAAG,6BAA6B;WACtF,IAAIH,MAAMpB,SAASwB,OAAON,SAAS,2DAA2D;;QAEpG,OAAO;YACLA,OAAOC;YACPxB,QAAQQ;YACRL,QAAQS;QACV;IACF,SAAU;QACRf,QAAQM,MAAM,CAACF,KAAK,GAAGN,UAAUO,WAAW;QAC5CL,QAAQG,MAAM,CAACC,KAAK,GAAGN,UAAUI,WAAW;QAC5CF,QAAQC,GAAG,CAACF,QAAQ,GAAGD,UAAUC,QAAQ;IAC3C;AACF"}
@@ -3,6 +3,7 @@ import { vi } from 'vitest';
3
3
  * Creates a mock spinner function to avoid writing output to stdout.
4
4
  * @param overrides - Pass any mocks or stubs for test expectations
5
5
  * @internal
6
+ * @deprecated Use mocks/cli-core/ux/spinner instead
6
7
  */ export function createMockSpinner(overrides) {
7
8
  return vi.fn(()=>{
8
9
  return {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/test/createMockSpinner.ts"],"sourcesContent":["import {type SpinnerInstance} from '@sanity/cli-core/ux'\nimport {type PersistOptions} from 'ora'\nimport {Mock, vi} from 'vitest'\n\n/**\n * Creates a mock spinner function to avoid writing output to stdout.\n * @param overrides - Pass any mocks or stubs for test expectations\n * @internal\n */\nexport function createMockSpinner(\n overrides?: Partial<SpinnerInstance>,\n): Mock<(options: string) => SpinnerInstance> {\n return vi.fn(() => {\n return {\n clear: function (): SpinnerInstance {\n throw new Error('Function not implemented.')\n },\n color: false,\n fail: function (_text?: string): SpinnerInstance {\n throw new Error('Function not implemented.')\n },\n frame: function (): string {\n throw new Error('Function not implemented.')\n },\n indent: 0,\n info: function (_text?: string): SpinnerInstance {\n throw new Error('Function not implemented.')\n },\n interval: 0,\n isSpinning: false,\n prefixText: '',\n render: function (): SpinnerInstance {\n throw new Error('Function not implemented.')\n },\n spinner: {\n frames: [],\n interval: undefined,\n },\n start: function (_text?: string): SpinnerInstance {\n throw new Error('Function not implemented.')\n },\n stop: function (): SpinnerInstance {\n throw new Error('Function not implemented.')\n },\n stopAndPersist: function (_options?: PersistOptions): SpinnerInstance {\n throw new Error('Function not implemented.')\n },\n succeed: function (_text?: string): SpinnerInstance {\n throw new Error('Function not implemented.')\n },\n suffixText: '',\n text: '',\n warn: function (_text?: string): SpinnerInstance {\n throw new Error('Function not implemented.')\n },\n ...overrides,\n }\n })\n}\n"],"names":["vi","createMockSpinner","overrides","fn","clear","Error","color","fail","_text","frame","indent","info","interval","isSpinning","prefixText","render","spinner","frames","undefined","start","stop","stopAndPersist","_options","succeed","suffixText","text","warn"],"mappings":"AAEA,SAAcA,EAAE,QAAO,SAAQ;AAE/B;;;;CAIC,GACD,OAAO,SAASC,kBACdC,SAAoC;IAEpC,OAAOF,GAAGG,EAAE,CAAC;QACX,OAAO;YACLC,OAAO;gBACL,MAAM,IAAIC,MAAM;YAClB;YACAC,OAAO;YACPC,MAAM,SAAUC,KAAc;gBAC5B,MAAM,IAAIH,MAAM;YAClB;YACAI,OAAO;gBACL,MAAM,IAAIJ,MAAM;YAClB;YACAK,QAAQ;YACRC,MAAM,SAAUH,KAAc;gBAC5B,MAAM,IAAIH,MAAM;YAClB;YACAO,UAAU;YACVC,YAAY;YACZC,YAAY;YACZC,QAAQ;gBACN,MAAM,IAAIV,MAAM;YAClB;YACAW,SAAS;gBACPC,QAAQ,EAAE;gBACVL,UAAUM;YACZ;YACAC,OAAO,SAAUX,KAAc;gBAC7B,MAAM,IAAIH,MAAM;YAClB;YACAe,MAAM;gBACJ,MAAM,IAAIf,MAAM;YAClB;YACAgB,gBAAgB,SAAUC,QAAyB;gBACjD,MAAM,IAAIjB,MAAM;YAClB;YACAkB,SAAS,SAAUf,KAAc;gBAC/B,MAAM,IAAIH,MAAM;YAClB;YACAmB,YAAY;YACZC,MAAM;YACNC,MAAM,SAAUlB,KAAc;gBAC5B,MAAM,IAAIH,MAAM;YAClB;YACA,GAAGH,SAAS;QACd;IACF;AACF"}
1
+ {"version":3,"sources":["../../src/test/createMockSpinner.ts"],"sourcesContent":["import {type SpinnerInstance} from '@sanity/cli-core/ux'\nimport {Mock, vi} from 'vitest'\n\n/**\n * Creates a mock spinner function to avoid writing output to stdout.\n * @param overrides - Pass any mocks or stubs for test expectations\n * @internal\n * @deprecated Use mocks/cli-core/ux/spinner instead\n */\nexport function createMockSpinner(\n overrides?: Partial<SpinnerInstance>,\n): Mock<(options: string) => SpinnerInstance> {\n return vi.fn(() => {\n return {\n clear: function (): SpinnerInstance {\n throw new Error('Function not implemented.')\n },\n color: false,\n fail: function (_text?: string): SpinnerInstance {\n throw new Error('Function not implemented.')\n },\n frame: function (): string {\n throw new Error('Function not implemented.')\n },\n indent: 0,\n info: function (_text?: string): SpinnerInstance {\n throw new Error('Function not implemented.')\n },\n interval: 0,\n isSpinning: false,\n prefixText: '',\n render: function (): SpinnerInstance {\n throw new Error('Function not implemented.')\n },\n spinner: {\n frames: [],\n interval: undefined,\n },\n start: function (_text?: string): SpinnerInstance {\n throw new Error('Function not implemented.')\n },\n stop: function (): SpinnerInstance {\n throw new Error('Function not implemented.')\n },\n stopAndPersist: function (_options?: any): SpinnerInstance {\n throw new Error('Function not implemented.')\n },\n succeed: function (_text?: string): SpinnerInstance {\n throw new Error('Function not implemented.')\n },\n suffixText: '',\n text: '',\n warn: function (_text?: string): SpinnerInstance {\n throw new Error('Function not implemented.')\n },\n ...overrides,\n }\n })\n}\n"],"names":["vi","createMockSpinner","overrides","fn","clear","Error","color","fail","_text","frame","indent","info","interval","isSpinning","prefixText","render","spinner","frames","undefined","start","stop","stopAndPersist","_options","succeed","suffixText","text","warn"],"mappings":"AACA,SAAcA,EAAE,QAAO,SAAQ;AAE/B;;;;;CAKC,GACD,OAAO,SAASC,kBACdC,SAAoC;IAEpC,OAAOF,GAAGG,EAAE,CAAC;QACX,OAAO;YACLC,OAAO;gBACL,MAAM,IAAIC,MAAM;YAClB;YACAC,OAAO;YACPC,MAAM,SAAUC,KAAc;gBAC5B,MAAM,IAAIH,MAAM;YAClB;YACAI,OAAO;gBACL,MAAM,IAAIJ,MAAM;YAClB;YACAK,QAAQ;YACRC,MAAM,SAAUH,KAAc;gBAC5B,MAAM,IAAIH,MAAM;YAClB;YACAO,UAAU;YACVC,YAAY;YACZC,YAAY;YACZC,QAAQ;gBACN,MAAM,IAAIV,MAAM;YAClB;YACAW,SAAS;gBACPC,QAAQ,EAAE;gBACVL,UAAUM;YACZ;YACAC,OAAO,SAAUX,KAAc;gBAC7B,MAAM,IAAIH,MAAM;YAClB;YACAe,MAAM;gBACJ,MAAM,IAAIf,MAAM;YAClB;YACAgB,gBAAgB,SAAUC,QAAc;gBACtC,MAAM,IAAIjB,MAAM;YAClB;YACAkB,SAAS,SAAUf,KAAc;gBAC/B,MAAM,IAAIH,MAAM;YAClB;YACAmB,YAAY;YACZC,MAAM;YACNC,MAAM,SAAUlB,KAAc;gBAC5B,MAAM,IAAIH,MAAM;YAClB;YACA,GAAGH,SAAS;QACd;IACF;AACF"}
@@ -0,0 +1,57 @@
1
+ import { ClientConfig } from "@sanity/client";
2
+ import { SanityClient } from "@sanity/client";
3
+ import { vi } from "vitest";
4
+
5
+ /**
6
+ * Creates a real Sanity client instance for testing that makes actual HTTP requests.
7
+ * Use with mockApi() to intercept and mock the HTTP calls.
8
+ *
9
+ * @public
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * // Mock getGlobalCliClient to return a test client
14
+ * vi.mock('@sanity/cli-core', async (importOriginal) => {
15
+ * const actual = await importOriginal<typeof import('@sanity/cli-core')>()
16
+ * const {createTestClient} = await import('@sanity/cli-test')
17
+ *
18
+ * return {
19
+ * ...actual,
20
+ * getGlobalCliClient: vi.fn().mockImplementation((opts) => {
21
+ * return Promise.resolve(createTestClient({
22
+ * apiVersion: opts.apiVersion,
23
+ * }))
24
+ * }),
25
+ * }
26
+ * })
27
+ *
28
+ * // Then use mockApi to intercept requests
29
+ * mockApi({
30
+ * apiVersion: 'v2025-02-19',
31
+ * method: 'get',
32
+ * uri: '/media-libraries',
33
+ * }).reply(200, {data: [...]})
34
+ * ```
35
+ */
36
+ export declare function createTestClient(options: CreateTestClientOptions): {
37
+ client: SanityClient;
38
+ request: ReturnType<typeof vi.fn>;
39
+ };
40
+
41
+ /**
42
+ * Options for createTestClient
43
+ *
44
+ * @public
45
+ */
46
+ export declare interface CreateTestClientOptions extends ClientConfig {
47
+ /**
48
+ * API version for the client
49
+ */
50
+ apiVersion: string;
51
+ /**
52
+ * Authentication token
53
+ */
54
+ token?: string;
55
+ }
56
+
57
+ export {};
@@ -0,0 +1,56 @@
1
+ import nock from "nock";
2
+
3
+ /**
4
+ * Mocks the API calls, add some defaults so it doesn't cause too much friction
5
+ *
6
+ * @internal
7
+ */
8
+ export declare function mockApi({
9
+ apiHost,
10
+ apiVersion,
11
+ includeQueryTag,
12
+ method,
13
+ projectId,
14
+ query,
15
+ uri,
16
+ }: MockApiOptions): nock.Interceptor;
17
+
18
+ /**
19
+ * @internal
20
+ */
21
+ export declare interface MockApiOptions {
22
+ /**
23
+ * Uri to mock
24
+ */
25
+ uri: string;
26
+ /**
27
+ * Api host to mock, defaults to `https://api.sanity.io`
28
+ */
29
+ apiHost?: string;
30
+ /**
31
+ * Api version to mock, defaults to `v2025-05-14`
32
+ */
33
+ apiVersion?: string;
34
+ /**
35
+ * Whether to include `tag: 'sanity.cli'` in query parameters.
36
+ * Defaults to `true`. Set to `false` for endpoints that don't use CLI tagging.
37
+ */
38
+ includeQueryTag?: boolean;
39
+ /**
40
+ * HTTP method to mock
41
+ *
42
+ * Defaults to 'get'
43
+ */
44
+ method?: "delete" | "get" | "patch" | "post" | "put";
45
+ /**
46
+ * Project ID to mock. When provided, constructs apiHost as `https://{projectId}.api.sanity.io`
47
+ * Takes precedence over apiHost if both are provided.
48
+ */
49
+ projectId?: string;
50
+ /**
51
+ * Query parameters to mock
52
+ */
53
+ query?: Record<string, string>;
54
+ }
55
+
56
+ export {};
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/test/mockSanityCommand.ts"],"sourcesContent":["import {type Command} from '@oclif/core'\nimport {type CliConfig, type ProjectRootResult, SanityCommand} from '@sanity/cli-core'\n\nimport {convertToSystemPath} from '../utils/paths.js'\nimport {createTestToken} from './createTestToken.js'\n\n/**\n * @public\n */\nexport interface MockSanityCommandOptions {\n /**\n * Mock CLI config (required if command uses getCliConfig or getProjectId)\n */\n cliConfig?: CliConfig\n /**\n * When provided, getCliConfig() will throw this error instead of returning a config.\n * Useful for simulating running outside a project directory.\n */\n cliConfigError?: Error\n /**\n * Mock whether the terminal is interactive (used by isUnattended)\n */\n isInteractive?: boolean\n /**\n * Mock project root result (required if command uses getProjectRoot)\n */\n projectRoot?: ProjectRootResult\n /**\n * Mock authentication token (passed to API clients, bypasses getCliToken)\n */\n token?: string\n}\n\n/**\n * Creates a testable subclass of a command with mocked SanityCommand dependencies.\n *\n * @public\n *\n * @example\n * ```ts\n * // Basic config mocking\n * const TestAdd = mockSanityCommand(Add, {\n * cliConfig: { api: { projectId: 'test-project' } }\n * })\n *\n * // With mock API client\n * const mockClient = {\n * getDocument: vi.fn().mockResolvedValue({ _id: 'doc1', title: 'Test' }),\n * fetch: vi.fn().mockResolvedValue([]),\n * }\n * const TestGet = mockSanityCommand(GetDocumentCommand, {\n * cliConfig: { api: { projectId: 'test-project', dataset: 'production' } },\n * projectApiClient: mockClient,\n * })\n *\n * const {stdout} = await testCommand(TestGet, ['doc1'])\n * expect(mockClient.getDocument).toHaveBeenCalledWith('doc1')\n * ```\n */\nexport function mockSanityCommand<T extends typeof SanityCommand<typeof Command>>(\n CommandClass: T,\n options: MockSanityCommandOptions = {},\n): T {\n if (options.token) {\n createTestToken(options.token)\n }\n\n // Auto-convert paths in projectRoot to platform-appropriate format\n const projectRoot = options.projectRoot\n ? {\n ...options.projectRoot,\n directory: convertToSystemPath(options.projectRoot.directory),\n path: convertToSystemPath(options.projectRoot.path),\n }\n : undefined\n\n // Create a subclass that overrides methods when mocks are provided\n // Note: we use @ts-expect-error because TypeScript can't properly infer\n // the relationship between the generic CommandClass and SanityCommand\n // @ts-expect-error - TypeScript struggles with abstract class subclassing\n class MockedCommand extends CommandClass {\n protected getCliConfig(): Promise<CliConfig> {\n if (options.cliConfigError) {\n return Promise.reject(options.cliConfigError)\n }\n if (options.cliConfig) {\n return Promise.resolve(options.cliConfig)\n }\n return super.getCliConfig()\n }\n\n protected getProjectRoot(): Promise<ProjectRootResult> {\n if (\n options.cliConfigError &&\n 'name' in options.cliConfigError &&\n options.cliConfigError.name === 'ProjectRootNotFoundError'\n ) {\n return Promise.reject(options.cliConfigError)\n }\n if (projectRoot) {\n return Promise.resolve(projectRoot)\n }\n return super.getProjectRoot()\n }\n\n protected resolveIsInteractive(): boolean {\n if (options.isInteractive !== undefined) {\n return options.isInteractive\n }\n return super.resolveIsInteractive()\n }\n }\n\n return MockedCommand as T\n}\n"],"names":["convertToSystemPath","createTestToken","mockSanityCommand","CommandClass","options","token","projectRoot","directory","path","undefined","MockedCommand","getCliConfig","cliConfigError","Promise","reject","cliConfig","resolve","getProjectRoot","name","resolveIsInteractive","isInteractive"],"mappings":"AAGA,SAAQA,mBAAmB,QAAO,oBAAmB;AACrD,SAAQC,eAAe,QAAO,uBAAsB;AA6BpD;;;;;;;;;;;;;;;;;;;;;;;;;CAyBC,GACD,OAAO,SAASC,kBACdC,YAAe,EACfC,UAAoC,CAAC,CAAC;IAEtC,IAAIA,QAAQC,KAAK,EAAE;QACjBJ,gBAAgBG,QAAQC,KAAK;IAC/B;IAEA,mEAAmE;IACnE,MAAMC,cAAcF,QAAQE,WAAW,GACnC;QACE,GAAGF,QAAQE,WAAW;QACtBC,WAAWP,oBAAoBI,QAAQE,WAAW,CAACC,SAAS;QAC5DC,MAAMR,oBAAoBI,QAAQE,WAAW,CAACE,IAAI;IACpD,IACAC;IAEJ,mEAAmE;IACnE,wEAAwE;IACxE,sEAAsE;IACtE,0EAA0E;IAC1E,MAAMC,sBAAsBP;QAChBQ,eAAmC;YAC3C,IAAIP,QAAQQ,cAAc,EAAE;gBAC1B,OAAOC,QAAQC,MAAM,CAACV,QAAQQ,cAAc;YAC9C;YACA,IAAIR,QAAQW,SAAS,EAAE;gBACrB,OAAOF,QAAQG,OAAO,CAACZ,QAAQW,SAAS;YAC1C;YACA,OAAO,KAAK,CAACJ;QACf;QAEUM,iBAA6C;YACrD,IACEb,QAAQQ,cAAc,IACtB,UAAUR,QAAQQ,cAAc,IAChCR,QAAQQ,cAAc,CAACM,IAAI,KAAK,4BAChC;gBACA,OAAOL,QAAQC,MAAM,CAACV,QAAQQ,cAAc;YAC9C;YACA,IAAIN,aAAa;gBACf,OAAOO,QAAQG,OAAO,CAACV;YACzB;YACA,OAAO,KAAK,CAACW;QACf;QAEUE,uBAAgC;YACxC,IAAIf,QAAQgB,aAAa,KAAKX,WAAW;gBACvC,OAAOL,QAAQgB,aAAa;YAC9B;YACA,OAAO,KAAK,CAACD;QACf;IACF;IAEA,OAAOT;AACT"}
1
+ {"version":3,"sources":["../../src/test/mockSanityCommand.ts"],"sourcesContent":["import {type Command} from '@oclif/core'\nimport {SanityCommand} from '@sanity/cli-core/SanityCommand'\nimport {type CliConfig, type ProjectRootResult} from '@sanity/cli-core/types'\n\nimport {convertToSystemPath} from '../utils/paths.js'\nimport {createTestToken} from './createTestToken.js'\n\n/**\n * @public\n */\nexport interface MockSanityCommandOptions {\n /**\n * Mock CLI config (required if command uses getCliConfig or getProjectId)\n */\n cliConfig?: CliConfig\n /**\n * When provided, getCliConfig() will throw this error instead of returning a config.\n * Useful for simulating running outside a project directory.\n */\n cliConfigError?: Error\n /**\n * Mock whether the terminal is interactive (used by isUnattended)\n */\n isInteractive?: boolean\n /**\n * Mock project root result (required if command uses getProjectRoot)\n */\n projectRoot?: ProjectRootResult\n /**\n * Mock authentication token (passed to API clients, bypasses getCliToken)\n */\n token?: string\n}\n\n/**\n * Creates a testable subclass of a command with mocked SanityCommand dependencies.\n *\n * @public\n *\n * @example\n * ```ts\n * // Basic config mocking\n * const TestAdd = mockSanityCommand(Add, {\n * cliConfig: { api: { projectId: 'test-project' } }\n * })\n *\n * // With mock API client\n * const mockClient = {\n * getDocument: vi.fn().mockResolvedValue({ _id: 'doc1', title: 'Test' }),\n * fetch: vi.fn().mockResolvedValue([]),\n * }\n * const TestGet = mockSanityCommand(GetDocumentCommand, {\n * cliConfig: { api: { projectId: 'test-project', dataset: 'production' } },\n * projectApiClient: mockClient,\n * })\n *\n * const {stdout} = await testCommand(TestGet, ['doc1'])\n * expect(mockClient.getDocument).toHaveBeenCalledWith('doc1')\n * ```\n */\nexport function mockSanityCommand<T extends typeof SanityCommand<typeof Command>>(\n CommandClass: T,\n options: MockSanityCommandOptions = {},\n): T {\n if (options.token) {\n createTestToken(options.token)\n }\n\n // Auto-convert paths in projectRoot to platform-appropriate format\n const projectRoot = options.projectRoot\n ? {\n ...options.projectRoot,\n directory: convertToSystemPath(options.projectRoot.directory),\n path: convertToSystemPath(options.projectRoot.path),\n }\n : undefined\n\n // Create a subclass that overrides methods when mocks are provided\n // Note: we use @ts-expect-error because TypeScript can't properly infer\n // the relationship between the generic CommandClass and SanityCommand\n // @ts-expect-error - TypeScript struggles with abstract class subclassing\n class MockedCommand extends CommandClass {\n protected getCliConfig(): Promise<CliConfig> {\n if (options.cliConfigError) {\n return Promise.reject(options.cliConfigError)\n }\n if (options.cliConfig) {\n return Promise.resolve(options.cliConfig)\n }\n return super.getCliConfig()\n }\n\n protected getProjectRoot(): Promise<ProjectRootResult> {\n if (\n options.cliConfigError &&\n 'name' in options.cliConfigError &&\n options.cliConfigError.name === 'ProjectRootNotFoundError'\n ) {\n return Promise.reject(options.cliConfigError)\n }\n if (projectRoot) {\n return Promise.resolve(projectRoot)\n }\n return super.getProjectRoot()\n }\n\n protected resolveIsInteractive(): boolean {\n if (options.isInteractive !== undefined) {\n return options.isInteractive\n }\n return super.resolveIsInteractive()\n }\n }\n\n return MockedCommand as T\n}\n"],"names":["convertToSystemPath","createTestToken","mockSanityCommand","CommandClass","options","token","projectRoot","directory","path","undefined","MockedCommand","getCliConfig","cliConfigError","Promise","reject","cliConfig","resolve","getProjectRoot","name","resolveIsInteractive","isInteractive"],"mappings":"AAIA,SAAQA,mBAAmB,QAAO,oBAAmB;AACrD,SAAQC,eAAe,QAAO,uBAAsB;AA6BpD;;;;;;;;;;;;;;;;;;;;;;;;;CAyBC,GACD,OAAO,SAASC,kBACdC,YAAe,EACfC,UAAoC,CAAC,CAAC;IAEtC,IAAIA,QAAQC,KAAK,EAAE;QACjBJ,gBAAgBG,QAAQC,KAAK;IAC/B;IAEA,mEAAmE;IACnE,MAAMC,cAAcF,QAAQE,WAAW,GACnC;QACE,GAAGF,QAAQE,WAAW;QACtBC,WAAWP,oBAAoBI,QAAQE,WAAW,CAACC,SAAS;QAC5DC,MAAMR,oBAAoBI,QAAQE,WAAW,CAACE,IAAI;IACpD,IACAC;IAEJ,mEAAmE;IACnE,wEAAwE;IACxE,sEAAsE;IACtE,0EAA0E;IAC1E,MAAMC,sBAAsBP;QAChBQ,eAAmC;YAC3C,IAAIP,QAAQQ,cAAc,EAAE;gBAC1B,OAAOC,QAAQC,MAAM,CAACV,QAAQQ,cAAc;YAC9C;YACA,IAAIR,QAAQW,SAAS,EAAE;gBACrB,OAAOF,QAAQG,OAAO,CAACZ,QAAQW,SAAS;YAC1C;YACA,OAAO,KAAK,CAACJ;QACf;QAEUM,iBAA6C;YACrD,IACEb,QAAQQ,cAAc,IACtB,UAAUR,QAAQQ,cAAc,IAChCR,QAAQQ,cAAc,CAACM,IAAI,KAAK,4BAChC;gBACA,OAAOL,QAAQC,MAAM,CAACV,QAAQQ,cAAc;YAC9C;YACA,IAAIN,aAAa;gBACf,OAAOO,QAAQG,OAAO,CAACV;YACzB;YACA,OAAO,KAAK,CAACW;QACf;QAEUE,uBAAgC;YACxC,IAAIf,QAAQgB,aAAa,KAAKX,WAAW;gBACvC,OAAOL,QAAQgB,aAAa;YAC9B;YACA,OAAO,KAAK,CAACD;QACf;IACF;IAEA,OAAOT;AACT"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/test/mockTelemetry.ts"],"sourcesContent":["import {type CLITelemetryStore} from '@sanity/cli-core'\nimport {vi} from 'vitest'\n\n/**\n * @public\n */\nexport interface MockTelemetryOptions {\n trace?: () => void\n updateUserProperties?: () => void\n}\n\n/**\n * @public\n * @param options - Options for mocking the telemetry store.\n * @returns The mocked telemetry store.\n */\nexport const mockTelemetry = (options: MockTelemetryOptions = {}): CLITelemetryStore => {\n const telemetry = {\n trace: vi.fn().mockReturnValue({\n complete: vi.fn(),\n error: vi.fn(),\n log: vi.fn(),\n newContext: vi.fn(),\n start: vi.fn(),\n }),\n updateUserProperties: vi.fn(),\n ...options,\n } as unknown as CLITelemetryStore\n\n // We are not using the export from @sanity/cli-core because\n // This can be used where `@sanity/cli-core` is mocked and vitest will run into issues.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ;(globalThis as any)[Symbol.for('sanity.cli.telemetry')] = {logger: telemetry}\n\n return telemetry\n}\n"],"names":["vi","mockTelemetry","options","telemetry","trace","fn","mockReturnValue","complete","error","log","newContext","start","updateUserProperties","globalThis","Symbol","for","logger"],"mappings":"AACA,SAAQA,EAAE,QAAO,SAAQ;AAUzB;;;;CAIC,GACD,OAAO,MAAMC,gBAAgB,CAACC,UAAgC,CAAC,CAAC;IAC9D,MAAMC,YAAY;QAChBC,OAAOJ,GAAGK,EAAE,GAAGC,eAAe,CAAC;YAC7BC,UAAUP,GAAGK,EAAE;YACfG,OAAOR,GAAGK,EAAE;YACZI,KAAKT,GAAGK,EAAE;YACVK,YAAYV,GAAGK,EAAE;YACjBM,OAAOX,GAAGK,EAAE;QACd;QACAO,sBAAsBZ,GAAGK,EAAE;QAC3B,GAAGH,OAAO;IACZ;IAKEW,UAAkB,CAACC,OAAOC,GAAG,CAAC,wBAAwB,GAAG;QAACC,QAAQb;IAAS;IAE7E,OAAOA;AACT,EAAC"}
1
+ {"version":3,"sources":["../../src/test/mockTelemetry.ts"],"sourcesContent":["import {type CLITelemetryStore} from '@sanity/cli-core/types'\nimport {vi} from 'vitest'\n\n/**\n * @public\n */\nexport interface MockTelemetryOptions {\n trace?: () => void\n updateUserProperties?: () => void\n}\n\n/**\n * @public\n * @param options - Options for mocking the telemetry store.\n * @returns The mocked telemetry store.\n */\nexport const mockTelemetry = (options: MockTelemetryOptions = {}): CLITelemetryStore => {\n const telemetry = {\n trace: vi.fn().mockReturnValue({\n complete: vi.fn(),\n error: vi.fn(),\n log: vi.fn(),\n newContext: vi.fn(),\n start: vi.fn(),\n }),\n updateUserProperties: vi.fn(),\n ...options,\n } as unknown as CLITelemetryStore\n\n // We are not using the export from @sanity/cli-core because\n // This can be used where `@sanity/cli-core` is mocked and vitest will run into issues.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ;(globalThis as any)[Symbol.for('sanity.cli.telemetry')] = {logger: telemetry}\n\n return telemetry\n}\n"],"names":["vi","mockTelemetry","options","telemetry","trace","fn","mockReturnValue","complete","error","log","newContext","start","updateUserProperties","globalThis","Symbol","for","logger"],"mappings":"AACA,SAAQA,EAAE,QAAO,SAAQ;AAUzB;;;;CAIC,GACD,OAAO,MAAMC,gBAAgB,CAACC,UAAgC,CAAC,CAAC;IAC9D,MAAMC,YAAY;QAChBC,OAAOJ,GAAGK,EAAE,GAAGC,eAAe,CAAC;YAC7BC,UAAUP,GAAGK,EAAE;YACfG,OAAOR,GAAGK,EAAE;YACZI,KAAKT,GAAGK,EAAE;YACVK,YAAYV,GAAGK,EAAE;YACjBM,OAAOX,GAAGK,EAAE;QACd;QACAO,sBAAsBZ,GAAGK,EAAE;QAC3B,GAAGH,OAAO;IACZ;IAKEW,UAAkB,CAACC,OAAOC,GAAG,CAAC,wBAAwB,GAAG;QAACC,QAAQb;IAAS;IAE7E,OAAOA;AACT,EAAC"}
@@ -0,0 +1,179 @@
1
+ import { CliConfig } from "@sanity/cli-core/types";
2
+ import { Command } from "@oclif/core";
3
+ import { Config } from "@oclif/core";
4
+ import { Errors } from "@oclif/core";
5
+ import { Hook } from "@oclif/core/hooks";
6
+ import { Hooks } from "@oclif/core/hooks";
7
+ import { ProjectRootResult } from "@sanity/cli-core/types";
8
+ import { SanityCommand } from "@sanity/cli-core/SanityCommand";
9
+
10
+ /** @public */
11
+ export declare interface CaptureOptions {
12
+ /**
13
+ * Whether to print the output to the console
14
+ */
15
+ print?: boolean;
16
+ /**
17
+ * Whether to strip ANSI escape codes from the output
18
+ */
19
+ stripAnsi?: boolean;
20
+ testNodeEnv?: string;
21
+ }
22
+
23
+ /**
24
+ * Capture the output of a command and return the result
25
+ *
26
+ * @param fn - The function to capture the output of
27
+ * @param opts - The options for the capture
28
+ * @returns The result of the command
29
+ * @public
30
+ *
31
+ * Credits to oclif for the original implementation:
32
+ * https://github.com/oclif/test/blob/2a5407e6fc80d388043d10f6b7b8eaa586483015/src/index.ts
33
+ *
34
+ * We are not using the library directly since it does not support mocking code inside of the command
35
+ * possibly because the commands run in a different thread
36
+ */
37
+ export declare function captureOutput<T>(
38
+ fn: () => Promise<T>,
39
+ opts?: CaptureOptions,
40
+ ): Promise<CaptureResult<T>>;
41
+
42
+ /** @public */
43
+ export declare interface CaptureResult<T = unknown> {
44
+ stderr: string;
45
+ stdout: string;
46
+ error?: Error & Partial<Errors.CLIError>;
47
+ result?: T;
48
+ }
49
+
50
+ declare type CommandClass = (new (argv: string[], config: Config) => Command) &
51
+ typeof Command;
52
+
53
+ /**
54
+ * Creates a testable subclass of a command with mocked SanityCommand dependencies.
55
+ *
56
+ * @public
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * // Basic config mocking
61
+ * const TestAdd = mockSanityCommand(Add, {
62
+ * cliConfig: { api: { projectId: 'test-project' } }
63
+ * })
64
+ *
65
+ * // With mock API client
66
+ * const mockClient = {
67
+ * getDocument: vi.fn().mockResolvedValue({ _id: 'doc1', title: 'Test' }),
68
+ * fetch: vi.fn().mockResolvedValue([]),
69
+ * }
70
+ * const TestGet = mockSanityCommand(GetDocumentCommand, {
71
+ * cliConfig: { api: { projectId: 'test-project', dataset: 'production' } },
72
+ * projectApiClient: mockClient,
73
+ * })
74
+ *
75
+ * const {stdout} = await testCommand(TestGet, ['doc1'])
76
+ * expect(mockClient.getDocument).toHaveBeenCalledWith('doc1')
77
+ * ```
78
+ */
79
+ export declare function mockSanityCommand<
80
+ T extends typeof SanityCommand<typeof Command>,
81
+ >(CommandClass: T, options?: MockSanityCommandOptions): T;
82
+
83
+ /**
84
+ * @public
85
+ */
86
+ export declare interface MockSanityCommandOptions {
87
+ /**
88
+ * Mock CLI config (required if command uses getCliConfig or getProjectId)
89
+ */
90
+ cliConfig?: CliConfig;
91
+ /**
92
+ * When provided, getCliConfig() will throw this error instead of returning a config.
93
+ * Useful for simulating running outside a project directory.
94
+ */
95
+ cliConfigError?: Error;
96
+ /**
97
+ * Mock whether the terminal is interactive (used by isUnattended)
98
+ */
99
+ isInteractive?: boolean;
100
+ /**
101
+ * Mock project root result (required if command uses getProjectRoot)
102
+ */
103
+ projectRoot?: ProjectRootResult;
104
+ /**
105
+ * Mock authentication token (passed to API clients, bypasses getCliToken)
106
+ */
107
+ token?: string;
108
+ }
109
+
110
+ /**
111
+ * @public
112
+ */
113
+ declare interface MockTelemetryOptions {
114
+ trace?: () => void;
115
+ updateUserProperties?: () => void;
116
+ }
117
+
118
+ declare interface Options {
119
+ config: Config;
120
+ Command?: Command.Class;
121
+ context?: Hook.Context;
122
+ }
123
+
124
+ /**
125
+ * @public
126
+ */
127
+ export declare function testCommand(
128
+ command: CommandClass,
129
+ args?: string[],
130
+ options?: TestCommandOptions,
131
+ ): Promise<CaptureResult<unknown>>;
132
+
133
+ /**
134
+ * @public
135
+ */
136
+ export declare interface TestCommandOptions {
137
+ /**
138
+ * Options for capturing output
139
+ */
140
+ capture?: CaptureOptions;
141
+ /**
142
+ * Partial oclif config overrides
143
+ */
144
+ config?: Partial<Config>;
145
+ /**
146
+ * Mock options for SanityCommand dependencies (config, project root, API clients).
147
+ * When provided, the command is automatically wrapped with mockSanityCommand.
148
+ */
149
+ mocks?: MockSanityCommandOptions & MockTelemetryOptions;
150
+ }
151
+
152
+ /**
153
+ * Test an oclif hook
154
+ *
155
+ * @public
156
+ *
157
+ * @example
158
+ * ```ts
159
+ * const result = await testHook(hook, {
160
+ * Command: Command.loadable,
161
+ * context: {
162
+ * config: Config.load({
163
+ * // CLI root is the directory of the package that contains the hook.
164
+ * root: path.resolve(fileURLToPath(import.meta.url), '../../../root'),
165
+ * }),
166
+ * },
167
+ * })
168
+ * ```
169
+ *
170
+ * @param hook - The hook to test
171
+ * @param options - The options for the hook
172
+ * @returns The result of the hook
173
+ */
174
+ export declare function testHook<T extends keyof Hooks>(
175
+ hook: Hook<T>,
176
+ options: Options,
177
+ ): Promise<CaptureResult<Hooks[T]["return"]>>;
178
+
179
+ export {};
@@ -0,0 +1,7 @@
1
+ // Heavy integration test tooling goes here. OCLIF is a transient dependency. Do not rely on this tooling - instead write unit tests!
2
+ export * from './captureOutput.js';
3
+ export * from './mockSanityCommand.js';
4
+ export * from './testCommand.js';
5
+ export * from './testHook.js';
6
+
7
+ //# sourceMappingURL=oclif.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/test/oclif.ts"],"sourcesContent":["// Heavy integration test tooling goes here. OCLIF is a transient dependency. Do not rely on this tooling - instead write unit tests!\nexport * from './captureOutput.js'\nexport * from './mockSanityCommand.js'\nexport * from './testCommand.js'\nexport * from './testHook.js'\n"],"names":[],"mappings":"AAAA,qIAAqI;AACrI,cAAc,qBAAoB;AAClC,cAAc,yBAAwB;AACtC,cAAc,mBAAkB;AAChC,cAAc,gBAAe"}
@@ -0,0 +1,90 @@
1
+ import { TestProject } from "vitest/node";
2
+
3
+ /**
4
+ * Global setup function for initializing test fixtures.
5
+ *
6
+ * Copies fixtures from the bundled location to a temp directory
7
+ * and installs dependencies.
8
+ *
9
+ * Note: Fixtures are NOT built during setup. Tests that need built
10
+ * fixtures should build them as part of the test.
11
+ *
12
+ * This function is designed to be used with vitest globalSetup.
13
+ *
14
+ * @public
15
+ *
16
+ * @param options - Configuration options
17
+ * @example
18
+ * ```typescript
19
+ * // In vitest.config.ts
20
+ * export default defineConfig({
21
+ * test: {
22
+ * globalSetup: ['@sanity/cli-test/vitest']
23
+ * }
24
+ * })
25
+ * ```
26
+ */
27
+ export declare function setup(
28
+ _: TestProject,
29
+ options?: SetupTestFixturesOptions,
30
+ ): Promise<void>;
31
+
32
+ /**
33
+ * Options for setupTestFixtures
34
+ *
35
+ * @public
36
+ */
37
+ export declare interface SetupTestFixturesOptions {
38
+ /**
39
+ * Glob patterns for additional fixture directories to set up.
40
+ *
41
+ * Each pattern is matched against directories in the current working directory.
42
+ * Only directories containing a `package.json` file are included.
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * ['fixtures/*', 'dev/*']
47
+ * ```
48
+ */
49
+ additionalFixtures?: string[];
50
+ /**
51
+ * When true, passes `--ignore-workspace` to pnpm install so fixtures get
52
+ * their own node_modules even when the temp directory is inside a pnpm
53
+ * workspace. Required for E2E tests that spawn the CLI binary against
54
+ * fixture directories.
55
+ */
56
+ ignoreWorkspace?: boolean;
57
+ /**
58
+ * Custom temp directory path. Defaults to process.cwd()/tmp
59
+ */
60
+ tempDir?: string;
61
+ }
62
+
63
+ /**
64
+ * Teardown function to clean up test fixtures.
65
+ *
66
+ * Removes the temp directory created by setupTestFixtures.
67
+ *
68
+ * This function is designed to be used with vitest globalSetup.
69
+ *
70
+ * @public
71
+ *
72
+ * @param options - Configuration options
73
+ */
74
+ export declare function teardown(
75
+ options?: TeardownTestFixturesOptions,
76
+ ): Promise<void>;
77
+
78
+ /**
79
+ * Options for teardownTestFixtures
80
+ *
81
+ * @public
82
+ */
83
+ export declare interface TeardownTestFixturesOptions {
84
+ /**
85
+ * Custom temp directory path. Defaults to process.cwd()/tmp
86
+ */
87
+ tempDir?: string;
88
+ }
89
+
90
+ export {};