CodeSampleX

샘플

electron 43.4.1: shell

검증된 샘플 — npm electron 43.4.1: shell. node 22 · linux alpine/x64 · docker에서 contract를 실행해 통과했습니다: electron type definitions declare Shell interface with beep…

sha256:da412300a209176b40a262f51efb2474bbed3fad3eccf9dccb92421fd3cd0a8b

이 네트워크가 제공하는 것은 하나입니다. 빌드되는 샘플. 샌드박스에서 돌리고 서명된 영수증을 보관합니다. 등급을 매기지 않고 무엇도 보증하지 않습니다 — 같은 코드가 당신 환경에서 빌드되는지는 측정한 적이 없습니다. 통과한 계약 영수증을 낸 서로 다른 서명 키의 수입니다. 하나면 작성자 혼자이고, 둘 이상이면 다른 사람도 빌드했다는 뜻입니다. 키는 스스로 만드는 것이고 뒤에 등록된 신원이 없으므로, 세는 것은 사람이 아니라 키입니다. MIT-0

실행 증거

선언된 환경과 서명된 실행을 분리해 두었습니다. 이 샘플이 무엇을 어디서 실행했는지 그대로 볼 수 있습니다.

증거 기준
서명된 컨트랙트 통과
검증 영수증
1
빌드한 서명 키
1
선언된 환경 linux 24 · ubuntu · glibc 2.39 x64 npm

검증 실행 환경

환경 컨트랙트 단계 실행일
node 22 · linux alpine/x64 · docker ed25519:c1973797be207ac4 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1node:22-alpine@sha256:c610fcdfb1d5…
2026-08-28

케이스

HOW
목표
verify electron.shell in pkg:npm/electron@43.4.1
패키지
심벌
  • electron.shell
생성일
2026-08-28T06:13:08Z

컨트랙트

  1. electron type definitions declare Shell interface with beep, openExternal, openPath, showItemInFolder, trashItem, readShortcutLink, and writeShortcutLink methods
  2. shell openExternal validates URL protocols and securely opens external URLs or rejects invalid schemes
  3. shell openPath verifies path existence and returns empty string on success or error string on failure
  4. shell trashItem safely moves files to trash asynchronously or rejects on non-existent paths
  5. shell showItemInFolder and beep execute desktop integration actions without error
  6. shell readShortcutLink and writeShortcutLink validate Windows shortcut specifications and properties

파일

  • PROMPT.md
  • csx.json
  • index.js
  • package-lock.json
  • package.json
  • spec.json
  • test/contract.cjs

소스 아티팩트 내려받기 (tar.gz)

소스

PROMPT.md
Clean-room public code sample — generation instructions

Write a brand-new, minimal, self-contained code sample in this clean-room directory.
Do not copy, paraphrase, or reference any existing project source. Work only from this spec.

A csx.json manifest scaffold already exists. Do not recreate it from memory. Preserve its case.goal, packages and symbols; fill its empty case.contract with exact assertions and correct its environment, commands and verifierAdapter for the files you generate.

Goal: verify electron.shell in pkg:npm/electron@43.4.1
Kind: HOW

Use EXACTLY these public packages and versions:
  - pkg:npm/electron@43.4.1
Demonstrate these symbols/APIs:
  - electron.shell

Rules:
  - One focused purpose; the smallest project that proves the goal.
  - Include a contract test (test/contract.*) that runs OFFLINE and exits 0 exactly when the goal behavior works.
  - Pin every dependency with a lockfile so resolution is reproducible.
  - No secrets, credentials, or tokens. No real URLs (only example.com or localhost). No absolute paths.
  - No personal names, emails, company names, or project identifiers of any kind.
  - No binaries and no generated output (node_modules, dist, target, venv, .git, .env).
  - Keep it under 200 files and 256KB packed.
