@mintlify/cli 4.0.604 → 4.0.606

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/CONTRIBUTING.md CHANGED
@@ -43,3 +43,7 @@ If for some reason the release workflow is broken or we can't access AWS program
43
43
  ## Manually overriding the client version used locally
44
44
 
45
45
  Run `mintlify dev --client-version x.x.xxxx` with the client version you would like to use and that version will be downloaded instead of the latest client version.
46
+
47
+ ## Breaking changes
48
+
49
+ If cli or client contain breaking changes that require they are both updated together, change the MINIMUM_CLI_VERSION to make sure `mint dev` will silently update to a minimum viable CLI version.
@@ -0,0 +1,106 @@
1
+ import { getBrokenInternalLinks } from '@mintlify/link-rot';
2
+ import { MdxPath } from '@mintlify/link-rot/dist/graph.js';
3
+ import * as previewing from '@mintlify/previewing';
4
+ import { mockProcessExit } from 'vitest-mock-process';
5
+
6
+ import { checkForMintJson } from '../src/helpers.js';
7
+ import { runCommand } from './utils.js';
8
+
9
+ vi.mock('../src/helpers.js', async () => {
10
+ const actual = await import('../src/helpers.js');
11
+ return {
12
+ ...actual,
13
+ checkForMintJson: vi.fn(),
14
+ };
15
+ });
16
+
17
+ vi.mock('@mintlify/link-rot', async () => {
18
+ const actual = await import('@mintlify/link-rot');
19
+ return {
20
+ ...actual,
21
+ getBrokenInternalLinks: vi.fn(),
22
+ };
23
+ });
24
+
25
+ const addLogSpy = vi.spyOn(previewing, 'addLog');
26
+ const clearLogsSpy = vi.spyOn(previewing, 'clearLogs');
27
+ const processExitMock = mockProcessExit();
28
+
29
+ describe('brokenLinks', () => {
30
+ beforeEach(() => {
31
+ vi.clearAllMocks();
32
+ });
33
+
34
+ afterEach(() => {
35
+ vi.clearAllMocks();
36
+ });
37
+
38
+ it('success with no broken links', async () => {
39
+ vi.mocked(checkForMintJson).mockResolvedValueOnce(true);
40
+ vi.mocked(getBrokenInternalLinks).mockResolvedValueOnce([]);
41
+
42
+ await runCommand('broken-links');
43
+
44
+ expect(addLogSpy).toHaveBeenCalledWith(
45
+ expect.objectContaining({
46
+ props: { message: 'checking for broken links...' },
47
+ })
48
+ );
49
+ expect(addLogSpy).toHaveBeenCalledWith(
50
+ expect.objectContaining({
51
+ props: { message: 'no broken links found' },
52
+ })
53
+ );
54
+ expect(processExitMock).toHaveBeenCalledWith(0);
55
+ });
56
+
57
+ it('success with broken links', async () => {
58
+ vi.mocked(checkForMintJson).mockResolvedValueOnce(true);
59
+ vi.mocked(getBrokenInternalLinks).mockResolvedValueOnce([
60
+ {
61
+ relativeDir: '.',
62
+ filename: 'introduction.mdx',
63
+ originalPath: '/api/invalid-path',
64
+ pathType: 'internal',
65
+ } as MdxPath,
66
+ ]);
67
+
68
+ await runCommand('broken-links');
69
+
70
+ expect(addLogSpy).toHaveBeenCalledWith(
71
+ expect.objectContaining({
72
+ props: { message: 'checking for broken links...' },
73
+ })
74
+ );
75
+ expect(clearLogsSpy).toHaveBeenCalled();
76
+ expect(addLogSpy).toHaveBeenCalledWith(
77
+ expect.objectContaining({
78
+ props: {
79
+ brokenLinksByFile: {
80
+ 'introduction.mdx': ['/api/invalid-path'],
81
+ },
82
+ },
83
+ })
84
+ );
85
+ expect(processExitMock).toHaveBeenCalledWith(0);
86
+ });
87
+
88
+ it('error with broken links', async () => {
89
+ vi.mocked(checkForMintJson).mockResolvedValueOnce(true);
90
+ vi.mocked(getBrokenInternalLinks).mockRejectedValueOnce(new Error('some error'));
91
+
92
+ await runCommand('broken-links');
93
+
94
+ expect(addLogSpy).toHaveBeenCalledWith(
95
+ expect.objectContaining({
96
+ props: { message: 'checking for broken links...' },
97
+ })
98
+ );
99
+ expect(addLogSpy).toHaveBeenCalledWith(
100
+ expect.objectContaining({
101
+ props: { message: 'some error' },
102
+ })
103
+ );
104
+ expect(processExitMock).toHaveBeenCalledWith(1);
105
+ });
106
+ });
@@ -1,9 +1,17 @@
1
- import { dev } from '@mintlify/previewing';
1
+ import * as previewing from '@mintlify/previewing';
2
2
  import inquirer from 'inquirer';
