From f29f35ca74ae58e55a7a481f7193016a05093732 Mon Sep 17 00:00:00 2001 From: Darryl Pogue Date: Tue, 20 Aug 2024 02:38:55 -0700 Subject: [PATCH] fix: Only support tinted/dark icons if using Xcode 16+ Xcode 15 doesn't understand the appearance variants, and if variants are specified then it thinks there are 3 images assigned to the same slot and ends up picking one at random and we don't want that. --- lib/prepare.js | 110 +++++++++------- tests/spec/unit/prepare.spec.js | 215 +++++++++++++++++++------------- 2 files changed, 192 insertions(+), 133 deletions(-) diff --git a/lib/prepare.js b/lib/prepare.js index b549acdb0..73f900443 100644 --- a/lib/prepare.js +++ b/lib/prepare.js @@ -33,11 +33,21 @@ const projectFile = require('./projectFile'); const Podfile = require('./Podfile').Podfile; const check_reqs = require('./check_reqs'); const PlatformConfigParser = require('./PlatformConfigParser'); +const versions = require('./versions'); // launch storyboard and related constants const IMAGESET_COMPACT_SIZE_CLASS = 'compact'; const CDV_ANY_SIZE_CLASS = 'any'; +const ASSUMED_XCODE_VERSION = '15.0.0'; +function checkOrAssumeXcodeVersion () { + if (process.platform === 'darwin') { + return versions.get_apple_xcode_version(); + } else { + return Promise.resolve(ASSUMED_XCODE_VERSION); + } +} + module.exports.prepare = function (cordovaProject, options) { const platformJson = PlatformJson.load(this.locations.root, 'ios'); const munger = new PlatformMunger('ios', this.locations.root, platformJson, new PluginInfoProvider()); @@ -55,15 +65,11 @@ module.exports.prepare = function (cordovaProject, options) { return updateWww(cordovaProject, this.locations) // update project according to config.xml changes. .then(() => updateProject(this._config, this.locations)) - .then(() => { - updateIcons(cordovaProject, this.locations); - updateLaunchStoryboardImages(cordovaProject, this.locations); - updateBackgroundColor(cordovaProject, this.locations); - updateFileResources(cordovaProject, this.locations); - }) - .then(() => { - alertDeprecatedPreference(this._config); - }) + .then(() => updateIcons(cordovaProject, this.locations)) + .then(() => updateLaunchStoryboardImages(cordovaProject, this.locations)) + .then(() => updateBackgroundColor(cordovaProject, this.locations)) + .then(() => updateFileResources(cordovaProject, this.locations)) + .then(() => alertDeprecatedPreference(this._config)) .then(() => { events.emit('verbose', 'Prepared iOS project successfully'); }); @@ -83,13 +89,12 @@ module.exports.clean = function (options) { const projectConfig = new ConfigParser(this.locations.configXml); - return Promise.resolve().then(() => { - cleanWww(projectRoot, this.locations); - cleanIcons(projectRoot, projectConfig, this.locations); - cleanLaunchStoryboardImages(projectRoot, projectConfig, this.locations); - cleanBackgroundColor(projectRoot, projectConfig, this.locations); - cleanFileResources(projectRoot, projectConfig, this.locations); - }); + return Promise.resolve() + .then(() => cleanWww(projectRoot, this.locations)) + .then(() => cleanIcons(projectRoot, projectConfig, this.locations)) + .then(() => cleanLaunchStoryboardImages(projectRoot, projectConfig, this.locations)) + .then(() => cleanBackgroundColor(projectRoot, projectConfig, this.locations)) + .then(() => cleanFileResources(projectRoot, projectConfig, this.locations)); }; /** @@ -362,7 +367,7 @@ function handleBuildSettings (platformConfig, locations, infoPlist) { return Promise.resolve(); } -function mapIconResources (icons, iconsDir) { +function mapIconResources (icons, iconsDir, xcodeversion) { // Ref: https://developer.apple.com/design/human-interface-guidelines/app-icons // These are ordered according to how Xcode puts them in the Contents.json file const platformIcons = [ @@ -426,6 +431,9 @@ function mapIconResources (icons, iconsDir) { const pathMap = {}; + // We can only support dark mode and tinted icons with Xcode 16 + const isAtLeastXcode16 = versions.compareVersions(xcodeversion, '16.0.0') >= 0; + function getDefaultIconForTarget (target) { const def = icons.filter(res => !res.width && !res.height && !res.target).pop(); @@ -475,7 +483,7 @@ function mapIconResources (icons, iconsDir) { } // Only iOS has dark/tinted icon variants - if (!item.target || item.target === 'spotlight') { + if (isAtLeastXcode16 && (!item.target || item.target === 'spotlight')) { if (icon.foreground) { pathMap[dest.replace('.png', '-dark.png')] = icon.foreground; } @@ -533,48 +541,58 @@ function updateIcons (cordovaProject, locations) { if (icons.length === 0) { events.emit('verbose', 'This app does not have icons defined'); - return; + return Promise.resolve(); } const platformProjDir = path.relative(cordovaProject.root, locations.xcodeCordovaProj); const iconsDir = path.join(platformProjDir, 'Assets.xcassets', 'AppIcon.appiconset'); - const resourceMap = mapIconResources(icons, iconsDir); - events.emit('verbose', `Updating icons at ${iconsDir}`); - FileUpdater.updatePaths( - resourceMap, { rootDir: cordovaProject.root }, logFileOp); - // Now we need to update the AppIcon.appiconset/Contents.json file - const contentsJSON = generateAppIconContentsJson(resourceMap); + return checkOrAssumeXcodeVersion() + .then((xcodeversion) => { + const resourceMap = mapIconResources(icons, iconsDir, xcodeversion); + events.emit('verbose', `Updating icons at ${iconsDir}`); + FileUpdater.updatePaths( + resourceMap, { rootDir: cordovaProject.root }, logFileOp); + + // Now we need to update the AppIcon.appiconset/Contents.json file + const contentsJSON = generateAppIconContentsJson(resourceMap); - events.emit('verbose', 'Updating App Icon image set contents.json'); - fs.writeFileSync(path.join(cordovaProject.root, iconsDir, 'Contents.json'), JSON.stringify(contentsJSON, null, 2)); + events.emit('verbose', 'Updating App Icon image set contents.json'); + fs.writeFileSync(path.join(cordovaProject.root, iconsDir, 'Contents.json'), JSON.stringify(contentsJSON, null, 2)); + }); } function cleanIcons (projectRoot, projectConfig, locations) { const icons = projectConfig.getIcons('ios'); - if (icons.length > 0) { - const platformProjDir = path.relative(projectRoot, locations.xcodeCordovaProj); - const iconsDir = path.join(platformProjDir, 'Assets.xcassets', 'AppIcon.appiconset'); - const resourceMap = mapIconResources(icons, iconsDir); - Object.keys(resourceMap).forEach(targetIconPath => { - resourceMap[targetIconPath] = null; - }); - events.emit('verbose', `Cleaning icons at ${iconsDir}`); + if (icons.length === 0) { + return Promise.resolve(); + } - // Source paths are removed from the map, so updatePaths() will delete the target files. - FileUpdater.updatePaths( - resourceMap, { rootDir: projectRoot, all: true }, logFileOp); + const platformProjDir = path.relative(projectRoot, locations.xcodeCordovaProj); + const iconsDir = path.join(platformProjDir, 'Assets.xcassets', 'AppIcon.appiconset'); - const contentsJSON = generateAppIconContentsJson(resourceMap); + return checkOrAssumeXcodeVersion() + .then((xcodeversion) => { + const resourceMap = mapIconResources(icons, iconsDir, xcodeversion); + Object.keys(resourceMap).forEach(targetIconPath => { + resourceMap[targetIconPath] = null; + }); + events.emit('verbose', `Cleaning icons at ${iconsDir}`); - // delete filename from contents.json - contentsJSON.images.forEach(image => { - image.filename = undefined; - }); + // Source paths are removed from the map, so updatePaths() will delete the target files. + FileUpdater.updatePaths( + resourceMap, { rootDir: projectRoot, all: true }, logFileOp); - events.emit('verbose', 'Updating App Icon image set contents.json'); - fs.writeFileSync(path.join(projectRoot, iconsDir, 'Contents.json'), JSON.stringify(contentsJSON, null, 2)); - } + const contentsJSON = generateAppIconContentsJson(resourceMap); + + // delete filename from contents.json + contentsJSON.images.forEach(image => { + image.filename = undefined; + }); + + events.emit('verbose', 'Updating App Icon image set contents.json'); + fs.writeFileSync(path.join(projectRoot, iconsDir, 'Contents.json'), JSON.stringify(contentsJSON, null, 2)); + }); } /** diff --git a/tests/spec/unit/prepare.spec.js b/tests/spec/unit/prepare.spec.js index 836928d8b..b8ecd7672 100644 --- a/tests/spec/unit/prepare.spec.js +++ b/tests/spec/unit/prepare.spec.js @@ -29,6 +29,7 @@ const rewire = require('rewire'); const prepare = rewire('../../../lib/prepare'); const projectFile = require('../../../lib/projectFile'); const FileUpdater = require('cordova-common').FileUpdater; +const versions = require('../../../lib/versions'); const tmpDir = path.join(__dirname, '../../../tmp'); const FIXTURES = path.join(__dirname, 'fixtures'); @@ -401,6 +402,7 @@ describe('prepare', () => { }); describe('App Icon handling', () => { + const xcver = '16.0.0'; const mapIconResources = prepare.__get__('mapIconResources'); describe('#mapIconResources', () => { @@ -409,7 +411,7 @@ describe('prepare', () => { { src: 'dummy.png' } ]; - const resMap = mapIconResources(icons, ''); + const resMap = mapIconResources(icons, '', xcver); expect(resMap).toEqual(jasmine.objectContaining({ 'icon.png': 'dummy.png', @@ -423,7 +425,7 @@ describe('prepare', () => { { src: 'dummy-watch.png', target: 'watchos' } ]; - const resMap = mapIconResources(icons, ''); + const resMap = mapIconResources(icons, '', xcver); expect(resMap).toEqual(jasmine.objectContaining({ 'icon.png': 'dummy.png', @@ -431,12 +433,12 @@ describe('prepare', () => { })); }); - it('should handle default icon variants', () => { + it('should handle default icon variants on Xcode 16+', () => { const icons = [ { src: 'dummy.png', monochrome: 'dummy-tint.png', foreground: 'dummy-dark.png' } ]; - const resMap = mapIconResources(icons, ''); + const resMap = mapIconResources(icons, '', xcver); expect(resMap).toEqual(jasmine.objectContaining({ 'icon.png': 'dummy.png', @@ -446,12 +448,25 @@ describe('prepare', () => { })); }); + it('should ignore default icon variants on Xcode 15', () => { + const icons = [ + { src: 'dummy.png', monochrome: 'dummy-tint.png', foreground: 'dummy-dark.png' } + ]; + + const resMap = mapIconResources(icons, '', '15.0.0'); + + expect(resMap).toEqual(jasmine.objectContaining({ + 'icon.png': 'dummy.png', + 'watchos.png': 'dummy.png' + })); + }); + it('should handle a single sized icon', () => { const icons = [ { src: 'dummy.png', height: 1024, width: 1024 } ]; - const resMap = mapIconResources(icons, ''); + const resMap = mapIconResources(icons, '', xcver); expect(resMap).toEqual(jasmine.objectContaining({ 'icon.png': 'dummy.png', @@ -464,7 +479,7 @@ describe('prepare', () => { { src: 'dummy.png', height: 1024, width: 1024, target: 'watchos' } ]; - const resMap = mapIconResources(icons, ''); + const resMap = mapIconResources(icons, '', xcver); expect(resMap).toEqual(jasmine.objectContaining({ 'watchos.png': 'dummy.png' @@ -476,7 +491,7 @@ describe('prepare', () => { { src: 'dummy.png', height: 120, width: 120 } ]; - const resMap = mapIconResources(icons, ''); + const resMap = mapIconResources(icons, '', xcver); expect(resMap).toEqual(jasmine.objectContaining({ 'icon-40@3x.png': 'dummy.png', @@ -490,7 +505,7 @@ describe('prepare', () => { { src: 'dummy-spot.png', height: 120, width: 120, target: 'spotlight' } ]; - const resMap = mapIconResources(icons, ''); + const resMap = mapIconResources(icons, '', xcver); expect(resMap).toEqual(jasmine.objectContaining({ 'icon-40@3x.png': 'dummy-spot.png', @@ -503,7 +518,7 @@ describe('prepare', () => { { src: 'dummy.png', height: 76, width: 76, monochrome: 'dummy-tint.png', foreground: 'dummy-dark.png' } ]; - const resMap = mapIconResources(icons, ''); + const resMap = mapIconResources(icons, '', xcver); expect(resMap).toEqual(jasmine.objectContaining({ 'icon-38@2x.png': 'dummy.png', @@ -517,7 +532,7 @@ describe('prepare', () => { { src: 'dummy.png', height: 216, width: 216 } ]; - const resMap = mapIconResources(icons, ''); + const resMap = mapIconResources(icons, '', xcver); expect(resMap).toEqual({}); }); @@ -527,7 +542,7 @@ describe('prepare', () => { { src: 'dummy.png', height: 256, width: 256, target: 'mac' } ]; - const resMap = mapIconResources(icons, ''); + const resMap = mapIconResources(icons, '', xcver); expect(resMap).toEqual(jasmine.objectContaining({ 'mac-128@2x.png': 'dummy.png', @@ -541,7 +556,7 @@ describe('prepare', () => { { foreground: 'dummy-dark.png', height: 216, width: 216, target: 'watchos' } ]; - const resMap = mapIconResources(icons, ''); + const resMap = mapIconResources(icons, '', xcver); expect(resMap).toEqual({}); }); @@ -569,81 +584,103 @@ describe('prepare', () => { fs.cpSync(path.join(FIXTURES, 'icon-support', 'res'), path.join(iosProject, 'res'), { recursive: true }); // copy icons and update Contents.json - updateIcons(project, p.locations); + return updateIcons(project, p.locations); } it('should not update paths if no icons are specified', () => { const updatePaths = spyOn(FileUpdater, 'updatePaths'); - updateIconsWithConfig('none.xml'); + return updateIconsWithConfig('none.xml') + .then(() => { + expect(updatePaths).not.toHaveBeenCalled(); - expect(updatePaths).not.toHaveBeenCalled(); - - // verify that that Contents.json is as we expect - const result = JSON.parse(fs.readFileSync(path.join(iosProject, iconsDir, 'Contents.json'))); - expect(result).toEqual(require('./fixtures/icon-support/contents-json/none')); + // verify that that Contents.json is as we expect + const result = JSON.parse(fs.readFileSync(path.join(iosProject, iconsDir, 'Contents.json'))); + expect(result).toEqual(require('./fixtures/icon-support/contents-json/none')); + }); }); it('should update paths if a single icon is specified', () => { const updatePaths = spyOn(FileUpdater, 'updatePaths'); - updateIconsWithConfig('single-only.xml'); - - expect(updatePaths).toHaveBeenCalledWith({ - [path.join(iconsDir, 'icon.png')]: 'res/ios/appicon.png', - [path.join(iconsDir, 'watchos.png')]: 'res/ios/appicon.png' - }, { rootDir: iosProject }, logFileOp); + return updateIconsWithConfig('single-only.xml') + .then(() => { + expect(updatePaths).toHaveBeenCalledWith({ + [path.join(iconsDir, 'icon.png')]: 'res/ios/appicon.png', + [path.join(iconsDir, 'watchos.png')]: 'res/ios/appicon.png' + }, { rootDir: iosProject }, logFileOp); - // verify that that Contents.json is as we expect - const result = JSON.parse(fs.readFileSync(path.join(iosProject, iconsDir, 'Contents.json'))); - expect(result).toEqual(require('./fixtures/icon-support/contents-json/single-only')); + // verify that that Contents.json is as we expect + const result = JSON.parse(fs.readFileSync(path.join(iosProject, iconsDir, 'Contents.json'))); + expect(result).toEqual(require('./fixtures/icon-support/contents-json/single-only')); + }); }); - it('should update paths if a single icon with variants is specified', () => { + it('should update only some paths if a single icon with variants is specified with Xcode 15', () => { const updatePaths = spyOn(FileUpdater, 'updatePaths'); + spyOn(versions, 'get_apple_xcode_version').and.returnValue(Promise.resolve('15.0.0')); - updateIconsWithConfig('single-variants.xml'); + return updateIconsWithConfig('single-variants.xml') + .then(() => { + expect(updatePaths).toHaveBeenCalledWith({ + [path.join(iconsDir, 'icon.png')]: 'res/ios/appicon.png', + [path.join(iconsDir, 'watchos.png')]: 'res/ios/appicon.png' + }, { rootDir: iosProject }, logFileOp); - expect(updatePaths).toHaveBeenCalledWith({ - [path.join(iconsDir, 'icon.png')]: 'res/ios/appicon.png', - [path.join(iconsDir, 'icon-dark.png')]: 'res/ios/appicon-dark.png', - [path.join(iconsDir, 'icon-tinted.png')]: 'res/ios/appicon-tint.png', - [path.join(iconsDir, 'watchos.png')]: 'res/ios/appicon.png' - }, { rootDir: iosProject }, logFileOp); + // verify that that Contents.json is as we expect + const result = JSON.parse(fs.readFileSync(path.join(iosProject, iconsDir, 'Contents.json'))); + expect(result).toEqual(require('./fixtures/icon-support/contents-json/single-only')); + }); + }); - // verify that that Contents.json is as we expect - const result = JSON.parse(fs.readFileSync(path.join(iosProject, iconsDir, 'Contents.json'))); - expect(result).toEqual(require('./fixtures/icon-support/contents-json/single-variants')); + it('should update paths if a single icon with variants is specified with Xcode 16', () => { + const updatePaths = spyOn(FileUpdater, 'updatePaths'); + spyOn(versions, 'get_apple_xcode_version').and.returnValue(Promise.resolve('16.0.0')); + + return updateIconsWithConfig('single-variants.xml') + .then(() => { + expect(updatePaths).toHaveBeenCalledWith({ + [path.join(iconsDir, 'icon.png')]: 'res/ios/appicon.png', + [path.join(iconsDir, 'icon-dark.png')]: 'res/ios/appicon-dark.png', + [path.join(iconsDir, 'icon-tinted.png')]: 'res/ios/appicon-tint.png', + [path.join(iconsDir, 'watchos.png')]: 'res/ios/appicon.png' + }, { rootDir: iosProject }, logFileOp); + + // verify that that Contents.json is as we expect + const result = JSON.parse(fs.readFileSync(path.join(iosProject, iconsDir, 'Contents.json'))); + expect(result).toEqual(require('./fixtures/icon-support/contents-json/single-variants')); + }); }); it('should update paths if multiple icon sizes are specified', () => { const updatePaths = spyOn(FileUpdater, 'updatePaths'); - updateIconsWithConfig('multi.xml'); - - expect(updatePaths).toHaveBeenCalledWith({ - [path.join(iconsDir, 'icon.png')]: 'res/ios/AppIcon-1024x1024@1x.png', - [path.join(iconsDir, 'watchos.png')]: 'res/ios/AppIcon-1024x1024@1x.png', - [path.join(iconsDir, 'icon-20@2x.png')]: 'res/ios/AppIcon-20x20@2x.png', - [path.join(iconsDir, 'icon-20@3x.png')]: 'res/ios/AppIcon-20x20@3x.png', - [path.join(iconsDir, 'icon-29@2x.png')]: 'res/ios/AppIcon-29x29@2x.png', - [path.join(iconsDir, 'icon-29@3x.png')]: 'res/ios/AppIcon-29x29@3x.png', - [path.join(iconsDir, 'icon-38@2x.png')]: 'res/ios/AppIcon-38x38@2x.png', - [path.join(iconsDir, 'icon-38@3x.png')]: 'res/ios/AppIcon-38x38@3x.png', - [path.join(iconsDir, 'icon-40@2x.png')]: 'res/ios/AppIcon-40x40@2x.png', - [path.join(iconsDir, 'icon-40@3x.png')]: 'res/ios/AppIcon-40x40@3x.png', - [path.join(iconsDir, 'icon-60@2x.png')]: 'res/ios/AppIcon-60x60@2x.png', - [path.join(iconsDir, 'icon-60@3x.png')]: 'res/ios/AppIcon-60x60@3x.png', - [path.join(iconsDir, 'icon-64@2x.png')]: 'res/ios/AppIcon-64x64@2x.png', - [path.join(iconsDir, 'icon-64@3x.png')]: 'res/ios/AppIcon-64x64@3x.png', - [path.join(iconsDir, 'icon-68@2x.png')]: 'res/ios/AppIcon-68x68@2x.png', - [path.join(iconsDir, 'icon-76@2x.png')]: 'res/ios/AppIcon-76x76@2x.png', - [path.join(iconsDir, 'icon-83.5@2x.png')]: 'res/ios/AppIcon-83.5x83.5@2x.png' - }, { rootDir: iosProject }, logFileOp); - - // verify that that Contents.json is as we expect - const result = JSON.parse(fs.readFileSync(path.join(iosProject, iconsDir, 'Contents.json'))); - expect(result).toEqual(require('./fixtures/icon-support/contents-json/multi')); + return updateIconsWithConfig('multi.xml') + .then(() => { + expect(updatePaths).toHaveBeenCalledWith({ + [path.join(iconsDir, 'icon.png')]: 'res/ios/AppIcon-1024x1024@1x.png', + [path.join(iconsDir, 'watchos.png')]: 'res/ios/AppIcon-1024x1024@1x.png', + [path.join(iconsDir, 'icon-20@2x.png')]: 'res/ios/AppIcon-20x20@2x.png', + [path.join(iconsDir, 'icon-20@3x.png')]: 'res/ios/AppIcon-20x20@3x.png', + [path.join(iconsDir, 'icon-29@2x.png')]: 'res/ios/AppIcon-29x29@2x.png', + [path.join(iconsDir, 'icon-29@3x.png')]: 'res/ios/AppIcon-29x29@3x.png', + [path.join(iconsDir, 'icon-38@2x.png')]: 'res/ios/AppIcon-38x38@2x.png', + [path.join(iconsDir, 'icon-38@3x.png')]: 'res/ios/AppIcon-38x38@3x.png', + [path.join(iconsDir, 'icon-40@2x.png')]: 'res/ios/AppIcon-40x40@2x.png', + [path.join(iconsDir, 'icon-40@3x.png')]: 'res/ios/AppIcon-40x40@3x.png', + [path.join(iconsDir, 'icon-60@2x.png')]: 'res/ios/AppIcon-60x60@2x.png', + [path.join(iconsDir, 'icon-60@3x.png')]: 'res/ios/AppIcon-60x60@3x.png', + [path.join(iconsDir, 'icon-64@2x.png')]: 'res/ios/AppIcon-64x64@2x.png', + [path.join(iconsDir, 'icon-64@3x.png')]: 'res/ios/AppIcon-64x64@3x.png', + [path.join(iconsDir, 'icon-68@2x.png')]: 'res/ios/AppIcon-68x68@2x.png', + [path.join(iconsDir, 'icon-76@2x.png')]: 'res/ios/AppIcon-76x76@2x.png', + [path.join(iconsDir, 'icon-83.5@2x.png')]: 'res/ios/AppIcon-83.5x83.5@2x.png' + }, { rootDir: iosProject }, logFileOp); + + // verify that that Contents.json is as we expect + const result = JSON.parse(fs.readFileSync(path.join(iosProject, iconsDir, 'Contents.json'))); + expect(result).toEqual(require('./fixtures/icon-support/contents-json/multi')); + }); }); }); @@ -674,27 +711,29 @@ describe('prepare', () => { // now, clean the images const updatePaths = spyOn(FileUpdater, 'updatePaths'); - cleanIcons(iosProject, project.projectConfig, p.locations); - - expect(updatePaths).toHaveBeenCalledWith({ - [path.join(iconsDir, 'icon.png')]: null, - [path.join(iconsDir, 'watchos.png')]: null, - [path.join(iconsDir, 'icon-20@2x.png')]: null, - [path.join(iconsDir, 'icon-20@3x.png')]: null, - [path.join(iconsDir, 'icon-29@2x.png')]: null, - [path.join(iconsDir, 'icon-29@3x.png')]: null, - [path.join(iconsDir, 'icon-38@2x.png')]: null, - [path.join(iconsDir, 'icon-38@3x.png')]: null, - [path.join(iconsDir, 'icon-40@2x.png')]: null, - [path.join(iconsDir, 'icon-40@3x.png')]: null, - [path.join(iconsDir, 'icon-60@2x.png')]: null, - [path.join(iconsDir, 'icon-60@3x.png')]: null, - [path.join(iconsDir, 'icon-64@2x.png')]: null, - [path.join(iconsDir, 'icon-64@3x.png')]: null, - [path.join(iconsDir, 'icon-68@2x.png')]: null, - [path.join(iconsDir, 'icon-76@2x.png')]: null, - [path.join(iconsDir, 'icon-83.5@2x.png')]: null - }, { rootDir: iosProject, all: true }, logFileOp); + + return cleanIcons(iosProject, project.projectConfig, p.locations) + .then(() => { + expect(updatePaths).toHaveBeenCalledWith({ + [path.join(iconsDir, 'icon.png')]: null, + [path.join(iconsDir, 'watchos.png')]: null, + [path.join(iconsDir, 'icon-20@2x.png')]: null, + [path.join(iconsDir, 'icon-20@3x.png')]: null, + [path.join(iconsDir, 'icon-29@2x.png')]: null, + [path.join(iconsDir, 'icon-29@3x.png')]: null, + [path.join(iconsDir, 'icon-38@2x.png')]: null, + [path.join(iconsDir, 'icon-38@3x.png')]: null, + [path.join(iconsDir, 'icon-40@2x.png')]: null, + [path.join(iconsDir, 'icon-40@3x.png')]: null, + [path.join(iconsDir, 'icon-60@2x.png')]: null, + [path.join(iconsDir, 'icon-60@3x.png')]: null, + [path.join(iconsDir, 'icon-64@2x.png')]: null, + [path.join(iconsDir, 'icon-64@3x.png')]: null, + [path.join(iconsDir, 'icon-68@2x.png')]: null, + [path.join(iconsDir, 'icon-76@2x.png')]: null, + [path.join(iconsDir, 'icon-83.5@2x.png')]: null + }, { rootDir: iosProject, all: true }, logFileOp); + }); }); it('should have no effect if no icons are specified', () => { @@ -713,9 +752,11 @@ describe('prepare', () => { // now, clean the images const updatePaths = spyOn(FileUpdater, 'updatePaths'); - cleanIcons(iosProject, project.projectConfig, p.locations); - expect(updatePaths).not.toHaveBeenCalled(); + return cleanIcons(iosProject, project.projectConfig, p.locations) + .then(() => { + expect(updatePaths).not.toHaveBeenCalled(); + }); }); }); });