diff --git a/lib/simulator.js b/lib/simulator.js index e4f41d5..c4947f3 100644 --- a/lib/simulator.js +++ b/lib/simulator.js @@ -83,6 +83,14 @@ function compareSims(a, b) { return a.model < b.model ? -1 : a.model > b.model ? 1 : 0; } +function getCreateSimulatorHelpText(selectedXcode) { + if (selectedXcode && selectedXcode.version && appc.version.gte(selectedXcode.version, '27.0')) { + return '\n\nPlease open "Device Hub" app and create a new Simulator with your preferred configuration.'; + } + + return '\n\nPlease open Xcode, navigate to "Window > Devices and Simulators" and create a new Simulator with your preferred configuration.'; +} + /** * Detects iOS simulators. * @@ -770,7 +778,7 @@ function findSimulators(options, callback) { } if (!simHandle) { - const helpText = '\n\nPlease open Xcode, navigate to "Window > Devices and Simulators" and create a new Simulator with your preferred configuration.'; + const helpText = getCreateSimulatorHelpText(selectedXcode); // user experience! if (options.simVersion) { diff --git a/lib/xcode.js b/lib/xcode.js index 6940c6a..28cb109 100644 --- a/lib/xcode.js +++ b/lib/xcode.js @@ -40,204 +40,6 @@ var cache, * what Watch Simulator. It's a mystery! */ const simulatorDevicePairCompatibility = { - '>=6.2 <7.0': { // Xcode 6.2, 6.3, 6.4 - '>=8.2 <9.0': { // iOS 8.2, 8.3, 8.4 - '1.x': true // watchOS 1.0 - } - }, - '7.x': { // Xcode 7.x - '>=8.2 <9.0': { // iOS 8.2, 8.3, 8.4 - '1.x': true // watchOS 1.0 - }, - '>=9.0 <=9.2': { // iOS 9.0, 9.1, 9.2 - '>=2.0 <=2.1': true // watchOS 2.0, 2.1 - }, - '>=9.3 <10': { // iOS 9.3 - '2.2': true // watchOS 2.2 - } - }, - '8.x': { // Xcode 8.x - '>=9.0 <=9.2': { // iOS 9.0, 9.1, 9.2 - '>=2.0 <=2.1': true // watchOS 2.0, 2.1 - }, - '>=9.3 <10': { // iOS 9.x - '2.2': true, // watchOS 2.2 - '3.x': true // watchOS 3.x - }, - '10.x': { // iOS 10.x - '2.2': true, // watchOS 2.2 - '3.x': true // watchOS 3.x - } - }, - '9.x': { // Xcode 9.x - '>=9.0 <=9.2': { // iOS 9.0, 9.1, 9.2 - '>=2.0 <=2.1': true // watchOS 2.0, 2.1 - }, - '>=9.3 <10': { // iOS 9.x - '2.2': true, // watchOS 2.2 - '3.x': true // watchOS 3.x - }, - '10.x': { // iOS 10.x - '2.2': true, // watchOS 2.2 - '3.x': true // watchOS 3.x - }, - '11.x': { // iOS 11.x - '>=3.2 <4.0': true, // watchOS 3.2 - '4.x': true // watchOS 4.x - } - }, - '10.x <10.3': { // Xcode 10.0-10.2.1 - '8.x': {}, // iOS 8.x - '>=9.0 <=9.2': { // iOS 9.0, 9.1, 9.2 - '>=2.0 <=2.1': true // watchOS 2.0, 2.1 - }, - '>=9.3 <10': { // iOS 9.x - '2.2': true, // watchOS 2.2 - '3.x': true // watchOS 3.x - }, - '>=10.0 <=10.2': { // iOS 10.0, 10.1, 10.2 - '2.2': true, // watchOS 2.2 - '3.x': true // watchOS 3.x - }, - '>=10.3 <11': { // iOS 10.3 - '3.x': true // watchOS 3.x - }, - '11.x': { // iOS 11.x - '>=3.2 <4.0': true, // watchOS 3.2 - '4.x': true // watchOS 4.x - }, - '12.x': { // iOS 12.x - '>=3.2 <4.0': true, // watchOS 3.2 - '4.x': true, // watchOS 4.x - '5.x': true // watchOS 5.x - } - }, - '>=10.3 <11': { - '>=10.3 <11': { // iOS 10.3 - '3.x': true // watchOS 3.x - }, - '11.x': { // iOS 11.x - '>=3.2 <4.0': true, // watchOS 3.2 - '4.x': true // watchOS 4.x - }, - '12.x': { // iOS 12.x - '4.x': true, // watchOS 4.x - '5.x': true // watchOS 5.x - } - }, - '11.x': { // Xcode 11.x - '>=10.3 <11': { // iOS 10.3 - '2.2': true, // watchOS 2.2 - '3.x': true // watchOS 3.x - }, - '11.x': { // iOS 11.x - '>=3.2 <4.0': true, // watchOS 3.2 - '4.x': true // watchOS 4.x - }, - '12.x': { // iOS 12.x - '4.x': true, // watchOS 4.x - '5.x': true, // watchOS 5.x - '6.x': true // watchOS 6.x - }, - '13.x': { // iOS 13.x - '4.x': true, // watchOS 4.x - '5.x': true, // watchOS 5.x - '6.x': true // watchOS 6.x - } - }, - '12.x': { // Xcode 12.x - '>=10.3 <11': { // iOS 10.x - '2.2': true, // watchOS 2.2 - '3.x': true // watchOS 3.x - }, - '11.x': { // iOS 11.x - '>=3.2 <4.0': true, // watchOS 3.2 - '4.x': true // watchOS 4.x - }, - '12.x': { // iOS 12.x - '4.x': true, // watchOS 4.x - '5.x': true, // watchOS 5.x - '6.x': true, // watchOS 6.x - '7.x': true // watchOS 7.x - }, - '13.x': { // iOS 13.x - '4.x': true, // watchOS 4.x - '5.x': true, // watchOS 5.x - '6.x': true, // watchOS 6.x - '7.x': true // watchOS 7.x - }, - '14.x': { // iOS 14.x - '4.x': true, // watchOS 4.x - '5.x': true, // watchOS 5.x - '6.x': true, // watchOS 6.x - '7.x': true // watchOS 7.x - } - }, - '13.x': { // Xcode 13.x - '>=10.3 <11': { // iOS 10.x - '2.2': true, // watchOS 2.2 - '3.x': true // watchOS 3.x - }, - '11.x': { // iOS 11.x - '>=3.2 <4.0': true, // watchOS 3.2 - '4.x': true // watchOS 4.x - }, - '12.x': { // iOS 12.x - '4.x': true, // watchOS 4.x - '5.x': true, // watchOS 5.x - '6.x': true, // watchOS 6.x - '7.x': true, // watchOS 7.x - '8.x': true // watchOS 8.x - }, - '13.x': { // iOS 13.x - '4.x': true, // watchOS 4.x - '5.x': true, // watchOS 5.x - '6.x': true, // watchOS 6.x - '7.x': true, // watchOS 7.x - '8.x': true // watchOS 8.x - }, - '14.x': { // iOS 14.x - '4.x': true, // watchOS 4.x - '5.x': true, // watchOS 5.x - '6.x': true, // watchOS 6.x - '7.x': true, // watchOS 7.x - '8.x': true // watchOS 8.x - }, - '15.x': { - '4.x': true, // watchOS 4.x - '5.x': true, // watchOS 5.x - '6.x': true, // watchOS 6.x - '7.x': true, // watchOS 7.x - '8.x': true // watchOS 8.x - } - }, - '14.x': { // Xcode 14.x - '12.x': { // iOS 12.x - '7.x': true, // watchOS 7.x - '8.x': true, // watchOS 8.x - '9.x': true // watchOS 9.x - }, - '13.x': { // iOS 13.x - '7.x': true, // watchOS 7.x - '8.x': true, // watchOS 8.x - '9.x': true // watchOS 9.x - }, - '14.x': { // iOS 14.x - '7.x': true, // watchOS 7.x - '8.x': true, // watchOS 8.x - '9.x': true // watchOS 9.x - }, - '15.x': { - '7.x': true, // watchOS 7.x - '8.x': true, // watchOS 8.x - '9.x': true // watchOS 9.x - }, - '16.x': { - '7.x': true, // watchOS 7.x - '8.x': true, // watchOS 8.x - '9.x': true // watchOS 9.x - } - }, '15.x': { // Xcode 15.x '13.x': { // iOS 13.x '7.x': true, // watchOS 7.x @@ -315,6 +117,16 @@ const simulatorDevicePairCompatibility = { '11.x': true, // watchOS 11.x '26.x': true, // watchOS 26.x }, + }, + '27.x': { // Xcode 27.x + '26.x': { // iOS 26.x + '26.x': true, // watchOS 26.x + '27.x': true, // watchOS 27.x + }, + '27.x': { // iOS 27.x + '26.x': true, // watchOS 26.x + '27.x': true, // watchOS 27.x + }, } }; @@ -641,6 +453,13 @@ exports.detect = function detect(options, callback) { } }); + if (!xc.executables.simulator) { + var deviceHub = path.join(dir, '..', 'Applications', 'DeviceHub.app', 'Contents', 'MacOS', 'DeviceHub'); + if (fs.existsSync(deviceHub)) { + xc.executables.simulator = deviceHub; + } + } + if (appc.version.gte(xc.version, 9)) { xc.executables.watchsimulator = xc.executables.simulator; } else { diff --git a/test/test-ioslib.js b/test/test-ioslib.js index a61cf27..ea76706 100644 --- a/test/test-ioslib.js +++ b/test/test-ioslib.js @@ -19,8 +19,8 @@ describe('ioslib', function () { }); it('detect all iOS information', function (done) { - this.timeout(80000); - this.slow(30000); + this.timeout(180000); + this.slow(60000); ioslib.detect(function (err, results) { if (err) { diff --git a/test/test-simulator.js b/test/test-simulator.js index eceedac..d0444de 100644 --- a/test/test-simulator.js +++ b/test/test-simulator.js @@ -166,8 +166,8 @@ describe('simulator', function () { }); it('detect iOS Simulators', function (done) { - this.timeout(8000); - this.slow(2000); + this.timeout(30000); + this.slow(10000); ioslib.simulator.detect(function (err, results) { if (err) { diff --git a/test/test-teams.js b/test/test-teams.js index 4359bf0..2a8dfc1 100644 --- a/test/test-teams.js +++ b/test/test-teams.js @@ -17,8 +17,8 @@ describe('teams', function () { }); it('detect teams', function (done) { - this.timeout(5000); - this.slow(2000); + this.timeout(30000); + this.slow(10000); ioslib.teams.detect(function (err, results) { if (err) { diff --git a/test/test-xcode.js b/test/test-xcode.js index 24823be..ebe8822 100644 --- a/test/test-xcode.js +++ b/test/test-xcode.js @@ -10,14 +10,29 @@ */ const + execFile = require('child_process').execFile, fs = require('fs'), ioslib = require('..'); +function skipUnlessSelectedXcode27(ctx, done, callback) { + execFile('xcodebuild', [ '-version' ], function (err, stdout) { + if (err) { + return done(err); + } + + if (!/^Xcode 27\./m.test(stdout)) { + return ctx.skip(); + } + + callback(); + }); +} + function checkXcode(xcode) { should(xcode).be.an.Object; should(xcode).have.keys('xcodeapp', 'path', 'selected', 'version', 'build', 'supported', 'eulaAccepted', 'sdks', 'sims', 'simDeviceTypes', - 'simRuntimes', 'watchos', 'tvos', 'teams', 'executables'); + 'simRuntimes', 'simDevicePairs', 'watchos', 'tvos', 'teams', 'executables'); should(xcode.xcodeapp).be.a.String; should(xcode.xcodeapp).not.equal(''); @@ -154,4 +169,66 @@ describe('xcode', function () { done(); }); }); + + it('detect should map Xcode 27 simulator device pairs', function (done) { + this.timeout(5000); + this.slow(2000); + + skipUnlessSelectedXcode27(this, done, function () { + ioslib.xcode.detect({ bypassCache: true }, function (err, results) { + if (err) { + return done(err); + } + + var xcode27 = Object.keys(results.xcode) + .map(function (id) { return results.xcode[id]; }) + .filter(function (xc) { return /^27\./.test(xc.version); }) + .shift(); + + if (!xcode27) { + return done(); + } + + should(xcode27.simDevicePairs).eql({ + '26.x': { + '26.x': true, + '27.x': true + }, + '27.x': { + '26.x': true, + '27.x': true + } + }); + done(); + }); + }); + }); + + it('detect should use DeviceHub as the Xcode 27 simulator executable', function (done) { + this.timeout(5000); + this.slow(2000); + + skipUnlessSelectedXcode27(this, done, function () { + ioslib.xcode.detect({ bypassCache: true }, function (err, results) { + if (err) { + return done(err); + } + + var xcode27 = Object.keys(results.xcode) + .map(function (id) { return results.xcode[id]; }) + .filter(function (xc) { return /^27\./.test(xc.version); }) + .shift(); + + if (!xcode27) { + return done(); + } + + var deviceHub = 'DeviceHub.app/Contents/MacOS/DeviceHub'; + should(xcode27.executables.simulator).not.equal(null); + should(xcode27.executables.simulator).endWith(deviceHub); + should(xcode27.executables.watchsimulator).equal(xcode27.executables.simulator); + done(); + }); + }); + }); });