diff --git a/.mocharc.js b/.mocharc.js deleted file mode 100644 index 40599e9..0000000 --- a/.mocharc.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - require: ['ts-node/register'], - forbidOnly: Boolean(process.env.CI) -}; diff --git a/package.json b/package.json index 8511b7c..6b9ac05 100644 --- a/package.json +++ b/package.json @@ -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, @@ -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", @@ -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" diff --git a/test/e2e/simctl-e2e-specs.ts b/test/e2e/simctl-e2e.spec.ts similarity index 87% rename from test/e2e/simctl-e2e-specs.ts rename to test/e2e/simctl-e2e.spec.ts index 9455fad..fe53f37 100644 --- a/test/e2e/simctl-e2e-specs.ts +++ b/test/e2e/simctl-e2e.spec.ts @@ -1,6 +1,4 @@ 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'; @@ -8,14 +6,14 @@ 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; @@ -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) { @@ -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'; @@ -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); }); @@ -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, @@ -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: { diff --git a/test/unit/simctl-specs.ts b/test/unit/simctl.spec.ts similarity index 89% rename from test/unit/simctl-specs.ts rename to test/unit/simctl.spec.ts index 40c5dd1..71762be 100644 --- a/test/unit/simctl-specs.ts +++ b/test/unit/simctl.spec.ts @@ -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}}); @@ -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)]; +} diff --git a/tsconfig.json b/tsconfig.json index 1e5a50e..f1aed4a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -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"