Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions .mocharc.js

This file was deleted.

10 changes: 2 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@
"format": "prettier -w ./lib ./test",
"format:check": "prettier --check ./lib ./test",
"prepare": "npm run build",
"test": "mocha --exit --timeout 1m \"./test/unit/**/*-specs.ts\"",
"e2e-test": "mocha --exit --timeout 5m \"./test/e2e/**/*-specs.ts\""
"test": "node --test --test-timeout=60000 \"./build/test/unit/**/*.spec.js\"",
"e2e-test": "node --test --test-force-exit --test-concurrency=1 --test-timeout=300000 \"./build/test/e2e/**/*.spec.js\""
},
"prettier": {
"bracketSpacing": false,
Expand All @@ -61,16 +61,10 @@
"@appium/types": "^1.0.0-rc.1",
"@semantic-release/changelog": "^6.0.1",
"@semantic-release/git": "^10.0.1",
"@types/chai": "^5.2.3",
"@types/mocha": "^10.0.1",
"@types/node": "^25.0.3",
"chai": "^6.0.0",
"chai-as-promised": "^8.0.0",
"conventional-changelog-conventionalcommits": "^9.0.0",
"mocha": "^11.0.1",
"prettier": "^3.0.0",
"semantic-release": "^25.0.2",
"ts-node": "^10.9.1",
"typescript": "^6.0.2"
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import {expect} from 'chai';
import assert from 'node:assert/strict';
import {Devicectl} from '../../lib/devicectl';
import {describe, it, type TestContext} from 'node:test';

describe('manual sudo execution e2e', function () {
it('runs devicectl as original non-root user under sudo', async function () {
it('runs devicectl as original non-root user under sudo', async function (ctx: TestContext) {
if (
process.env.CI ||
process.platform !== 'darwin' ||
Expand All @@ -11,8 +12,7 @@ describe('manual sudo execution e2e', function () {
!process.env.SUDO_UID ||
!process.env.SUDO_GID
) {
this.skip();
return;
return ctx.skip();
}

const devicectl = new Devicectl('');
Expand All @@ -21,7 +21,7 @@ describe('manual sudo execution e2e', function () {
asJson: true,
});

expect(result.stdout).to.be.a('string');
expect(result.stdout.length).to.be.greaterThan(0);
assert.strictEqual(typeof result.stdout, 'string');
assert.ok(result.stdout.length > 0);
});
});
53 changes: 29 additions & 24 deletions test/unit/devicectl-specs.ts → test/unit/devicectl.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {expect} from 'chai';
import assert from 'node:assert/strict';
import {Devicectl} from '../../lib/devicectl';
import {appUrlToFilesystemPath, escapeProcessFilterValue} from '../../lib/mixins/process';
import {describe, it, beforeEach} from 'node:test';

describe('Devicectl', function () {
let devicectl: Devicectl;
Expand All @@ -11,120 +12,124 @@ describe('Devicectl', function () {

describe('constructor', function () {
it('should create a Devicectl instance with the provided UDID and default logger', function () {
expect(devicectl.udid).to.equal('test-device-udid');
assert.strictEqual(devicectl.udid, 'test-device-udid');
});

it('should allow disabling non-root sudo execution in constructor options', function () {
const localDevicectl = new Devicectl('test-device-udid', {preferNonRootWhenSudo: false});
expect((localDevicectl as any).preferNonRootWhenSudo).to.equal(false);
assert.strictEqual((localDevicectl as any).preferNonRootWhenSudo, false);
});
});

describe('sudo behavior', function () {
it('should cache sudo user identity when available', function () {
const localDevicectl = new Devicectl('test-device-udid');
expect(
assert.strictEqual(
(localDevicectl as any).sudoUser === null || !!(localDevicectl as any).sudoUser,
).to.equal(true);
true,
);
});

it('should keep constructor default for runAsNonRootWhenSudo behavior', function () {
expect((devicectl as any).preferNonRootWhenSudo).to.equal(true);
assert.strictEqual((devicectl as any).preferNonRootWhenSudo, true);
});
});

describe('execute', function () {
it('should throw an error when command execution fails', async function () {
// This test would need to be mocked in a real implementation
// For now, we'll just test that the method exists
expect(devicectl.execute).to.be.a('function');
assert.strictEqual(typeof devicectl.execute, 'function');
});
});

describe('sendMemoryWarning', function () {
it('should be a function', function () {
expect(devicectl.sendMemoryWarning).to.be.a('function');
assert.strictEqual(typeof devicectl.sendMemoryWarning, 'function');
});
});

describe('listProcesses', function () {
it('should be a function', function () {
expect(devicectl.listProcesses).to.be.a('function');
assert.strictEqual(typeof devicectl.listProcesses, 'function');
});
});

describe('listFiles', function () {
it('should be a function', function () {
expect(devicectl.listFiles).to.be.a('function');
assert.strictEqual(typeof devicectl.listFiles, 'function');
});
});

describe('pullFile', function () {
it('should be a function', function () {
expect(devicectl.pullFile).to.be.a('function');
assert.strictEqual(typeof devicectl.pullFile, 'function');
});
});

describe('sendSignalToProcess', function () {
it('should be a function', function () {
expect(devicectl.sendSignalToProcess).to.be.a('function');
assert.strictEqual(typeof devicectl.sendSignalToProcess, 'function');
});
});

describe('listApps', function () {
it('should be a function', function () {
expect(devicectl.listApps).to.be.a('function');
assert.strictEqual(typeof devicectl.listApps, 'function');
});
});

describe('launchApp', function () {
it('should be a function', function () {
expect(devicectl.launchApp).to.be.a('function');
assert.strictEqual(typeof devicectl.launchApp, 'function');
});
});

describe('terminateApp', function () {
it('should be a function', function () {
expect(devicectl.terminateApp).to.be.a('function');
assert.strictEqual(typeof devicectl.terminateApp, 'function');
});

describe('appUrlToFilesystemPath', function () {
it('should strip the file:// prefix', function () {
expect(appUrlToFilesystemPath('file:///path/to/App.app')).to.equal('/path/to/App.app');
assert.strictEqual(appUrlToFilesystemPath('file:///path/to/App.app'), '/path/to/App.app');
});

it('should strip a trailing slash', function () {
expect(appUrlToFilesystemPath('/path/to/App.app/')).to.equal('/path/to/App.app');
assert.strictEqual(appUrlToFilesystemPath('/path/to/App.app/'), '/path/to/App.app');
});

it('should strip both the file:// prefix and trailing slash', function () {
expect(appUrlToFilesystemPath('file:///private/var/App.app/')).to.equal(
assert.strictEqual(
appUrlToFilesystemPath('file:///private/var/App.app/'),
'/private/var/App.app',
);
});

it('should leave paths without a file:// prefix or trailing slash unchanged', function () {
expect(appUrlToFilesystemPath('/path/to/App.app')).to.equal('/path/to/App.app');
assert.strictEqual(appUrlToFilesystemPath('/path/to/App.app'), '/path/to/App.app');
});
});

describe('escapeProcessFilterValue', function () {
it('should leave values without special characters unchanged', function () {
expect(escapeProcessFilterValue('/path/to/App.app')).to.equal('/path/to/App.app');
assert.strictEqual(escapeProcessFilterValue('/path/to/App.app'), '/path/to/App.app');
});

it('should escape backslashes', function () {
expect(escapeProcessFilterValue(String.raw`path\with\slashes`)).to.equal(
assert.strictEqual(
escapeProcessFilterValue(String.raw`path\with\slashes`),
String.raw`path\\with\\slashes`,
);
});

it('should escape double quotes', function () {
expect(escapeProcessFilterValue('path"with"quotes')).to.equal('path\\"with\\"quotes');
assert.strictEqual(escapeProcessFilterValue('path"with"quotes'), 'path\\"with\\"quotes');
});

it('should escape backslashes and double quotes together', function () {
expect(escapeProcessFilterValue(String.raw`path\"mixed`)).to.equal(
assert.strictEqual(
escapeProcessFilterValue(String.raw`path\"mixed`),
String.raw`path\\\"mixed`,
);
});
Expand All @@ -133,7 +138,7 @@ describe('Devicectl', function () {

describe('listDevices', function () {
it('should be a function', function () {
expect(devicectl.listDevices).to.be.a('function');
assert.strictEqual(typeof devicectl.listDevices, 'function');
});
});
});
8 changes: 1 addition & 7 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,8 @@
"outDir": "build",
"checkJs": true,
"esModuleInterop": true,
"types": ["node", "mocha"],
"types": ["node"],
"strict": true
},
"ts-node": {
"transpileOnly": true,
"compilerOptions": {
"rootDir": "."
}
},
"include": ["lib", "test"]
}
Loading