Skip to content

Commit 88d46a5

Browse files
committed
chore(ci): Add unit tests for run and Catalyst stuff
1 parent ca5bd0c commit 88d46a5

File tree

4 files changed

+194
-82
lines changed

4 files changed

+194
-82
lines changed

lib/run.js

+20-8
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ module.exports.run = function (runOptions) {
4848
return Promise.resolve()
4949
.then(() => {
5050
if (!runOptions.emulator && !useCatalyst) {
51-
return require('./listDevices').run().then(devices => {
51+
return module.exports.execListDevices().then(devices => {
5252
if (devices.length > 0) {
5353
useDevice = true;
5454

@@ -84,13 +84,12 @@ module.exports.run = function (runOptions) {
8484
.then(() => {
8585
// Uncompress IPA (zip file)
8686
const appFileInflated = path.join(buildOutputDir, 'Payload', `${projectName}.app`);
87-
const appFile = path.join(buildOutputDir, `${projectName}.app`);
8887
const payloadFolder = path.join(buildOutputDir, 'Payload');
8988

9089
// delete the existing platform/ios/build/device/appname.app
91-
fs.rmSync(appFile, { recursive: true, force: true });
90+
fs.rmSync(appPath, { recursive: true, force: true });
9291
// move the platform/ios/build/device/Payload/appname.app to parent
93-
fs.renameSync(appFileInflated, appFile);
92+
fs.renameSync(appFileInflated, appPath);
9493
// delete the platform/ios/build/device/Payload folder
9594
fs.rmSync(payloadFolder, { recursive: true, force: true });
9695

@@ -106,6 +105,7 @@ module.exports.run = function (runOptions) {
106105
return module.exports.deployToDevice(appPath, runOptions.target, extraArgs);
107106
},
108107
// if device connection check failed use emulator then
108+
// This might fail due to being the wrong type of app bundle
109109
() => module.exports.deployToSim(appPath, runOptions.target)
110110
);
111111
} else if (useCatalyst) {
@@ -127,6 +127,8 @@ module.exports.deployToSim = deployToSim;
127127
module.exports.startSim = startSim;
128128
module.exports.listDevices = listDevices;
129129
module.exports.listEmulators = listEmulators;
130+
module.exports.execListDevices = execListDevices;
131+
module.exports.execListEmulatorTargets = execListEmulatorTargets;
130132

131133
/**
132134
* Filters the args array and removes supported args for the 'run' command.
@@ -195,13 +197,13 @@ async function deployToSim (appPath, target) {
195197

196198
if (!target) {
197199
// Select target device for emulator (preferring iPhone Emulators)
198-
const emulators = await require('./listEmulatorImages').run();
200+
const emulators = await module.exports.execListEmulatorTargets();
199201
const iPhoneEmus = emulators.filter(emulator => emulator.startsWith('iPhone'));
200202
target = iPhoneEmus.concat(emulators)[0];
201203
events.emit('log', `No target specified for emulator. Deploying to "${target}" simulator.`);
202204
}
203205

204-
return startSim(appPath, target);
206+
return module.exports.startSim(appPath, target);
205207
}
206208

207209
function startSim (appPath, target) {
@@ -230,8 +232,18 @@ function startSim (appPath, target) {
230232
return subprocess;
231233
}
232234

235+
/* istanbul ignore next */
236+
function execListDevices () {
237+
return require('./listDevices').run();
238+
}
239+
240+
/* istanbul ignore next */
241+
function execListEmulatorTargets () {
242+
return require('./listEmulatorTargets').run();
243+
}
244+
233245
function listDevices () {
234-
return require('./listDevices').run()
246+
return module.exports.execListDevices()
235247
.then(devices => {
236248
events.emit('log', 'Available iOS Devices:');
237249
devices.forEach(device => {
@@ -241,7 +253,7 @@ function listDevices () {
241253
}
242254

243255
function listEmulators () {
244-
return require('./listEmulatorImages').run()
256+
return module.exports.execListEmulatorTargets()
245257
.then(emulators => {
246258
events.emit('log', 'Available iOS Simulators:');
247259
emulators.forEach(emulator => {

tests/spec/unit/build.spec.js

+21
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,27 @@ describe('build', () => {
225225
]);
226226
expect(args.length).toEqual(18);
227227
});
228+
229+
it('should generate appropriate args for Catalyst macOS builds', () => {
230+
const buildOpts = {
231+
catalyst: true
232+
};
233+
234+
const args = getXcodeBuildArgs('TestProjectName', testProjectPath, 'TestConfiguration', '', buildOpts);
235+
expect(args).toEqual([
236+
'-workspace',
237+
'TestProjectName.xcworkspace',
238+
'-scheme',
239+
'TestProjectName',
240+
'-configuration',
241+
'TestConfiguration',
242+
'-destination',
243+
'generic/platform=macOS,variant=Mac Catalyst',
244+
'build',
245+
`SYMROOT=${path.join(testProjectPath, 'build')}`
246+
]);
247+
expect(args.length).toEqual(10);
248+
});
228249
});
229250

230251
describe('getXcodeArchiveArgs method', () => {

tests/spec/unit/lib/run.spec.js

-74
This file was deleted.

tests/spec/unit/run.spec.js

+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/*
2+
Licensed to the Apache Software Foundation (ASF) under one
3+
or more contributor license agreements. See the NOTICE file
4+
distributed with this work for additional information
5+
regarding copyright ownership. The ASF licenses this file
6+
to you under the Apache License, Version 2.0 (the
7+
"License"); you may not use this file except in compliance
8+
with the License. You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing,
13+
software distributed under the License is distributed on an
14+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
KIND, either express or implied. See the License for the
16+
specific language governing permissions and limitations
17+
under the License.
18+
*/
19+
20+
const path = require('node:path');
21+
const { CordovaError, events } = require('cordova-common');
22+
const build = require('../../../lib/build');
23+
const run = require('../../../lib/run');
24+
25+
describe('cordova/lib/run', () => {
26+
const testProjectPath = path.join('/test', 'project', 'path');
27+
28+
beforeEach(() => {
29+
run.root = testProjectPath;
30+
});
31+
32+
describe('runListDevices method', () => {
33+
beforeEach(() => {
34+
spyOn(events, 'emit');
35+
spyOn(run, 'execListDevices').and.returnValue(Promise.resolve(["iPhone Xs"]));
36+
spyOn(run, 'execListEmulatorTargets').and.returnValue(Promise.resolve(["iPhone 15 Simulator"]));
37+
});
38+
39+
it('should delegate to "listDevices" when the "runListDevices" method options param contains "options.device".', () => {
40+
return run.runListDevices({ options: { device: true } }).then(() => {
41+
expect(run.execListDevices).toHaveBeenCalled();
42+
expect(run.execListEmulatorTargets).not.toHaveBeenCalled();
43+
44+
expect(events.emit).toHaveBeenCalledWith('log', '\tiPhone Xs');
45+
});
46+
});
47+
48+
it('should delegate to "listDevices" when the "runListDevices" method options param contains "options.emulator".', () => {
49+
return run.runListDevices({ options: { emulator: true } }).then(() => {
50+
expect(run.execListDevices).not.toHaveBeenCalled();
51+
expect(run.execListEmulatorTargets).toHaveBeenCalled();
52+
53+
expect(events.emit).toHaveBeenCalledWith('log', '\tiPhone 15 Simulator');
54+
});
55+
});
56+
57+
it('should delegate to both "listEmulators" and "listDevices" when the "runListDevices" method does not contain "options.device" or "options.emulator".', () => {
58+
return run.runListDevices().then(() => {
59+
expect(run.execListDevices).toHaveBeenCalled();
60+
expect(run.execListEmulatorTargets).toHaveBeenCalled();
61+
62+
expect(events.emit).toHaveBeenCalledWith('log', '\tiPhone Xs');
63+
expect(events.emit).toHaveBeenCalledWith('log', '\tiPhone 15 Simulator');
64+
});
65+
});
66+
});
67+
68+
describe('run method', () => {
69+
beforeEach(() => {
70+
spyOn(build, 'run').and.returnValue(Promise.resolve());
71+
spyOn(build, 'findXCodeProjectIn').and.returnValue('ProjectName');
72+
spyOn(run, 'execListDevices').and.resolveTo([]);
73+
spyOn(run, 'execListEmulatorTargets').and.resolveTo([]);
74+
spyOn(run, 'listDevices').and.resolveTo();
75+
spyOn(run, 'deployToMac').and.resolveTo();
76+
spyOn(run, 'deployToSim').and.resolveTo();
77+
spyOn(run, 'checkDeviceConnected').and.rejectWith(new Error('No Device Connected'));
78+
});
79+
80+
describe('--list option', () => {
81+
beforeEach(() => {
82+
spyOn(run, 'listEmulators').and.returnValue(Promise.resolve());
83+
});
84+
85+
it('should delegate to listDevices method if `options.device` specified', () => {
86+
return run.run({ list: true, device: true }).then(() => {
87+
expect(run.listDevices).toHaveBeenCalled();
88+
expect(run.listEmulators).not.toHaveBeenCalled();
89+
});
90+
});
91+
92+
it('should delegate to listEmulators method if `options.device` specified', () => {
93+
return run.run({ list: true, emulator: true }).then(() => {
94+
expect(run.listDevices).not.toHaveBeenCalled();
95+
expect(run.listEmulators).toHaveBeenCalled();
96+
});
97+
});
98+
99+
it('should delegate to both listEmulators and listDevices methods if neither `options.device` nor `options.emulator` are specified', () => {
100+
return run.run({ list: true }).then(() => {
101+
expect(run.listDevices).toHaveBeenCalled();
102+
expect(run.listEmulators).toHaveBeenCalled();
103+
});
104+
});
105+
});
106+
107+
it('should not accept device and emulator options together', () => {
108+
spyOn(Promise, 'reject');
109+
110+
run.run({
111+
device: true,
112+
emulator: true
113+
});
114+
115+
expect(Promise.reject).toHaveBeenCalledWith(new CordovaError('Only one of "device"/"emulator" options should be specified'));
116+
});
117+
118+
it('should run on a simulator if --device is not specified and no device is connected', () => {
119+
return run.run({ }).then(() => {
120+
expect(run.deployToSim).toHaveBeenCalled();
121+
expect(build.run).toHaveBeenCalled();
122+
});
123+
});
124+
125+
it('should try to run on a device if --device is not specified and a device is connected', () => {
126+
run.execListDevices.and.resolveTo(["iPhone 12 Plus"]);
127+
128+
return run.run({ }).then(() => {
129+
expect(run.checkDeviceConnected).toHaveBeenCalled();
130+
expect(build.run).toHaveBeenCalledWith(jasmine.objectContaining({ device: true }));
131+
});
132+
});
133+
134+
it('should try to run on a device if --device is specified', () => {
135+
return run.run({ device: true }).then(() => {
136+
expect(run.checkDeviceConnected).toHaveBeenCalled();
137+
expect(build.run).toHaveBeenCalledWith(jasmine.objectContaining({ device: true }));
138+
});
139+
});
140+
141+
it('should not run a build if --noBuild is passed', () => {
142+
return run.run({ emulator: true, nobuild: true }).then(() => {
143+
expect(build.run).not.toHaveBeenCalled();
144+
});
145+
});
146+
147+
it('should try to launch the macOS Catalyst app bundle', () => {
148+
return run.run({ device: true, target: 'mac', release: true }).then(() => {
149+
expect(run.deployToMac).toHaveBeenCalledWith(path.join(testProjectPath, 'build', 'Release-maccatalyst', 'ProjectName.app'));
150+
});
151+
});
152+
});
153+
});

0 commit comments

Comments
 (0)