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
4 changes: 0 additions & 4 deletions .mocharc.js

This file was deleted.

7 changes: 2 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,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 @@ -64,7 +64,6 @@
"@semantic-release/git": "^10.0.1",
"@types/chai": "^5.2.3",
"@types/chai-as-promised": "^8.0.2",
"@types/mocha": "^10.0.1",
"@types/node": "^26.0.0",
"@types/semver": "^7.7.1",
"@types/sinon": "^22.0.0",
Expand All @@ -73,11 +72,9 @@
"chai": "^6.0.0",
"chai-as-promised": "^8.0.0",
"conventional-changelog-conventionalcommits": "^9.3.1",
"mocha": "^11.0.1",
"prettier": "^3.0.0",
"semantic-release": "^25.0.2",
"sinon": "^22.0.0",
"ts-node": "^10.9.1",
"typescript": "^6.0.2"
},
"types": "./build/lib/index.d.ts"
Expand Down
55 changes: 5 additions & 50 deletions test/e2e/simctl-e2e-specs.ts → test/e2e/simctl-e2e.spec.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
import {Simctl} from '../../lib/simctl';
import xcode from 'appium-xcode';
import {retryInterval} from 'asyncbox';
import {rimraf} from 'rimraf';
import {randomUUID} from 'node:crypto';
import path from 'node:path';
import os from 'node:os';
import fs from 'node:fs/promises';
import {expect, use} from 'chai';
import chaiAsPromised from 'chai-as-promised';
import {describe, it, beforeEach, afterEach, after, before} from 'node:test';
import {retryInterval} from 'asyncbox';

use(chaiAsPromised);
const BOOT_TIMEOUT_MS = 200000;