3
+ import { mockProcessExit } from 'vitest-mock-process';
3
4
 
4
5
  import { runCommand } from './utils.js';
5
6
 
6
- vi.mock('@mintlify/previewing', () => ({ dev: vi.fn() }));
7
+ vi.mock('@mintlify/previewing', async () => {
8
+ const originalModule =
9
+ await vi.importActual<typeof import('@mintlify/previewing')>('@mintlify/previewing');
10
+ return {
11
+ ...originalModule,
12
+ dev: vi.fn(),
13
+ };
14
+ });
7
15
 
8
16
  vi.mock('readline/promises', () => {
9
17
  return {
@@ -25,7 +33,11 @@ vi.mock('detect-port', () => ({
25
33
  default: (port: number) => (allowedPorts.includes(port) ? port : port + 1),
26
34
  }));
27
35
 
28
- describe('cli', () => {
36
+ const devSpy = vi.spyOn(previewing, 'dev');
37
+ const addLogSpy = vi.spyOn(previewing, 'addLog');
38
+ const processExitMock = mockProcessExit();
39
+
40
+ describe('checkPort', () => {
29
41
  let originalArgv: string[];
30
42
 
31
43
  beforeEach(() => {
@@ -49,28 +61,32 @@ describe('cli', () => {
49
61
  it('should run dev command', async () => {
50
62
  await runCommand('dev');
51
63
 
52
- expect(dev).toHaveBeenCalledWith(expect.objectContaining({ port: 3000 }));
64
+ expect(devSpy).toHaveBeenCalledWith(expect.objectContaining({ port: 3000 }));
53
65
  });
54
66
 
55
67
  it('port 5000 and 5001 should be taken and 5002 should be accepted by the user and available.', async () => {
56
- const consoleSpy = vi.spyOn(console, 'log');
57
-
58
68
  await runCommand('dev', '--port=5000');
59
69
 
60
- expect(consoleSpy).toHaveBeenCalledTimes(2);
61
- expect(consoleSpy).toHaveBeenCalledWith(`Port 5000 is already in use. Trying 5001 instead.`);
62
- expect(consoleSpy).toHaveBeenCalledWith(`Port 5001 is already in use. Trying 5002 instead.`);
63
- expect(dev).toHaveBeenCalledWith(expect.objectContaining({ port: 5002 }));
70
+ expect(addLogSpy).toHaveBeenCalledTimes(2);
71
+ expect(addLogSpy).toHaveBeenCalledWith(
72
+ expect.objectContaining({
73
+ props: { message: 'port 5000 is already in use. trying 5001 instead' },
74
+ })
75
+ );
76
+ expect(addLogSpy).toHaveBeenCalledWith(
77
+ expect.objectContaining({
78
+ props: { message: 'port 5001 is already in use. trying 5002 instead' },
79
+ })
80
+ );
64
81
  });
65
82
 
66
83
  it('fails after the 10th used port', async () => {
67
- const consoleSpy = vi.spyOn(console, 'log');
68
-
69
84
  await runCommand('dev', '--port=8000');
70
85
 
71
- expect(consoleSpy).toHaveBeenCalledTimes(9);
72
- expect(consoleSpy).toHaveBeenLastCalledWith(
73
- `Port 8008 is already in use. Trying 8009 instead.`
86
+ expect(addLogSpy).toHaveBeenCalledTimes(10);
87
+ expect(addLogSpy).toHaveBeenLastCalledWith(
88
+ expect.objectContaining({ props: { message: 'no available port found' } })
74
89
  );
90
+ expect(processExitMock).toHaveBeenCalledWith(1);
75
91
  });
76
92
  });
@@ -1,25 +1,36 @@
1
- import { dev } from '@mintlify/previewing';
1
+ import * as previewing from '@mintlify/previewing';
2
2
 
3
3
  import { LOCAL_LINKED_VERSION } from '../src/constants.js';
4
4
  import { getCliVersion } from '../src/helpers.js';
5
5
  import * as updateModule from '../src/update.js';
6
6
  import { runCommand } from './utils.js';
7
7
 
8
- vi.mock('../src/helpers.js', async (importOriginal) => {
9
- const original = await importOriginal();
8
+ vi.mock('@mintlify/previewing', async () => {
9
+ const originalModule =
10
+ await vi.importActual<typeof import('@mintlify/previewing')>('@mintlify/previewing');
10
11
  return {
11
- // @ts-expect-error - this is a mock
12
- ...original,
13
- getCliVersion: vi.fn(),
12
+ ...originalModule,
13
+ dev: vi.fn().mockResolvedValue(undefined),
14
+ addLog: vi.fn().mockResolvedValue(undefined),
14
15
  };
15
16
  });
16
17
 
17
- vi.mock('@mintlify/previewing', () => ({ dev: vi.fn() }));
18
+ vi.mock('../src/helpers.js', async () => {
19
+ const originalModule =
20
+ await vi.importActual<typeof import('../src/helpers.js')>('../src/helpers.js');
21
+ return {
22
+ ...originalModule,
23
+ getCliVersion: vi.fn(),
24
+ };
25
+ });
18
26
 
19
27
  vi.mock('../src/update.js', () => ({
20
28
  update: vi.fn().mockResolvedValue(undefined),
21
29
  }));
22
30
 
31
+ const devSpy = vi.spyOn(previewing, 'dev');
32
+ const addLogSpy = vi.spyOn(previewing, 'addLog');
33
+
23
34
  describe('minimum version', () => {
24
35
  beforeEach(() => {
25
36
  vi.clearAllMocks();
@@ -36,7 +47,8 @@ describe('minimum version', () => {
36
47
  await runCommand('dev');
37
48
 
38
49
  expect(updateSpy).toHaveBeenCalled();
39
- expect(dev).toHaveBeenCalled();
50
+ expect(devSpy).toHaveBeenCalled();
51
+ expect(addLogSpy).not.toHaveBeenCalled();
40
52
  });
41
53
  it('should not update the cli if the version is above the minimum', async () => {
42
54
  vi.mocked(getCliVersion).mockReturnValueOnce('4.2.1');
@@ -45,7 +57,8 @@ describe('minimum version', () => {
45
57
  await runCommand('dev');
46
58
 
47
59
  expect(updateSpy).not.toHaveBeenCalled();
48
- expect(dev).toHaveBeenCalled();
60
+ expect(devSpy).toHaveBeenCalled();
61
+ expect(addLogSpy).not.toHaveBeenCalled();
49
62
  });
50
63
  it('should not update the cli if the version is linked to local package', async () => {
51
64
  vi.mocked(getCliVersion).mockReturnValueOnce(LOCAL_LINKED_VERSION);
@@ -54,6 +67,7 @@ describe('minimum version', () => {
54
67
  await runCommand('dev');
55
68
 
56
69
  expect(updateSpy).not.toHaveBeenCalled();
57
- expect(dev).toHaveBeenCalled();
70
+ expect(devSpy).toHaveBeenCalled();
71
+ expect(addLogSpy).not.toHaveBeenCalled();
58
72
  });
59
73
  });
@@ -0,0 +1,127 @@
1
+ import { getOpenApiDocumentFromUrl, isAllowedLocalSchemaUrl, validate } from '@mintlify/common';
2
+ import * as previewing from '@mintlify/previewing';
3
+ import { mockProcessExit } from 'vitest-mock-process';
4
+
5
+ import { readLocalOpenApiFile } from '../src/helpers.js';
6
+ import { mockValidOpenApiDocument, runCommand } from './utils.js';
7
+
8
+ vi.mock('@mintlify/common', () => ({
9
+ getOpenApiDocumentFromUrl: vi.fn(),
10
+ isAllowedLocalSchemaUrl: vi.fn(),
11
+ validate: vi.fn(),
12
+ }));
13
+
14
+ vi.mock('../src/helpers.js', async () => {
15
+ const originalModule = await import('../src/helpers.js');
16
+ return {
17
+ ...originalModule,
18
+ readLocalOpenApiFile: vi.fn(),
19
+ };
20
+ });
21
+
22
+ const addLogSpy = vi.spyOn(previewing, 'addLog');
23
+ const processExitMock = mockProcessExit();
24
+
25
+ describe('openApiCheck', () => {
26
+ beforeEach(() => {
27
+ vi.clearAllMocks();
28
+ });
29
+
30
+ afterEach(() => {
31
+ vi.clearAllMocks();
32
+ });
33
+
34
+ it('valid openApi file from url', async () => {
35
+ vi.mocked(isAllowedLocalSchemaUrl).mockReturnValueOnce(true);
36
+ vi.mocked(getOpenApiDocumentFromUrl).mockResolvedValueOnce(mockValidOpenApiDocument);
37
+
38
+ await runCommand('openapi-check', 'https://petstore3.swagger.io/api/v3/openapi.json');
39
+
40
+ expect(addLogSpy).toHaveBeenCalledWith(
41
+ expect.objectContaining({
42
+ props: { message: 'OpenAPI definition is valid.' },
43
+ })
44
+ );
45
+ expect(processExitMock).toHaveBeenCalledWith(0);
46
+ });
47
+
48
+ it('invalid openApi file from url', async () => {
49
+ vi.mocked(isAllowedLocalSchemaUrl).mockReturnValueOnce(true);
50
+ vi.mocked(getOpenApiDocumentFromUrl).mockRejectedValueOnce(
51
+ new Error('Could not parse OpenAPI document.')
52
+ );
53
+
54
+ await runCommand('openapi-check', 'https://petstore3.swagger.io/api/v3/openapi.json');
55
+
56
+ expect(addLogSpy).toHaveBeenCalledWith(
57
+ expect.objectContaining({
58
+ props: { message: 'Could not parse OpenAPI document.' },
59
+ })
60
+ );
61
+ expect(processExitMock).toHaveBeenCalledWith(1);
62
+ });
63
+
64
+ it('valid openApi file from localhost', async () => {
65
+ vi.mocked(isAllowedLocalSchemaUrl).mockReturnValueOnce(true);
66
+ vi.mocked(getOpenApiDocumentFromUrl).mockResolvedValueOnce(mockValidOpenApiDocument);
67
+
68
+ await runCommand('openapi-check', 'http://localhost:3000/openapi.json');
69
+
70
+ expect(addLogSpy).toHaveBeenCalledWith(
71
+ expect.objectContaining({
72
+ props: { message: 'OpenAPI definition is valid.' },
73
+ })
74
+ );
75
+ expect(processExitMock).toHaveBeenCalledWith(0);
76
+ });
77
+
78
+ it('invalid openApi file from localhost', async () => {
79
+ vi.mocked(isAllowedLocalSchemaUrl).mockReturnValueOnce(false);
80
+ vi.mocked(readLocalOpenApiFile).mockResolvedValueOnce(undefined);
81
+
82
+ await runCommand('openapi-check', 'http://localhost:3000/openapi.json');
83
+
84
+ expect(addLogSpy).toHaveBeenCalledWith(
85
+ expect.objectContaining({
86
+ props: {
87
+ message:
88
+ 'failed to parse OpenAPI spec: could not parse file correctly, please check for any syntax errors.',
89
+ },
90
+ })
91
+ );
92
+ expect(processExitMock).toHaveBeenCalledWith(1);
93
+ });
94
+
95
+ it('valid openApi file from local file', async () => {
96
+ vi.mocked(isAllowedLocalSchemaUrl).mockReturnValueOnce(false);
97
+ vi.mocked(readLocalOpenApiFile).mockResolvedValueOnce(mockValidOpenApiDocument);
98
+ vi.mocked(validate).mockResolvedValueOnce({
99
+ valid: true,
100
+ errors: [],
101
+ });
102
+
103
+ await runCommand('openapi-check', 'test/openapi.yaml');
104
+
105
+ expect(addLogSpy).toHaveBeenCalledWith(
106
+ expect.objectContaining({
107
+ props: { message: 'OpenAPI definition is valid.' },
108
+ })
109
+ );
110
+ expect(processExitMock).toHaveBeenCalledWith(0);
111
+ });
112
+
113
+ it('invalid openApi file from local file', async () => {
114
+ vi.mocked(isAllowedLocalSchemaUrl).mockReturnValueOnce(false);
115
+ vi.mocked(readLocalOpenApiFile).mockResolvedValueOnce(mockValidOpenApiDocument);
116
+ vi.mocked(validate).mockRejectedValueOnce(new Error('some schema parsing error'));
117
+
118
+ await runCommand('openapi-check', 'test/openapi.yaml');
119
+
120
+ expect(addLogSpy).toHaveBeenCalledWith(
121
+ expect.objectContaining({
122
+ props: { message: 'some schema parsing error' },
123
+ })
124
+ );
125
+ expect(processExitMock).toHaveBeenCalledWith(1);
126
+ });
127
+ });
@@ -1,25 +1,25 @@
1
- import { getTargetMintVersion, downloadTargetMint } from '@mintlify/previewing';
2
- import { execSync } from 'node:child_process';
3
- import { mockProcessExit } from 'vitest-mock-process';
1
+ import * as previewing from '@mintlify/previewing';
4
2
 
5
- import { getLatestCliVersion, getVersions } from '../src/helpers.js';
3
+ import { getLatestCliVersion, getVersions, execAsync } from '../src/helpers.js';
6
4
  import { update } from '../src/update.js';
7
5
 
8
- vi.mock('node:child_process', () => ({
9
- execSync: vi.fn(),
10
- }));
11
-
12
- vi.mock('@mintlify/previewing', () => ({
13
- getClientVersion: vi.fn(),
14
- getTargetMintVersion: vi.fn(),
15
- downloadTargetMint: vi.fn(),
16
- }));
6
+ vi.mock('@mintlify/previewing', async () => {
7
+ const originalModule =
8
+ await vi.importActual<typeof import('@mintlify/previewing')>('@mintlify/previewing');
9
+ return {
10
+ ...originalModule,
11
+ getClientVersion: vi.fn(),
12
+ getTargetMintVersion: vi.fn(),
13
+ downloadTargetMint: vi.fn(),
14
+ };
15
+ });
17
16
 
18
- vi.mock('../src/helpers.js', async (importOriginal) => {
19
- const original = await importOriginal();
17
+ vi.mock('../src/helpers.js', async () => {
18
+ const originalModule =
19
+ await vi.importActual<typeof import('../src/helpers.js')>('../src/helpers.js');
20
20
  return {
21
- // @ts-expect-error - this is a mock
22
- ...original,
21
+ ...originalModule,
22
+ execAsync: vi.fn(),
23
23
  getLatestCliVersion: vi.fn(),
24
24
  getVersions: vi.fn().mockReturnValue({
25
25
  cli: '1.0.0',
@@ -28,7 +28,7 @@ vi.mock('../src/helpers.js', async (importOriginal) => {
28
28
  };
29
29
  });
30
30
 
31
- const processExitMock = mockProcessExit();
31
+ const addLogSpy = vi.spyOn(previewing, 'addLog');
32
32
 
33
33
  describe('update', () => {
34
34
  beforeEach(() => {
@@ -44,16 +44,50 @@ describe('update', () => {
44
44
  cli: '1.0.0',
45
45
  client: '1.0.0',
46
46
  });
47
- vi.mocked(getTargetMintVersion).mockResolvedValue('2.0.0');
47
+ vi.mocked(previewing.getTargetMintVersion).mockResolvedValue('2.0.0');
48
48
  vi.mocked(getLatestCliVersion).mockReturnValue('2.0.0');
49
- vi.mocked(execSync).mockReturnValue(Buffer.from(''));
50
- vi.mocked(downloadTargetMint).mockResolvedValue();
49
+ vi.mocked(execAsync).mockResolvedValue({ stdout: '', stderr: '' });
50
+ vi.mocked(previewing.downloadTargetMint).mockResolvedValue();
51
51
 
52
52
  await update({ packageName: 'mintlify' });
53
53
 
54
- expect(execSync).toHaveBeenCalledWith('npm install -g mintlify@latest --silent');
55
- expect(downloadTargetMint).toHaveBeenCalledWith({
56
- logger: expect.any(Object),
54
+ expect(addLogSpy).toHaveBeenCalledTimes(4);
55
+ expect(addLogSpy).toHaveBeenCalledWith(
56
+ expect.objectContaining({ props: { message: 'updating...' } })
57
+ );
58
+ expect(addLogSpy).toHaveBeenCalledWith(
59
+ expect.objectContaining({ props: { message: 'updating mintlify package...' } })
60
+ );
61
+ expect(addLogSpy).toHaveBeenCalledWith(
62
+ expect.objectContaining({ props: { message: 'updating mintlify client to 2.0.0...' } })
63
+ );
64
+ expect(addLogSpy).toHaveBeenCalledWith(
65
+ expect.objectContaining({
66
+ props: { message: 'updated mintlify to the latest version: 2.0.0' },
67
+ })
68
+ );
69
+ expect(execAsync).toHaveBeenCalledWith('npm install -g mintlify@latest --silent');
70
+ expect(previewing.downloadTargetMint).toHaveBeenCalledWith({
71
+ targetVersion: '2.0.0',
72
+ existingVersion: '1.0.0',
73
+ });
74
+ });
75
+
76
+ it('should update the cli and client successfully - silently', async () => {
77
+ vi.mocked(getVersions).mockReturnValue({
78
+ cli: '1.0.0',
79
+ client: '1.0.0',
80
+ });
81
+ vi.mocked(previewing.getTargetMintVersion).mockResolvedValue('2.0.0');
82
+ vi.mocked(getLatestCliVersion).mockReturnValue('2.0.0');
83
+ vi.mocked(execAsync).mockResolvedValue({ stdout: '', stderr: '' });
84
+ vi.mocked(previewing.downloadTargetMint).mockResolvedValue();
85
+
86
+ await update({ packageName: 'mintlify', silent: true });
87
+
88
+ expect(addLogSpy).not.toHaveBeenCalled();
89
+ expect(execAsync).toHaveBeenCalledWith('npm install -g mintlify@latest --silent');
90
+ expect(previewing.downloadTargetMint).toHaveBeenCalledWith({
57
91
  targetVersion: '2.0.0',
58
92
  existingVersion: '1.0.0',
59
93
  });
@@ -65,14 +99,38 @@ describe('update', () => {
65
99
  client: '1.0.0',
66
100
  });
67
101
  vi.mocked(getLatestCliVersion).mockReturnValue('1.0.0');
68
- vi.mocked(getTargetMintVersion).mockResolvedValue('1.0.0');
69
- vi.mocked(execSync).mockReturnValue(Buffer.from(''));
70
- vi.mocked(downloadTargetMint).mockResolvedValue();
102
+ vi.mocked(previewing.getTargetMintVersion).mockResolvedValue('1.0.0');
103
+ vi.mocked(execAsync).mockResolvedValue({ stdout: '', stderr: '' });
104
+ vi.mocked(previewing.downloadTargetMint).mockResolvedValue();
71
105
 
72
106
  await update({ packageName: 'mintlify' });
73
107
 
74
- expect(execSync).not.toHaveBeenCalled();
75
- expect(downloadTargetMint).not.toHaveBeenCalled();
108
+ expect(addLogSpy).toHaveBeenCalledTimes(2);
109
+ expect(addLogSpy).toHaveBeenCalledWith(
110
+ expect.objectContaining({ props: { message: 'updating...' } })
111
+ );
112
+ expect(addLogSpy).toHaveBeenCalledWith(
113
+ expect.objectContaining({ props: { message: 'already up to date' } })
114
+ );
115
+ expect(execAsync).not.toHaveBeenCalled();
116
+ expect(previewing.downloadTargetMint).not.toHaveBeenCalled();
117
+ });
118
+
119
+ it('should return when already up to date - silently', async () => {
120
+ vi.mocked(getVersions).mockReturnValue({
121
+ cli: '1.0.0',
122
+ client: '1.0.0',
123
+ });
124
+ vi.mocked(getLatestCliVersion).mockReturnValue('1.0.0');
125
+ vi.mocked(previewing.getTargetMintVersion).mockResolvedValue('1.0.0');
126
+ vi.mocked(execAsync).mockResolvedValue({ stdout: '', stderr: '' });
127
+ vi.mocked(previewing.downloadTargetMint).mockResolvedValue();
128
+
129
+ await update({ packageName: 'mintlify', silent: true });
130
+
131
+ expect(addLogSpy).not.toHaveBeenCalled();
132
+ expect(execAsync).not.toHaveBeenCalled();
133
+ expect(previewing.downloadTargetMint).not.toHaveBeenCalled();
76
134
  });
77
135
 
78
136
  it('should only update cli if client version is up to date', async () => {
@@ -81,13 +139,26 @@ describe('update', () => {
81
139
  client: '2.0.0',
82
140
  });
83
141
  vi.mocked(getLatestCliVersion).mockReturnValue('2.0.0');
84
- vi.mocked(getTargetMintVersion).mockResolvedValue('2.0.0');
85
- vi.mocked(execSync).mockReturnValue(Buffer.from(''));
142
+ vi.mocked(previewing.getTargetMintVersion).mockResolvedValue('2.0.0');
143
+ vi.mocked(execAsync).mockResolvedValue({ stdout: '', stderr: '' });
86
144
 
87
145
  await update({ packageName: 'mintlify' });
88
146
 
89
- expect(execSync).toHaveBeenCalledWith('npm install -g mintlify@latest --silent');
90
- expect(downloadTargetMint).not.toHaveBeenCalled();
147
+ expect(addLogSpy).toHaveBeenCalledTimes(3);
148
+ expect(addLogSpy).toHaveBeenCalledWith(
149
+ expect.objectContaining({ props: { message: 'updating...' } })
150
+ );
151
+ expect(addLogSpy).toHaveBeenCalledWith(
152
+ expect.objectContaining({ props: { message: 'updating mintlify package...' } })
153
+ );
154
+ expect(addLogSpy).toHaveBeenCalledWith(
155
+ expect.objectContaining({
156
+ props: { message: 'updated mintlify to the latest version: 2.0.0' },
157
+ })
158
+ );
159
+
160
+ expect(execAsync).toHaveBeenCalledWith('npm install -g mintlify@latest --silent');
161
+ expect(previewing.downloadTargetMint).not.toHaveBeenCalled();
91
162
  });
92
163
 
93
164
  it('should only update client if cli version is up to date', async () => {
@@ -96,14 +167,26 @@ describe('update', () => {
96
167
  client: '1.0.0',
97
168
  });
98
169
  vi.mocked(getLatestCliVersion).mockReturnValue('2.0.0');
99
- vi.mocked(getTargetMintVersion).mockResolvedValue('2.0.0');
100
- vi.mocked(execSync).mockReturnValue(Buffer.from(''));
170
+ vi.mocked(previewing.getTargetMintVersion).mockResolvedValue('2.0.0');
171
+ vi.mocked(execAsync).mockResolvedValue({ stdout: '', stderr: '' });
101
172
 
102
173
  await update({ packageName: 'mintlify' });
103
174
 
104
- expect(execSync).not.toHaveBeenCalled();
105
- expect(downloadTargetMint).toHaveBeenCalledWith({
106
- logger: expect.any(Object),
175
+ expect(addLogSpy).toHaveBeenCalledTimes(3);
176
+ expect(addLogSpy).toHaveBeenCalledWith(
177
+ expect.objectContaining({ props: { message: 'updating...' } })
178
+ );
179
+ expect(addLogSpy).toHaveBeenCalledWith(
180
+ expect.objectContaining({ props: { message: 'updating mintlify client to 2.0.0...' } })
181
+ );
182
+ expect(addLogSpy).toHaveBeenCalledWith(
183
+ expect.objectContaining({
184
+ props: { message: 'updated mintlify to the latest version: 2.0.0' },
185
+ })
186
+ );
187
+
188
+ expect(execAsync).not.toHaveBeenCalled();
189
+ expect(previewing.downloadTargetMint).toHaveBeenCalledWith({
107
190
  targetVersion: '2.0.0',
108
191
  existingVersion: '1.0.0',
109
192
  });
@@ -115,13 +198,21 @@ describe('update', () => {
115
198
  client: '1.0.0',
116
199
  });
117
200
  vi.mocked(getLatestCliVersion).mockReturnValue('2.0.0');
118
- vi.mocked(getTargetMintVersion).mockResolvedValue('2.0.0');
119
- vi.mocked(execSync).mockImplementation(() => {
120
- throw new Error('Update failed');
121
- });
201
+ vi.mocked(previewing.getTargetMintVersion).mockResolvedValue('2.0.0');
202
+ vi.mocked(execAsync).mockRejectedValue(new Error('Update failed'));
122
203
 
123
204
  await update({ packageName: 'mintlify' });
124
- expect(processExitMock).toHaveBeenCalledWith(1);
205
+
206
+ expect(addLogSpy).toHaveBeenCalledTimes(3);
207
+ expect(addLogSpy).toHaveBeenCalledWith(
208
+ expect.objectContaining({ props: { message: 'updating...' } })
209
+ );
210
+ expect(addLogSpy).toHaveBeenCalledWith(
211
+ expect.objectContaining({ props: { message: 'updating mintlify package...' } })
212
+ );
213
+ expect(addLogSpy).toHaveBeenCalledWith(
214
+ expect.objectContaining({ props: { message: 'failed to update mintlify@latest' } })
215
+ );
125
216
  });
126
217
 
127
218
  it('should handle client update failure', async () => {
@@ -130,11 +221,21 @@ describe('update', () => {
130
221
  client: '1.0.0',
131
222
  });
132
223
  vi.mocked(getLatestCliVersion).mockReturnValue('2.0.0');
133
- vi.mocked(getTargetMintVersion).mockResolvedValue('2.0.0');
134
- vi.mocked(execSync).mockReturnValue(Buffer.from(''));
135
- vi.mocked(downloadTargetMint).mockRejectedValue(new Error('Download failed'));
224
+ vi.mocked(previewing.getTargetMintVersion).mockResolvedValue('2.0.0');
225
+ vi.mocked(execAsync).mockResolvedValue({ stdout: '', stderr: '' });
226
+ vi.mocked(previewing.downloadTargetMint).mockRejectedValue(new Error('Download failed'));
136
227
 
137
228
  await update({ packageName: 'mintlify' });
138
- expect(processExitMock).toHaveBeenCalledWith(1);
229
+
230
+ expect(addLogSpy).toHaveBeenCalledTimes(4);
231
+ expect(addLogSpy).toHaveBeenCalledWith(
232
+ expect.objectContaining({ props: { message: 'updating...' } })
233
+ );
234
+ expect(addLogSpy).toHaveBeenCalledWith(
235
+ expect.objectContaining({ props: { message: 'updating mintlify client to 2.0.0...' } })
236
+ );
237
+ expect(addLogSpy).toHaveBeenCalledWith(
238
+ expect.objectContaining({ props: { message: 'failed to update mintlify client to 2.0.0' } })
239
+ );
139
240
  });
140
241
  });
package/__test__/utils.ts CHANGED
@@ -9,3 +9,12 @@ export async function runCommand(...args: string[]) {
9
9
  process.argv = ['node', 'cli.js', ...args];
10
10
  return cli();
11
11
  }
12
+
13
+ export const mockValidOpenApiDocument = {
14
+ openapi: '3.0.0',
15
+ info: {
16
+ title: 'Test API',
17
+ version: '1.0.0',
18
+ },
19
+ components: {},
20
+ };