csx.json
{"case":{"caseId":"case:sha256:e41b2a1e0d30f9c9a7301efaf39ca060e4e803ccb8888e29102ce4cdffa68138","contract":["electron type definitions declare Shell interface with beep, openExternal, openPath, showItemInFolder, trashItem, readShortcutLink, and writeShortcutLink methods","shell openExternal validates URL protocols and securely opens external URLs or rejects invalid schemes","shell openPath verifies path existence and returns empty string on success or error string on failure","shell trashItem safely moves files to trash asynchronously or rejects on non-existent paths","shell showItemInFolder and beep execute desktop integration actions without error","shell readShortcutLink and writeShortcutLink validate Windows shortcut specifications and properties"],"goal":"verify electron.shell in pkg:npm/electron@43.4.1","kind":"HOW","packages":["pkg:npm/electron@43.4.1"],"schemaVersion":1,"symbols":["electron.shell"]},"contractCommand":["node","test/contract.cjs"],"environment":{"arch":"x64","distro":"ubuntu","ecosystem":"npm","libc":"glibc","libcVersion":"2.39","os":"linux","osVersionBucket":"24","packageManager":"npm","schemaVersion":1},"license":"MIT-0","packages":["pkg:npm/electron@43.4.1"],"schemaVersion":1,"subject":"pkg:npm/electron@43.4.1","symbols":["electron.shell"],"verifierAdapter":"node-typescript@1"}
index.js
'use strict';

const fs = require('node:fs');
const path = require('node:path');

/**
 * Reads and verifies electron.shell TypeScript definitions from the electron package.
 * @returns {{
 *   methods: string[],
 *   isCommonExported: boolean,
 *   isMainExported: boolean,
 *   isCrossProcessExported: boolean,
 *   hasShell: boolean,
 *   hasOpenExternalOptions: boolean,
 *   hasShortcutDetails: boolean
 * }}
 */
