@sanity/cli-test 0.3.3 → 0.3.4
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/dist/index.d.ts +29 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/test/createMockHttpServer.js +24 -0
- package/dist/test/createMockHttpServer.js.map +1 -0
- package/dist/test/createMockWatcher.js +26 -0
- package/dist/test/createMockWatcher.js.map +1 -0
- package/dist/test/testFixture.js +27 -2
- package/dist/test/testFixture.js.map +1 -1
- package/fixtures/basic-app/package.json +1 -1
- package/fixtures/basic-functions/package.json +1 -1
- package/fixtures/basic-studio/package.json +2 -2
- package/fixtures/graphql-studio/package.json +2 -2
- package/fixtures/multi-workspace-studio/package.json +2 -2
- package/fixtures/nextjs-app/package.json +1 -1
- package/fixtures/prebuilt-app/package.json +1 -1
- package/fixtures/prebuilt-studio/package.json +1 -1
- package/fixtures/worst-case-studio/package.json +2 -2
- package/package.json +6 -6
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { Config } from "@oclif/core";
|
|
|
6
6
|
import { Errors } from "@oclif/core";
|
|
7
7
|
import { Hook } from "@oclif/core/hooks";
|
|
8
8
|
import { Hooks } from "@oclif/core/hooks";
|
|
9
|
+
import { Mock } from "vitest";
|
|
9
10
|
import nock from "nock";
|
|
10
11
|
import { ProjectRootResult } from "@sanity/cli-core";
|
|
11
12
|
import { SanityClient } from "@sanity/client";
|
|
@@ -48,6 +49,25 @@ declare type CommandClass = (new (argv: string[], config: Config) => Command) &
|
|
|
48
49
|
*/
|
|
49
50
|
export declare function convertToSystemPath(pathStr: string): string;
|
|
50
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Creates a mock HTTP server for testing.
|
|
54
|
+
*
|
|
55
|
+
* @returns A mock HTTP server object with emit and once methods
|
|
56
|
+
* @internal
|
|
57
|
+
*/
|
|
58
|
+
export declare function createMockHttpServer(): {
|
|
59
|
+
emit(event: string, ...args: unknown[]): void;
|
|
60
|
+
once(event: string, listener: (...args: unknown[]) => void): void;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Creates a mock Vite watcher for testing.
|
|
65
|
+
*
|
|
66
|
+
* @returns A mock watcher object with add, emit, and on methods
|
|
67
|
+
* @internal
|
|
68
|
+
*/
|
|
69
|
+
export declare function createMockWatcher(): MockWatcher;
|
|
70
|
+
|
|
51
71
|
/**
|
|
52
72
|
* Creates a real Sanity client instance for testing that makes actual HTTP requests.
|
|
53
73
|
* Use with mockApi() to intercept and mock the HTTP calls.
|
|
@@ -324,6 +344,15 @@ export declare interface MockTelemetryOptions {
|
|
|
324
344
|
updateUserProperties?: () => void;
|
|
325
345
|
}
|
|
326
346
|
|
|
347
|
+
/**
|
|
348
|
+
* @internal
|
|
349
|
+
*/
|
|
350
|
+
export declare interface MockWatcher {
|
|
351
|
+
add: Mock;
|
|
352
|
+
emit: (event: string, ...args: unknown[]) => void;
|
|
353
|
+
on: (event: string, listener: (...args: unknown[]) => void) => void;
|
|
354
|
+
}
|
|
355
|
+
|
|
327
356
|
declare interface Options {
|
|
328
357
|
config: Config;
|
|
329
358
|
Command?: Command.Class;
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './test/constants.js'\nexport * from './test/createTestClient.js'\nexport * from './test/createTestToken.js'\nexport * from './test/mockApi.js'\nexport * from './test/mockSanityCommand.js'\nexport * from './test/mockTelemetry.js'\nexport * from './test/setupFixtures.js'\nexport * from './test/testCommand.js'\nexport * from './test/testFixture.js'\nexport * from './test/testHook.js'\nexport * from './utils/paths.js'\n"],"names":[],"mappings":"AAAA,cAAc,sBAAqB;AACnC,cAAc,6BAA4B;AAC1C,cAAc,4BAA2B;AACzC,cAAc,oBAAmB;AACjC,cAAc,8BAA6B;AAC3C,cAAc,0BAAyB;AACvC,cAAc,0BAAyB;AACvC,cAAc,wBAAuB;AACrC,cAAc,wBAAuB;AACrC,cAAc,qBAAoB;AAClC,cAAc,mBAAkB"}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './test/constants.js'\nexport * from './test/createMockHttpServer.js'\nexport * from './test/createMockWatcher.js'\nexport * from './test/createTestClient.js'\nexport * from './test/createTestToken.js'\nexport * from './test/mockApi.js'\nexport * from './test/mockSanityCommand.js'\nexport * from './test/mockTelemetry.js'\nexport * from './test/setupFixtures.js'\nexport * from './test/testCommand.js'\nexport * from './test/testFixture.js'\nexport * from './test/testHook.js'\nexport * from './utils/paths.js'\n"],"names":[],"mappings":"AAAA,cAAc,sBAAqB;AACnC,cAAc,iCAAgC;AAC9C,cAAc,8BAA6B;AAC3C,cAAc,6BAA4B;AAC1C,cAAc,4BAA2B;AACzC,cAAc,oBAAmB;AACjC,cAAc,8BAA6B;AAC3C,cAAc,0BAAyB;AACvC,cAAc,0BAAyB;AACvC,cAAc,wBAAuB;AACrC,cAAc,wBAAuB;AACrC,cAAc,qBAAoB;AAClC,cAAc,mBAAkB"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a mock HTTP server for testing.
|
|
3
|
+
*
|
|
4
|
+
* @returns A mock HTTP server object with emit and once methods
|
|
5
|
+
* @internal
|
|
6
|
+
*/ export function createMockHttpServer() {
|
|
7
|
+
const listeners = new Map();
|
|
8
|
+
return {
|
|
9
|
+
emit (event, ...args) {
|
|
10
|
+
const eventListeners = listeners.get(event) || [];
|
|
11
|
+
for (const listener of eventListeners){
|
|
12
|
+
listener(...args);
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
once (event, listener) {
|
|
16
|
+
if (!listeners.has(event)) {
|
|
17
|
+
listeners.set(event, []);
|
|
18
|
+
}
|
|
19
|
+
listeners.get(event).push(listener);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
//# sourceMappingURL=createMockHttpServer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/test/createMockHttpServer.ts"],"sourcesContent":["/**\n * Creates a mock HTTP server for testing.\n *\n * @returns A mock HTTP server object with emit and once methods\n * @internal\n */\nexport function createMockHttpServer() {\n const listeners = new Map<string, Array<(...args: unknown[]) => void>>()\n\n return {\n emit(event: string, ...args: unknown[]) {\n const eventListeners = listeners.get(event) || []\n for (const listener of eventListeners) {\n listener(...args)\n }\n },\n once(event: string, listener: (...args: unknown[]) => void) {\n if (!listeners.has(event)) {\n listeners.set(event, [])\n }\n listeners.get(event)!.push(listener)\n },\n }\n}\n"],"names":["createMockHttpServer","listeners","Map","emit","event","args","eventListeners","get","listener","once","has","set","push"],"mappings":"AAAA;;;;;CAKC,GACD,OAAO,SAASA;IACd,MAAMC,YAAY,IAAIC;IAEtB,OAAO;QACLC,MAAKC,KAAa,EAAE,GAAGC,IAAe;YACpC,MAAMC,iBAAiBL,UAAUM,GAAG,CAACH,UAAU,EAAE;YACjD,KAAK,MAAMI,YAAYF,eAAgB;gBACrCE,YAAYH;YACd;QACF;QACAI,MAAKL,KAAa,EAAEI,QAAsC;YACxD,IAAI,CAACP,UAAUS,GAAG,CAACN,QAAQ;gBACzBH,UAAUU,GAAG,CAACP,OAAO,EAAE;YACzB;YACAH,UAAUM,GAAG,CAACH,OAAQQ,IAAI,CAACJ;QAC7B;IACF;AACF"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { vi } from 'vitest';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a mock Vite watcher for testing.
|
|
4
|
+
*
|
|
5
|
+
* @returns A mock watcher object with add, emit, and on methods
|
|
6
|
+
* @internal
|
|
7
|
+
*/ export function createMockWatcher() {
|
|
8
|
+
const listeners = new Map();
|
|
9
|
+
return {
|
|
10
|
+
add: vi.fn(),
|
|
11
|
+
emit (event, ...args) {
|
|
12
|
+
const eventListeners = listeners.get(event) || [];
|
|
13
|
+
for (const listener of eventListeners){
|
|
14
|
+
listener(...args);
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
on (event, listener) {
|
|
18
|
+
if (!listeners.has(event)) {
|
|
19
|
+
listeners.set(event, []);
|
|
20
|
+
}
|
|
21
|
+
listeners.get(event).push(listener);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=createMockWatcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/test/createMockWatcher.ts"],"sourcesContent":["import {type Mock, vi} from 'vitest'\n\n/**\n * @internal\n */\nexport interface MockWatcher {\n add: Mock\n emit: (event: string, ...args: unknown[]) => void\n on: (event: string, listener: (...args: unknown[]) => void) => void\n}\n\n/**\n * Creates a mock Vite watcher for testing.\n *\n * @returns A mock watcher object with add, emit, and on methods\n * @internal\n */\nexport function createMockWatcher(): MockWatcher {\n const listeners = new Map<string, Array<(...args: unknown[]) => void>>()\n\n return {\n add: vi.fn(),\n emit(event: string, ...args: unknown[]) {\n const eventListeners = listeners.get(event) || []\n for (const listener of eventListeners) {\n listener(...args)\n }\n },\n on(event: string, listener: (...args: unknown[]) => void) {\n if (!listeners.has(event)) {\n listeners.set(event, [])\n }\n listeners.get(event)!.push(listener)\n },\n }\n}\n"],"names":["vi","createMockWatcher","listeners","Map","add","fn","emit","event","args","eventListeners","get","listener","on","has","set","push"],"mappings":"AAAA,SAAmBA,EAAE,QAAO,SAAQ;AAWpC;;;;;CAKC,GACD,OAAO,SAASC;IACd,MAAMC,YAAY,IAAIC;IAEtB,OAAO;QACLC,KAAKJ,GAAGK,EAAE;QACVC,MAAKC,KAAa,EAAE,GAAGC,IAAe;YACpC,MAAMC,iBAAiBP,UAAUQ,GAAG,CAACH,UAAU,EAAE;YACjD,KAAK,MAAMI,YAAYF,eAAgB;gBACrCE,YAAYH;YACd;QACF;QACAI,IAAGL,KAAa,EAAEI,QAAsC;YACtD,IAAI,CAACT,UAAUW,GAAG,CAACN,QAAQ;gBACzBL,UAAUY,GAAG,CAACP,OAAO,EAAE;YACzB;YACAL,UAAUQ,GAAG,CAACH,OAAQQ,IAAI,CAACJ;QAC7B;IACF;AACF"}
|
package/dist/test/testFixture.js
CHANGED
|
@@ -94,8 +94,33 @@ import { DEFAULT_FIXTURES } from './constants.js';
|
|
|
94
94
|
];
|
|
95
95
|
// Copy the fixture to the temp directory
|
|
96
96
|
await testCopyDirectory(tempFixturePath, tempPath, skipDirs);
|
|
97
|
-
//
|
|
98
|
-
|
|
97
|
+
// Mirror node_modules as per-entry symlinks excluding `.sanity` so each fixture
|
|
98
|
+
// gets its own Vite dep cache — sharing it across parallel tests races on
|
|
99
|
+
// dependency optimization.
|
|
100
|
+
const srcNodeModules = join(tempFixturePath, 'node_modules');
|
|
101
|
+
const destNodeModules = join(tempPath, 'node_modules');
|
|
102
|
+
let srcEntries;
|
|
103
|
+
try {
|
|
104
|
+
srcEntries = await readdir(srcNodeModules, {
|
|
105
|
+
withFileTypes: true
|
|
106
|
+
});
|
|
107
|
+
} catch (err) {
|
|
108
|
+
if (!(err instanceof Error && 'code' in err && err.code === 'ENOENT')) throw err;
|
|
109
|
+
}
|
|
110
|
+
if (srcEntries) {
|
|
111
|
+
await mkdir(destNodeModules, {
|
|
112
|
+
recursive: true
|
|
113
|
+
});
|
|
114
|
+
for (const entry of srcEntries){
|
|
115
|
+
if (entry.name === '.sanity') continue;
|
|
116
|
+
const srcPath = join(srcNodeModules, entry.name);
|
|
117
|
+
// Follow symlinks (e.g. pnpm's `node_modules/<pkg>` → `.pnpm/...`) so
|
|
118
|
+
// they get a 'dir' type on Windows — a file-typed symlink to a directory
|
|
119
|
+
// throws EPERM on stat.
|
|
120
|
+
const targetStats = entry.isSymbolicLink() ? await stat(srcPath) : entry;
|
|
121
|
+
await symlink(srcPath, join(destNodeModules, entry.name), targetStats.isDirectory() ? 'dir' : 'file');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
99
124
|
// Replace the package.json name with a temp name
|
|
100
125
|
const packageJsonPath = join(tempPath, 'package.json');
|
|
101
126
|
const packageJson = await readFile(packageJsonPath, 'utf8');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/test/testFixture.ts"],"sourcesContent":["import {randomBytes} from 'node:crypto'\nimport {copyFile, mkdir, readdir, readFile, stat, symlink, writeFile} from 'node:fs/promises'\nimport {tmpdir} from 'node:os'\nimport {join} from 'node:path'\n\nimport {getFixturesPath, getTempPath} from '../utils/paths.js'\nimport {DEFAULT_FIXTURES, type FixtureName} from './constants.js'\n\n/**\n * @deprecated Use {@link TestFixtureOptions} instead. This type alias will be removed in a future release.\n * @public\n */\nexport type TestExampleOptions = TestFixtureOptions\n\n/**\n * Recursively copy a directory, skipping specified folders.\n *\n * @param srcDir - Source directory to copy from\n * @param destDir - Destination directory to copy to\n * @param skip - Array of directory/file names to skip (e.g., ['node_modules', 'dist'])\n * @internal\n */\nexport async function testCopyDirectory(\n srcDir: string,\n destDir: string,\n skip: string[] = [],\n): Promise<void> {\n await mkdir(destDir, {recursive: true})\n\n const entries = await readdir(srcDir)\n\n for (const entry of entries) {\n if (skip.includes(entry)) {\n continue\n }\n\n const srcPath = join(srcDir, entry)\n const destPath = join(destDir, entry)\n\n const stats = await stat(srcPath)\n\n await (stats.isDirectory()\n ? testCopyDirectory(srcPath, destPath, skip)\n : copyFile(srcPath, destPath))\n }\n}\n\n/**\n * @public\n */\nexport interface TestFixtureOptions {\n /**\n * Custom temp directory. Defaults to process.cwd()/tmp (or the OS temp directory when\n * `useSystemTmp` is true).\n */\n tempDir?: string\n /**\n * Use the OS temp directory instead of cwd/tmp. Avoids monorepo workspace detection by\n * package managers and git when tests run inside a monorepo. Ignored when `tempDir` is set.\n */\n useSystemTmp?: boolean\n}\n\n/**\n * Clones a fixture directory into a temporary directory with an isolated copy.\n *\n * The function creates a unique temporary copy of the specified fixture with:\n * - A random unique ID to avoid conflicts between parallel tests\n * - Symlinked node_modules for performance (from the global setup version)\n * - Modified package.json name to prevent conflicts\n *\n * The fixture is first looked up in the temp directory (if global setup ran),\n * otherwise it falls back to the bundled fixtures in the package.\n *\n * @param fixtureName - The name of the fixture to clone (e.g., 'basic-app', 'basic-studio')\n * @param options - Configuration options\n * @returns The absolute path to the temporary directory containing the fixture\n *\n * @public\n *\n * @example\n * ```typescript\n * import {testFixture} from '@sanity/cli-test'\n * import {describe, test} from 'vitest'\n *\n * describe('my test suite', () => {\n * test('should work with basic-studio', async () => {\n * const cwd = await testFixture('basic-studio')\n * // ... run your tests in this directory\n * })\n * })\n * ```\n */\nexport async function testFixture(\n fixtureName: FixtureName | (string & {}),\n options: TestFixtureOptions = {},\n): Promise<string> {\n const {tempDir, useSystemTmp = false} = options\n const {includeDist = false} =\n fixtureName in DEFAULT_FIXTURES ? DEFAULT_FIXTURES[fixtureName as FixtureName] : {}\n\n // Source is always looked up in the default temp path (cwd/tmp) where global setup\n // pre-clones fixtures and installs their node_modules. The destination can be redirected\n // to system tmp to keep the working copy out of the monorepo (avoids pnpm treating it as\n // a workspace importer and mutating pnpm-lock.yaml).\n const sourceTempDir = getTempPath(tempDir)\n const destTempDir = tempDir\n ? sourceTempDir\n : useSystemTmp\n ? join(tmpdir(), 'sanity-cli-e2e')\n : sourceTempDir\n await mkdir(destTempDir, {recursive: true})\n\n // Fixtures are cloned in the source tmp directory by the setup function\n let tempFixturePath = join(sourceTempDir, `fixture-${fixtureName}`)\n\n try {\n const stats = await stat(tempFixturePath)\n if (!stats.isDirectory()) {\n throw new Error(`${tempFixturePath} is not a directory`)\n }\n } catch (err: unknown) {\n // If the cloned fixture doesn't exist, copy from the bundled fixtures\n if (err instanceof Error && 'code' in err && err.code === 'ENOENT') {\n tempFixturePath = join(getFixturesPath(), fixtureName)\n } else {\n throw err\n }\n }\n\n const tempId = randomBytes(8).toString('hex')\n const tempPath = join(destTempDir, `fixture-${fixtureName}-${tempId}`)\n\n // Always skip node_modules (will be symlinked), tmp and (unless specifically included) dist\n const skipDirs = ['node_modules', 'tmp', ...(includeDist ? [] : ['dist'])]\n\n // Copy the fixture to the temp directory\n await testCopyDirectory(tempFixturePath, tempPath, skipDirs)\n\n // Symlink the node_modules directory for performance\n await symlink(join(tempFixturePath, 'node_modules'), join(tempPath, 'node_modules'))\n\n // Replace the package.json name with a temp name\n const packageJsonPath = join(tempPath, 'package.json')\n const packageJson = await readFile(packageJsonPath, 'utf8')\n const packageJsonData = JSON.parse(packageJson)\n packageJsonData.name = `${packageJsonData.name}-${tempId}`\n await writeFile(packageJsonPath, JSON.stringify(packageJsonData, null, 2))\n\n return tempPath\n}\n\n/**\n * @deprecated Use {@link testFixture} instead. This function will be removed in a future release.\n *\n * Clones an example (now called fixture) directory into a temporary directory with an isolated copy.\n *\n * @param exampleName - The name of the example/fixture to clone (e.g., 'basic-app', 'basic-studio')\n * @param options - Configuration options\n * @returns The absolute path to the temporary directory containing the example/fixture\n *\n * @public\n */\nexport async function testExample(\n exampleName: FixtureName | (string & {}),\n options: TestFixtureOptions = {},\n): Promise<string> {\n return testFixture(exampleName, options)\n}\n"],"names":["randomBytes","copyFile","mkdir","readdir","readFile","stat","symlink","writeFile","tmpdir","join","getFixturesPath","getTempPath","DEFAULT_FIXTURES","testCopyDirectory","srcDir","destDir","skip","recursive","entries","entry","includes","srcPath","destPath","stats","isDirectory","testFixture","fixtureName","options","tempDir","useSystemTmp","includeDist","sourceTempDir","destTempDir","tempFixturePath","Error","err","code","tempId","toString","tempPath","skipDirs","packageJsonPath","packageJson","packageJsonData","JSON","parse","name","stringify","testExample","exampleName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/test/testFixture.ts"],"sourcesContent":["import {randomBytes} from 'node:crypto'\nimport {copyFile, mkdir, readdir, readFile, stat, symlink, writeFile} from 'node:fs/promises'\nimport {tmpdir} from 'node:os'\nimport {join} from 'node:path'\n\nimport {getFixturesPath, getTempPath} from '../utils/paths.js'\nimport {DEFAULT_FIXTURES, type FixtureName} from './constants.js'\n\n/**\n * @deprecated Use {@link TestFixtureOptions} instead. This type alias will be removed in a future release.\n * @public\n */\nexport type TestExampleOptions = TestFixtureOptions\n\n/**\n * Recursively copy a directory, skipping specified folders.\n *\n * @param srcDir - Source directory to copy from\n * @param destDir - Destination directory to copy to\n * @param skip - Array of directory/file names to skip (e.g., ['node_modules', 'dist'])\n * @internal\n */\nexport async function testCopyDirectory(\n srcDir: string,\n destDir: string,\n skip: string[] = [],\n): Promise<void> {\n await mkdir(destDir, {recursive: true})\n\n const entries = await readdir(srcDir)\n\n for (const entry of entries) {\n if (skip.includes(entry)) {\n continue\n }\n\n const srcPath = join(srcDir, entry)\n const destPath = join(destDir, entry)\n\n const stats = await stat(srcPath)\n\n await (stats.isDirectory()\n ? testCopyDirectory(srcPath, destPath, skip)\n : copyFile(srcPath, destPath))\n }\n}\n\n/**\n * @public\n */\nexport interface TestFixtureOptions {\n /**\n * Custom temp directory. Defaults to process.cwd()/tmp (or the OS temp directory when\n * `useSystemTmp` is true).\n */\n tempDir?: string\n /**\n * Use the OS temp directory instead of cwd/tmp. Avoids monorepo workspace detection by\n * package managers and git when tests run inside a monorepo. Ignored when `tempDir` is set.\n */\n useSystemTmp?: boolean\n}\n\n/**\n * Clones a fixture directory into a temporary directory with an isolated copy.\n *\n * The function creates a unique temporary copy of the specified fixture with:\n * - A random unique ID to avoid conflicts between parallel tests\n * - Symlinked node_modules for performance (from the global setup version)\n * - Modified package.json name to prevent conflicts\n *\n * The fixture is first looked up in the temp directory (if global setup ran),\n * otherwise it falls back to the bundled fixtures in the package.\n *\n * @param fixtureName - The name of the fixture to clone (e.g., 'basic-app', 'basic-studio')\n * @param options - Configuration options\n * @returns The absolute path to the temporary directory containing the fixture\n *\n * @public\n *\n * @example\n * ```typescript\n * import {testFixture} from '@sanity/cli-test'\n * import {describe, test} from 'vitest'\n *\n * describe('my test suite', () => {\n * test('should work with basic-studio', async () => {\n * const cwd = await testFixture('basic-studio')\n * // ... run your tests in this directory\n * })\n * })\n * ```\n */\nexport async function testFixture(\n fixtureName: FixtureName | (string & {}),\n options: TestFixtureOptions = {},\n): Promise<string> {\n const {tempDir, useSystemTmp = false} = options\n const {includeDist = false} =\n fixtureName in DEFAULT_FIXTURES ? DEFAULT_FIXTURES[fixtureName as FixtureName] : {}\n\n // Source is always looked up in the default temp path (cwd/tmp) where global setup\n // pre-clones fixtures and installs their node_modules. The destination can be redirected\n // to system tmp to keep the working copy out of the monorepo (avoids pnpm treating it as\n // a workspace importer and mutating pnpm-lock.yaml).\n const sourceTempDir = getTempPath(tempDir)\n const destTempDir = tempDir\n ? sourceTempDir\n : useSystemTmp\n ? join(tmpdir(), 'sanity-cli-e2e')\n : sourceTempDir\n await mkdir(destTempDir, {recursive: true})\n\n // Fixtures are cloned in the source tmp directory by the setup function\n let tempFixturePath = join(sourceTempDir, `fixture-${fixtureName}`)\n\n try {\n const stats = await stat(tempFixturePath)\n if (!stats.isDirectory()) {\n throw new Error(`${tempFixturePath} is not a directory`)\n }\n } catch (err: unknown) {\n // If the cloned fixture doesn't exist, copy from the bundled fixtures\n if (err instanceof Error && 'code' in err && err.code === 'ENOENT') {\n tempFixturePath = join(getFixturesPath(), fixtureName)\n } else {\n throw err\n }\n }\n\n const tempId = randomBytes(8).toString('hex')\n const tempPath = join(destTempDir, `fixture-${fixtureName}-${tempId}`)\n\n // Always skip node_modules (will be symlinked), tmp and (unless specifically included) dist\n const skipDirs = ['node_modules', 'tmp', ...(includeDist ? [] : ['dist'])]\n\n // Copy the fixture to the temp directory\n await testCopyDirectory(tempFixturePath, tempPath, skipDirs)\n\n // Mirror node_modules as per-entry symlinks excluding `.sanity` so each fixture\n // gets its own Vite dep cache — sharing it across parallel tests races on\n // dependency optimization.\n const srcNodeModules = join(tempFixturePath, 'node_modules')\n const destNodeModules = join(tempPath, 'node_modules')\n let srcEntries\n try {\n srcEntries = await readdir(srcNodeModules, {withFileTypes: true})\n } catch (err) {\n if (!(err instanceof Error && 'code' in err && err.code === 'ENOENT')) throw err\n }\n if (srcEntries) {\n await mkdir(destNodeModules, {recursive: true})\n for (const entry of srcEntries) {\n if (entry.name === '.sanity') continue\n const srcPath = join(srcNodeModules, entry.name)\n // Follow symlinks (e.g. pnpm's `node_modules/<pkg>` → `.pnpm/...`) so\n // they get a 'dir' type on Windows — a file-typed symlink to a directory\n // throws EPERM on stat.\n const targetStats = entry.isSymbolicLink() ? await stat(srcPath) : entry\n await symlink(\n srcPath,\n join(destNodeModules, entry.name),\n targetStats.isDirectory() ? 'dir' : 'file',\n )\n }\n }\n\n // Replace the package.json name with a temp name\n const packageJsonPath = join(tempPath, 'package.json')\n const packageJson = await readFile(packageJsonPath, 'utf8')\n const packageJsonData = JSON.parse(packageJson)\n packageJsonData.name = `${packageJsonData.name}-${tempId}`\n await writeFile(packageJsonPath, JSON.stringify(packageJsonData, null, 2))\n\n return tempPath\n}\n\n/**\n * @deprecated Use {@link testFixture} instead. This function will be removed in a future release.\n *\n * Clones an example (now called fixture) directory into a temporary directory with an isolated copy.\n *\n * @param exampleName - The name of the example/fixture to clone (e.g., 'basic-app', 'basic-studio')\n * @param options - Configuration options\n * @returns The absolute path to the temporary directory containing the example/fixture\n *\n * @public\n */\nexport async function testExample(\n exampleName: FixtureName | (string & {}),\n options: TestFixtureOptions = {},\n): Promise<string> {\n return testFixture(exampleName, options)\n}\n"],"names":["randomBytes","copyFile","mkdir","readdir","readFile","stat","symlink","writeFile","tmpdir","join","getFixturesPath","getTempPath","DEFAULT_FIXTURES","testCopyDirectory","srcDir","destDir","skip","recursive","entries","entry","includes","srcPath","destPath","stats","isDirectory","testFixture","fixtureName","options","tempDir","useSystemTmp","includeDist","sourceTempDir","destTempDir","tempFixturePath","Error","err","code","tempId","toString","tempPath","skipDirs","srcNodeModules","destNodeModules","srcEntries","withFileTypes","name","targetStats","isSymbolicLink","packageJsonPath","packageJson","packageJsonData","JSON","parse","stringify","testExample","exampleName"],"mappings":"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"}
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
"start": "sanity start"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@sanity/vision": "^5.
|
|
18
|
+
"@sanity/vision": "^5.26.0",
|
|
19
19
|
"react": "^19.2.5",
|
|
20
20
|
"react-dom": "^19.2.5",
|
|
21
|
-
"sanity": "^5.
|
|
21
|
+
"sanity": "^5.26.0",
|
|
22
22
|
"styled-components": "^6.4.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
"deploy-graphql": "sanity graphql deploy"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@sanity/vision": "^5.
|
|
15
|
+
"@sanity/vision": "^5.26.0",
|
|
16
16
|
"react": "^19.2.5",
|
|
17
17
|
"react-dom": "^19.2.5",
|
|
18
|
-
"sanity": "^5.
|
|
18
|
+
"sanity": "^5.26.0",
|
|
19
19
|
"styled-components": "^6.4.0"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
"start": "sanity start"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@sanity/vision": "^5.
|
|
18
|
+
"@sanity/vision": "^5.26.0",
|
|
19
19
|
"react": "^19.2.5",
|
|
20
20
|
"react-dom": "^19.2.5",
|
|
21
|
-
"sanity": "^5.
|
|
21
|
+
"sanity": "^5.26.0",
|
|
22
22
|
"styled-components": "^6.4.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@sanity/code-input": "^7.1.0",
|
|
19
|
-
"@sanity/vision": "^5.
|
|
19
|
+
"@sanity/vision": "^5.26.0",
|
|
20
20
|
"react": "^19.2.5",
|
|
21
21
|
"react-dom": "^19.2.5",
|
|
22
|
-
"sanity": "^5.
|
|
22
|
+
"sanity": "^5.26.0",
|
|
23
23
|
"sanity-plugin-media": "^4.1.1",
|
|
24
24
|
"styled-components": "^6.4.0",
|
|
25
25
|
"vite-tsconfig-paths": "^6.1.1"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/cli-test",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"description": "Sanity CLI test helpers and utilities",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -48,15 +48,16 @@
|
|
|
48
48
|
"esbuild": "^0.27.4",
|
|
49
49
|
"nock": "^14.0.14",
|
|
50
50
|
"ora": "^9.0.0",
|
|
51
|
-
"tinyglobby": "^0.2.16"
|
|
51
|
+
"tinyglobby": "^0.2.16",
|
|
52
|
+
"@sanity/cli-core": "1.3.3"
|
|
52
53
|
},
|
|
53
54
|
"devDependencies": {
|
|
54
55
|
"@eslint/compat": "^2.0.5",
|
|
55
|
-
"@oclif/core": "^4.
|
|
56
|
+
"@oclif/core": "^4.11.0",
|
|
56
57
|
"@sanity/client": "^7.22.0",
|
|
57
58
|
"@sanity/pkg-utils": "^10.4.18",
|
|
58
59
|
"@swc/cli": "^0.8.1",
|
|
59
|
-
"@types/node": "^20.19.
|
|
60
|
+
"@types/node": "^20.19.41",
|
|
60
61
|
"eslint": "^10.2.1",
|
|
61
62
|
"publint": "^0.3.18",
|
|
62
63
|
"rimraf": "^6.0.1",
|
|
@@ -66,14 +67,13 @@
|
|
|
66
67
|
"yaml": "^2.8.4",
|
|
67
68
|
"@repo/package.config": "0.0.1",
|
|
68
69
|
"@repo/tsconfig": "3.70.0",
|
|
69
|
-
"@sanity/cli-core": "1.3.2",
|
|
70
70
|
"@sanity/eslint-config-cli": "1.1.1"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
73
73
|
"@oclif/core": "^4.0.0",
|
|
74
74
|
"@sanity/client": "^7.0.0",
|
|
75
75
|
"vitest": ">=3.0.0 <5.0.0",
|
|
76
|
-
"@sanity/cli-core": "1.3.
|
|
76
|
+
"@sanity/cli-core": "1.3.3"
|
|
77
77
|
},
|
|
78
78
|
"engines": {
|
|
79
79
|
"node": ">=20.19.1 <22 || >=22.12"
|