describe('simctl', function () {
const DEVICE_NAME = process.env.DEVICE_NAME || 'iPhone 17';
const MOCHA_TIMEOUT = 200000;
this.timeout(MOCHA_TIMEOUT);

let randName: string;
let validSdks: string[] = [];
let sdk: string;
Expand Down Expand Up @@ -132,27 +130,12 @@ describe('simctl', function () {
});

describe('on running Simulator', function () {
if (process.env.TRAVIS) {
this.retries(3);
}

let major: number, minor: number;

before(async function () {
const version = await xcode.getVersion(true);
if (typeof version === 'string') {
return this.skip();
}
({major, minor} = version);
if (major < 8 || (major === 8 && minor < 1)) {
return this.skip();
}

const sdk = process.env.IOS_SDK || validSdks.at(-1);
simctl.udid = await simctl.createDevice('runningSimTest', DEVICE_NAME, sdk!);

await simctl.bootDevice();
await simctl.startBootMonitor({timeout: MOCHA_TIMEOUT});
await simctl.startBootMonitor({timeout: BOOT_TIMEOUT_MS});
});
after(async function () {
if (simctl.udid) {
Expand All @@ -166,15 +149,9 @@ describe('simctl', function () {

describe('startBootMonitor', function () {
it('should be fulfilled if the simulator is already booted', async function () {
if (major < 8 || (major === 8 && minor < 1)) {
return this.skip();
}
await expect(simctl.startBootMonitor()).to.eventually.be.fulfilled;
});
it('should fail to monitor booting of non-existing simulator', async function () {
if (major < 8 || (major === 8 && minor < 1)) {
return this.skip();
}
const udid = simctl.udid;
try {
simctl.udid = 'blabla';
Expand All @@ -186,25 +163,11 @@ describe('simctl', function () {
});

describe('pasteboard', function () {
let pbRetries = 0;
before(function () {
if (major < 8 || (major === 8 && minor < 1)) {
return this.skip();
}
if (major === 9) {
if (process.env.TRAVIS) {
return this.skip();
}
// TODO: recheck when full Xcode 9 comes out to see if pasteboard works better
pbRetries = 200;
this.timeout(200 * 1000 * 2);
}
});
it('should set and get the content of the pasteboard', async function () {
const pbContent = 'blablabla';
const encoding = 'ascii';

await retryInterval(pbRetries, 1000, async () => {
await retryInterval(10, 1000, async () => {
await simctl.setPasteboard(pbContent, encoding);
expect(await simctl.getPasteboard(encoding)).to.eql(pbContent);
});
Expand All @@ -216,9 +179,6 @@ describe('simctl', function () {
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==';
let picturePath: string | undefined;
before(async function () {
if (major < 8 || (major === 8 && minor < 1)) {
return this.skip();
}
picturePath = path.join(os.tmpdir(), `${randomUUID()}.png`);
await fs.writeFile(
picturePath,
Expand Down Expand Up @@ -299,11 +259,6 @@ describe('simctl', function () {

describe('pushNotification', function () {
it('should not throw an error when sending a push notification', async function () {
if (process.env.CI) {
// This test is unstable in CI env
return this.skip();
}

const payload = {
'Simulator Target Bundle': 'com.apple.Preferences',
aps: {
Expand Down
65 changes: 36 additions & 29 deletions test/unit/simctl-specs.ts → test/unit/simctl.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,14 @@ import path from 'node:path';
import {expect, use} from 'chai';
import chaiAsPromised from 'chai-as-promised';
import {Simctl} from '../../lib/simctl';
import {describe, it, beforeEach, afterEach, after} from 'node:test';

use(chaiAsPromised);

// @ts-ignore - __dirname is available in CommonJS
const testDir =
typeof __dirname !== 'undefined' ? __dirname : path.dirname(require.resolve('./simctl-specs.ts'));

const devicePayloads = [
[
{
stdout: fs.readFileSync(path.join(testDir, 'fixtures/devices.json'), 'utf-8'),
},
{
stdout: fs.readFileSync(
path.join(__dirname, 'fixtures/devices-with-unavailable.json'),
'utf-8',
),
},
],
[
{
stdout: fs.readFileSync(path.join(__dirname, 'fixtures/devices-simple.json'), 'utf-8'),
},
{
stdout: fs.readFileSync(
path.join(__dirname, 'fixtures/devices-with-unavailable-simple.json'),
'utf-8',
),
},
],
];
const MODULE_NAME = 'node-simctl';

describe('simctl', function () {
let execStub: sinon.SinonStub;
const devicePayloads = getDevicePayloadsSync();

function stubSimctl(xcrun: {path?: string | null} = {}) {
const simctl = new Simctl({xcrun: {path: xcrun.path ?? null}});
Expand Down Expand Up @@ -345,3 +319,36 @@ describe('simctl', function () {
});
});
});

function getModuleRootSync(): string {
let currentDir = path.dirname(path.resolve(__filename));
let isAtFsRoot = false;
while (!isAtFsRoot) {
const manifestPath = path.join(currentDir, 'package.json');
try {
if (fs.existsSync(manifestPath)) {
const {name} = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as {name?: string};
if (name === MODULE_NAME) {
return currentDir;
}
}
} catch {
// ignore
}
currentDir = path.dirname(currentDir);
isAtFsRoot = currentDir.length <= path.dirname(currentDir).length;
}
throw new Error('Module root cannot be found');
}

function getDevicePayloadsSync(): {stdout: string}[][] {
const fixturesRoot = path.join(getModuleRootSync(), 'test', 'unit', 'fixtures');
const payloads: string[] = [
'devices.json',
'devices-with-unavailable.json',
'devices-simple.json',
'devices-with-unavailable-simple.json',
].map((file) => fs.readFileSync(path.join(fixturesRoot, file), 'utf-8'));
const mapper = (payload: string) => ({stdout: payload});
return [[payloads[0], payloads[1]].map(mapper), [payloads[2], payloads[3]].map(mapper)];
}
8 changes: 1 addition & 7 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,9 @@
"compilerOptions": {
"esModuleInterop": true,
"outDir": "build",
"types": ["node", "mocha"],
"types": ["node"],
"strict": true
},
"ts-node": {
"transpileOnly": true,
"compilerOptions": {
"rootDir": "."
}
},
"include": [
"lib",
"test"
Expand Down
Loading