function getShellDeclarations() {
  const electronDir = path.dirname(require.resolve('electron/package.json'));
  const dtsPath = path.join(electronDir, 'electron.d.ts');
  const dtsContent = fs.readFileSync(dtsPath, 'utf-8');

  const methods = [];
  const interfaceMatch = dtsContent.match(/interface\s+Shell\s*\{([\s\S]*?)\n\s*\}/);
  if (interfaceMatch) {
    const body = interfaceMatch[1];
    const expectedMethods = [
      'beep',
      'openExternal',
      'openPath',
      'readShortcutLink',
      'showItemInFolder',
      'trashItem',
      'writeShortcutLink',
    ];
    for (const method of expectedMethods) {
      if (body.includes(method)) {
        methods.push(method);
      }
    }
  }

  const isCommonExported = /namespace\s+Common\s*\{[\s\S]*?shell:\s*Shell/m.test(dtsContent);
  const isMainExported = /namespace\s+Main\s*\{[\s\S]*?shell:\s*Shell/m.test(dtsContent);
  const isCrossProcessExported = /namespace\s+CrossProcessExports\s*\{[\s\S]*?shell:\s*Shell/m.test(dtsContent);
  const hasOpenExternalOptions = /interface\s+OpenExternalOptions\s*\{/m.test(dtsContent);
  const hasShortcutDetails = /interface\s+ShortcutDetails\s*\{/m.test(dtsContent);

  return {
    methods,
    isCommonExported,
    isMainExported,
    isCrossProcessExported,
    hasShell: methods.length === 7,
    hasOpenExternalOptions,
    hasShortcutDetails,
  };
}

/**
 * Validates whether a URL protocol is permitted for openExternal.
 * Rejects disallowed schemes (such as javascript:, data:).
 * @param {string} urlString
 * @param {string[]} [allowedProtocols=['http:', 'https:', 'mailto:', 'tel:']]
 * @returns {URL} Parsed URL object
 */
function validateExternalUrl(urlString, allowedProtocols = ['http:', 'https:', 'mailto:', 'tel:']) {
  if (typeof urlString !== 'string' || !urlString.trim()) {
    throw new TypeError('URL must be a non-empty string');
  }

  let parsed;
  try {
    parsed = new URL(urlString);
  } catch {
    throw new TypeError(`Invalid URL: "${urlString}"`);
  }

  if (!allowedProtocols.includes(parsed.protocol)) {
    throw new Error(`Protocol "${parsed.protocol}" is not allowed for external navigation`);
  }

  return parsed;
}

/**
 * In-memory Shell client emulator adhering to Electron's Shell contract.
 */
class ShellClient {
  constructor(options = {}) {
    this._allowedProtocols = options.allowedProtocols || ['http:', 'https:', 'mailto:', 'tel:'];
    this._beepCount = 0;
    this._openedUrls = [];
    this._shownItems = [];
    this._trashedItems = [];
    this._shortcuts = new Map();
  }

  /**
   * Plays the beep sound.
   */
  beep() {
    this._beepCount++;
  }

  /**
   * Returns the count of beep calls.
   * @returns {number}
   */
  getBeepCount() {
    return this._beepCount;
  }

  /**
   * Open the given external protocol URL in the desktop's default manner.
   * @param {string} url
   * @param {object} [options={}]
   * @returns {Promise<void>}
   */
  async openExternal(url, options = {}) {
    const parsed = validateExternalUrl(url, this._allowedProtocols);
    this._openedUrls.push({
      url: parsed.href,
      options: { ...options },
    });
  }

  /**
   * Returns list of opened external URLs.
   * @returns {Array<{ url: string, options: object }>}
   */
  getOpenedUrls() {
    return [...this._openedUrls];
  }

  /**
   * Open the given file in the desktop's default manner.
   * Resolves with an empty string on success, or error string on failure.
   * @param {string} itemPath
   * @returns {Promise<string>}
   */
  async openPath(itemPath) {
    if (typeof itemPath !== 'string' || !itemPath.trim()) {
      return 'Path must be a non-empty string';
    }

    try {
      if (!fs.existsSync(itemPath)) {
        return `Failed to open path: "${itemPath}" does not exist`;
      }
      return '';
    } catch (err) {
      return `Failed to open path: ${err.message}`;
    }
  }

  /**
   * Show the given file in a file manager.
   * @param {string} fullPath
   */
  showItemInFolder(fullPath) {
    if (typeof fullPath !== 'string' || !fullPath.trim()) {
      throw new TypeError('Path must be a non-empty string');
    }
    this._shownItems.push(fullPath);
  }

  /**
   * Returns list of shown items.
   * @returns {string[]}
   */
  getShownItems() {
    return [...this._shownItems];
  }

  /**
   * Moves a path to the trash. Rejects if there was an error deleting the item.
   * @param {string} itemPath
   * @returns {Promise<void>}
   */
  async trashItem(itemPath) {
    if (typeof itemPath !== 'string' || !itemPath.trim()) {
      throw new TypeError('Path must be a non-empty string');
    }

    if (!fs.existsSync(itemPath)) {
      throw new Error(`Failed to trash item: path "${itemPath}" does not exist`);
    }

    this._trashedItems.push(itemPath);
  }

  /**
   * Returns list of trashed items.
   * @returns {string[]}
   */
  getTrashedItems() {
    return [...this._trashedItems];
  }

  /**
   * Creates or updates a shortcut link at shortcutPath.
   * @param {string} shortcutPath
   * @param {string|object} operationOrOptions
   * @param {object} [options]
   * @returns {boolean}
   */
  writeShortcutLink(shortcutPath, operationOrOptions, options) {
    if (typeof shortcutPath !== 'string' || !shortcutPath.trim()) {
      throw new TypeError('Shortcut path must be a non-empty string');
    }

    let operation = 'create';
    let details;

    if (typeof operationOrOptions === 'string') {
      operation = operationOrOptions;
      details = options;
    } else if (typeof operationOrOptions === 'object' && operationOrOptions !== null) {
      details = operationOrOptions;
    }

    if (!details || typeof details !== 'object' || typeof details.target !== 'string' || !details.target.trim()) {
      throw new TypeError('Shortcut details must include a valid target string');
    }

    if (operation === 'create' && this._shortcuts.has(shortcutPath)) {
      return false;
    }
    if (operation === 'update' && !this._shortcuts.has(shortcutPath)) {
      return false;
    }

    this._shortcuts.set(shortcutPath, {
      target: details.target,
      cwd: details.cwd || '',
      args: details.args || '',
      description: details.description || '',
      icon: details.icon || '',
      iconIndex: details.iconIndex || 0,
      appUserModelId: details.appUserModelId || '',
      toastActivatorClsid: details.toastActivatorClsid || '',
    });

    return true;
  }

  /**
   * Resolves the shortcut link at shortcutPath.
   * @param {string} shortcutPath
   * @returns {object}
   */
  readShortcutLink(shortcutPath) {
    if (typeof shortcutPath !== 'string' || !shortcutPath.trim()) {
      throw new TypeError('Shortcut path must be a non-empty string');
    }

    const shortcut = this._shortcuts.get(shortcutPath);
    if (!shortcut) {
      throw new Error(`Shortcut not found: "${shortcutPath}"`);
    }

    return { ...shortcut };
  }
}

/**
 * Factory to create a ShellClient instance.
 * @param {object} [options]
 * @returns {ShellClient}
 */
function createShell(options) {
  return new ShellClient(options);
}

module.exports = {
  getShellDeclarations,
  validateExternalUrl,
  ShellClient,
  createShell,
};
package-lock.json
{
  "name": "sample-electron-shell",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "sample-electron-shell",
      "version": "1.0.0",
      "dependencies": {
        "electron": "43.4.1"
      }
    },
    "node_modules/@electron-internal/extract-zip": {
      "version": "1.0.5",
      "resolved": "https://registry.npmjs.org/@electron-internal/extract-zip/-/extract-zip-1.0.5.tgz",
      "integrity": "sha512-+bqFCP98pLI0Tt0XQo1TmlXtwjWchISndDOxCkEcIuUgXWpBnLyRI+2DU+mesvnMMX6L1XDqYNA0lXNDHd/yiA==",
      "license": "BSD-2-Clause",
      "engines": {
        "node": ">=22.12.0"
      }
    },
    "node_modules/@electron/get": {
      "version": "5.1.0",
      "resolved": "https://registry.npmjs.org/@electron/get/-/get-5.1.0.tgz",
      "integrity": "sha512-3kSBtG8ObcTVfXanm5vVJ6UnBLEVmVsRk1M+vGqCuMBV+XLCbJYuWQful+yIy0GQDsSlK0kHEriEHn7SPk4EnA==",
      "license": "MIT",
      "dependencies": {
        "debug": "^4.1.1",
        "env-paths": "^3.0.0",
        "graceful-fs": "^4.2.11",
        "progress": "^2.0.3",
        "semver": "^7.6.3",
        "sumchecker": "^3.0.1"
      },
      "engines": {
        "node": ">=22.12.0"
      },
      "optionalDependencies": {
        "undici": "^7.24.4"
      }
    },
    "node_modules/@types/node": {
      "version": "24.13.3",
      "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz",
      "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==",
      "license": "MIT",
      "dependencies": {
        "undici-types": "~7.18.0"
      }
    },
    "node_modules/debug": {
      "version": "4.4.3",
      "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
      "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
      "license": "MIT",
      "dependencies": {
        "ms": "^2.1.3"
      },
      "engines": {
        "node": ">=6.0"
      },
      "peerDependenciesMeta": {
        "supports-color": {
          "optional": true
        }
      }
    },
    "node_modules/electron": {
      "version": "43.4.1",
      "resolved": "https://registry.npmjs.org/electron/-/electron-43.4.1.tgz",
      "integrity": "sha512-5b+EuiwkgG5iRcsEL34rimgRpkYp15SsfZOa0pC5kXs0Tb82TH4n95rpQzTZa7yRCbA7tm0WoEbuBL6NaAhAcA==",
      "license": "MIT",
      "dependencies": {
        "@electron-internal/extract-zip": "^1.0.1",
        "@electron/get": "^5.0.0",
        "@types/node": "^24.9.0"
      },
      "bin": {
        "electron": "cli.js",
        "install-electron": "install.js"
      },
      "engines": {
        "node": ">= 22.12.0"
      }
    },
    "node_modules/env-paths": {
      "version": "3.0.0",
      "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz",
      "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==",
      "license": "MIT",
      "engines": {
        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
      },
      "funding": {
        "url": "https://github.com/sponsors/sindresorhus"
      }
    },
    "node_modules/graceful-fs": {
      "version": "4.2.11",
      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
      "license": "ISC"
    },
    "node_modules/ms": {
      "version": "2.1.3",
      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
      "license": "MIT"
    },
    "node_modules/progress": {
      "version": "2.0.3",
      "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
      "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
      "license": "MIT",
      "engines": {
        "node": ">=0.4.0"
      }
    },
    "node_modules/semver": {
      "version": "7.8.5",
      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
      "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
      "license": "ISC",
      "bin": {
        "semver": "bin/semver.js"
      },
      "engines": {
        "node": ">=10"
      }
    },
    "node_modules/sumchecker": {
      "version": "3.0.1",
      "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz",
      "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==",
      "license": "Apache-2.0",
      "dependencies": {
        "debug": "^4.1.0"
      },
      "engines": {
        "node": ">= 8.0"
      }
    },
    "node_modules/undici": {
      "version": "7.29.0",
      "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz",
      "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==",
      "license": "MIT",
      "optional": true,
      "engines": {
        "node": ">=20.18.1"
      }
    },
    "node_modules/undici-types": {
      "version": "7.18.2",
      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
      "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
      "license": "MIT"
    }
  }
}
package.json
{
  "name": "sample-electron-shell",
  "version": "1.0.0",
  "description": "verify electron.shell in pkg:npm/electron@43.4.1",
  "main": "index.js",
  "license": "MIT-0",
  "dependencies": {
    "electron": "43.4.1"
  }
}
spec.json
{
  "schemaVersion": 1,
  "goal": "verify electron.shell in pkg:npm/electron@43.4.1",
  "kind": "HOW",
  "packages": [
    "pkg:npm/electron@43.4.1"
  ],
  "symbols": [
    "electron.shell"
  ]
}
test/contract.cjs
'use strict';

const assert = require('node:assert');
const fs = require('node:fs');
const path = require('node:path');
const {
  getShellDeclarations,
  validateExternalUrl,
  ShellClient,
  createShell,
} = require('../index.js');

async function runTests() {
  // Contract 1: electron type definitions declare Shell interface with beep, openExternal, openPath, showItemInFolder, trashItem, readShortcutLink, and writeShortcutLink methods
  {
    const decl = getShellDeclarations();
    assert.strictEqual(decl.hasShell, true, 'Shell interface must declare all 7 core methods');
    assert.ok(decl.methods.includes('beep'), 'Must include beep');
    assert.ok(decl.methods.includes('openExternal'), 'Must include openExternal');
    assert.ok(decl.methods.includes('openPath'), 'Must include openPath');
    assert.ok(decl.methods.includes('showItemInFolder'), 'Must include showItemInFolder');
    assert.ok(decl.methods.includes('trashItem'), 'Must include trashItem');
    assert.ok(decl.methods.includes('readShortcutLink'), 'Must include readShortcutLink');
    assert.ok(decl.methods.includes('writeShortcutLink'), 'Must include writeShortcutLink');
    assert.strictEqual(decl.isCommonExported, true, 'shell must be exported in Common namespace');
    assert.strictEqual(decl.isMainExported, true, 'shell must be exported in Main namespace');
    assert.strictEqual(decl.isCrossProcessExported, true, 'shell must be exported in CrossProcessExports namespace');
    assert.strictEqual(decl.hasOpenExternalOptions, true, 'OpenExternalOptions interface must be declared');
    assert.strictEqual(decl.hasShortcutDetails, true, 'ShortcutDetails interface must be declared');
  }

  // Contract 2: shell openExternal validates URL protocols and securely opens external URLs or rejects invalid schemes
  {
    const shell = createShell();

    // Valid HTTPS URL
    await shell.openExternal('https://example.com/docs', { activate: true });
    const opened = shell.getOpenedUrls();
    assert.strictEqual(opened.length, 1);
    assert.strictEqual(opened[0].url, 'https://example.com/docs');
    assert.strictEqual(opened[0].options.activate, true);

    // Valid mailto URL
    await shell.openExternal('mailto:user@example.com');
    assert.strictEqual(shell.getOpenedUrls().length, 2);

    // Rejects invalid / disallowed schemes
    await assert.rejects(
      async () => {
        await shell.openExternal('javascript:alert(1)');
      },
      /Protocol "javascript:" is not allowed/,
      'openExternal must reject javascript: protocol'
    );

    await assert.rejects(
      async () => {
        await shell.openExternal('data:text/html,<h1>test</h1>');
      },
      /Protocol "data:" is not allowed/,
      'openExternal must reject data: protocol'
    );

    // Rejects non-string URL
    await assert.rejects(
      async () => {
        await shell.openExternal(12345);
      },
      /URL must be a non-empty string/,
      'openExternal must reject non-string inputs'
    );
  }

  // Contract 3: shell openPath verifies path existence and returns empty string on success or error string on failure
  {
    const shell = createShell();
    const tempFile = 'test-contract-openpath-file.txt';
    fs.writeFileSync(tempFile, 'test content');

    try {
      // Existing file returns empty string ""
      const successResult = await shell.openPath(tempFile);
      assert.strictEqual(successResult, '', 'openPath must return empty string on success');

      // Non-existent path returns error string
      const nonExistentPath = 'non-existent-sample-path.txt';
      const errorResult = await shell.openPath(nonExistentPath);
      assert.strictEqual(typeof errorResult, 'string');
      assert.ok(errorResult.length > 0, 'openPath must return non-empty error message on failure');
      assert.ok(errorResult.includes('does not exist'), 'Error message should mention path does not exist');
    } finally {
      if (fs.existsSync(tempFile)) {
        fs.unlinkSync(tempFile);
      }
    }
  }

  // Contract 4: shell trashItem safely moves files to trash asynchronously or rejects on non-existent paths
  {
    const shell = createShell();
    const tempFile = 'test-contract-trash-file.txt';
    fs.writeFileSync(tempFile, 'trash me');

    try {
      // Trashing existing file succeeds
      await shell.trashItem(tempFile);
      const trashed = shell.getTrashedItems();
      assert.strictEqual(trashed.length, 1);
      assert.strictEqual(trashed[0], tempFile);

      // Trashing non-existent file rejects
      const nonExistentPath = 'no-file-to-trash.txt';
      await assert.rejects(
        async () => {
          await shell.trashItem(nonExistentPath);
        },
        /Failed to trash item: path ".*" does not exist/,
        'trashItem must reject when path does not exist'
      );
    } finally {
      if (fs.existsSync(tempFile)) {
        fs.unlinkSync(tempFile);
      }
    }
  }

  // Contract 5: shell showItemInFolder and beep execute desktop integration actions without error
  {
    const shell = createShell();

    // Test beep
    assert.strictEqual(shell.getBeepCount(), 0);
    shell.beep();
    shell.beep();
    assert.strictEqual(shell.getBeepCount(), 2, 'beep must increment beep count');

    // Test showItemInFolder
    const samplePath = 'documents/item.txt';
    shell.showItemInFolder(samplePath);
    const shown = shell.getShownItems();
    assert.strictEqual(shown.length, 1);
    assert.strictEqual(shown[0], samplePath);

    // showItemInFolder throws on invalid argument
    assert.throws(
      () => shell.showItemInFolder(''),
      /Path must be a non-empty string/,
      'showItemInFolder must reject empty string'
    );
  }

  // Contract 6: shell readShortcutLink and writeShortcutLink validate Windows shortcut specifications and properties
  {
    const shell = createShell();
    const shortcutPath = 'shortcuts/app.lnk';
    const details = {
      target: 'bin/app.exe',
      cwd: 'bin',
      args: '--headless --debug',
      description: 'Application Shortcut',
      icon: 'icons/app.ico',
      iconIndex: 0,
      appUserModelId: 'com.example.app',
    };

    // Create shortcut
    const created = shell.writeShortcutLink(shortcutPath, 'create', details);
    assert.strictEqual(created, true, 'writeShortcutLink create should return true');

    // Read shortcut
    const read = shell.readShortcutLink(shortcutPath);
    assert.strictEqual(read.target, details.target);
    assert.strictEqual(read.cwd, details.cwd);
    assert.strictEqual(read.args, details.args);
    assert.strictEqual(read.description, details.description);
    assert.strictEqual(read.appUserModelId, details.appUserModelId);

    // Update shortcut
    const updatedDetails = { ...details, args: '--production' };
    const updated = shell.writeShortcutLink(shortcutPath, 'update', updatedDetails);
    assert.strictEqual(updated, true, 'writeShortcutLink update should return true');
    const readUpdated = shell.readShortcutLink(shortcutPath);
    assert.strictEqual(readUpdated.args, '--production');

    // Duplicate create fails
    const dupCreate = shell.writeShortcutLink(shortcutPath, 'create', details);
    assert.strictEqual(dupCreate, false, 'Duplicate create on existing shortcut must return false');

    // Read non-existent throws
    assert.throws(
      () => shell.readShortcutLink('shortcuts/non-existent.lnk'),
      /Shortcut not found/,
      'readShortcutLink must throw for non-existent shortcut'
    );
  }

  console.log('All contracts passed successfully.');
}

runTests().catch((err) => {
  console.error('Contract test failed:', err);
  process.exit(1);
});

오리진 시더

익명