diff --git a/ChangeLog.md b/ChangeLog.md index 0a0bdf29..a3e42176 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,6 +1,7 @@ #2.2.15 * Ensure that standard slots are repainted when any component changes * Reload page if Safari throws a security error + * Handle import of ships with incorrectly-sized slots #2.2.14 * Ensure that jitter is shown correctly when the result of a special effect diff --git a/__tests__/fixtures/companion-api-import-3.json b/__tests__/fixtures/companion-api-import-3.json new file mode 100644 index 00000000..88de0f55 --- /dev/null +++ b/__tests__/fixtures/companion-api-import-3.json @@ -0,0 +1,314 @@ +{ + "cargo": { + "capacity": 264 + }, + "free": false, + "fuel": { + "main": { + "capacity": 32 + }, + "reserve": { + "capacity": 0.52 + } + }, + "id": 4, + "modules": { + "Armour": { + "module": { + "free": false, + "id": 128049298, + "name": "Type7_Armour_Grade1", + "on": true, + "priority": 1, + "unloaned": 0, + "value": 0 + } + }, + "Bobble01": [], + "Bobble02": [], + "Bobble03": [], + "Bobble04": [], + "Bobble05": [], + "Bobble06": [], + "Bobble07": [], + "Bobble08": [], + "Bobble09": [], + "Bobble10": [], + "Decal1": { + "module": { + "free": false, + "id": 128667746, + "name": "Decal_Trade_Dealer", + "on": true, + "priority": 1, + "unloaned": 0, + "value": 0 + } + }, + "Decal2": { + "module": { + "free": false, + "id": 128667738, + "name": "Decal_Combat_Competent", + "on": true, + "priority": 1, + "unloaned": 0, + "value": 0 + } + }, + "Decal3": { + "module": { + "free": false, + "id": 128667753, + "name": "Decal_Explorer_Scout", + "on": true, + "priority": 1, + "unloaned": 0, + "value": 0 + } + }, + "EngineColour": [], + "FrameShiftDrive": { + "module": { + "free": false, + "id": 128064122, + "name": "Int_Hyperdrive_Size5_Class5", + "on": true, + "priority": 0, + "unloaned": 0, + "value": 5103953 + } + }, + "FuelTank": { + "module": { + "free": false, + "id": 128064350, + "name": "Int_FuelTank_Size5_Class3", + "on": true, + "priority": 1, + "unloaned": 97754, + "value": 97754 + } + }, + "LifeSupport": { + "module": { + "free": false, + "id": 128064154, + "name": "Int_LifeSupport_Size4_Class2", + "on": true, + "priority": 0, + "unloaned": 0, + "value": 28373 + } + }, + "MainEngines": { + "module": { + "free": false, + "id": 128064087, + "name": "Int_Engine_Size5_Class5", + "on": true, + "priority": 0, + "unloaned": 0, + "value": 5103953 + } + }, + "PaintJob": { + "module": { + "free": false, + "id": 128671422, + "name": "PaintJob_Type7_Tactical_White", + "on": true, + "priority": 1, + "unloaned": 0, + "value": 0 + } + }, + "PlanetaryApproachSuite": { + "module": { + "free": false, + "id": 128672317, + "name": "Int_PlanetApproachSuite", + "on": true, + "priority": 1, + "unloaned": 500, + "value": 500 + } + }, + "PowerDistributor": { + "module": { + "free": false, + "id": 128064192, + "name": "Int_PowerDistributor_Size3_Class5", + "on": true, + "priority": 0, + "unloaned": 0, + "value": 158331 + } + }, + "PowerPlant": { + "module": { + "free": false, + "id": 128064047, + "name": "Int_Powerplant_Size4_Class5", + "on": true, + "priority": 1, + "unloaned": 0, + "value": 1610080 + } + }, + "Radar": { + "module": { + "free": false, + "id": 128064229, + "name": "Int_Sensors_Size3_Class2", + "on": true, + "priority": 0, + "unloaned": 0, + "value": 10133 + } + }, + "Slot01_Size6": { + "module": { + "free": false, + "id": 128064343, + "name": "Int_CargoRack_Size6_Class1", + "on": true, + "priority": 1, + "unloaned": 0, + "value": 362591 + } + }, + "Slot02_Size6": { + "module": { + "free": false, + "id": 128064343, + "name": "Int_CargoRack_Size6_Class1", + "on": true, + "priority": 1, + "unloaned": 0, + "value": 362591 + } + }, + "Slot03_Size5": { + "module": { + "free": false, + "id": 128064343, + "name": "Int_CargoRack_Size6_Class1", + "on": true, + "priority": 1, + "unloaned": 0, + "value": 362591 + } + }, + "Slot04_Size5": { + "module": { + "free": false, + "id": 128064342, + "name": "Int_CargoRack_Size5_Class1", + "on": true, + "priority": 1, + "unloaned": 111566, + "value": 111566 + } + }, + "Slot05_Size4": { + "module": { + "free": false, + "id": 128064342, + "name": "Int_CargoRack_Size5_Class1", + "on": true, + "priority": 1, + "unloaned": 111566, + "value": 111566 + } + }, + "Slot06_Size4": { + "module": { + "free": false, + "id": 128064279, + "name": "Int_ShieldGenerator_Size5_Class2", + "on": true, + "priority": 0, + "unloaned": 0, + "value": 189035 + } + }, + "Slot07_Size2": { + "module": { + "free": false, + "id": 128049549, + "name": "Int_DockingComputer_Standard", + "on": true, + "priority": 0, + "unloaned": 0, + "value": 4500 + } + }, + "Slot08_Size2": { + "module": { + "free": false, + "id": 128064340, + "name": "Int_CargoRack_Size3_Class1", + "on": true, + "priority": 1, + "unloaned": 0, + "value": 10563 + } + }, + "SmallHardpoint1": [], + "SmallHardpoint2": [], + "SmallHardpoint3": [], + "SmallHardpoint4": [], + "TinyHardpoint1": { + "module": { + "free": false, + "id": 128668536, + "name": "Hpt_ShieldBooster_Size0_Class5", + "on": true, + "priority": 0, + "unloaned": 0, + "value": 281000 + } + }, + "TinyHardpoint2": { + "module": { + "free": false, + "id": 128668536, + "name": "Hpt_ShieldBooster_Size0_Class5", + "on": true, + "priority": 0, + "unloaned": 0, + "value": 281000 + } + }, + "TinyHardpoint3": { + "module": { + "free": false, + "id": 128668536, + "name": "Hpt_ShieldBooster_Size0_Class5", + "on": true, + "priority": 0, + "unloaned": 0, + "value": 281000 + } + }, + "TinyHardpoint4": { + "module": { + "free": false, + "id": 128049513, + "name": "Hpt_ChaffLauncher_Tiny", + "on": true, + "priority": 0, + "unloaned": 0, + "value": 8500 + } + }, + "WeaponColour": [] + }, + "name": "Type7", + "value": { + "hull": 16780009, + "modules": 14479580, + "unloaned": 321386 + } +} diff --git a/__tests__/test-import.js b/__tests__/test-import.js index 9a069359..19480ad8 100644 --- a/__tests__/test-import.js +++ b/__tests__/test-import.js @@ -229,7 +229,7 @@ describe('Import Modal', function() { beforeEach(reset); - it('imports a valid v4 build', function() { + it('imports a valid companion API build', function() { const importData = require('./fixtures/companion-api-import-1'); pasteText(JSON.stringify(importData)); @@ -241,7 +241,7 @@ describe('Import Modal', function() { expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/federal_corvette?code=A2putsFklndzsxf50x0x7l28281919040404040402020l06p05sf63c5ifr--v66g2f.AwRj4zNaqA%3D%3D.CwRgDBldUExuBiIlUA%3D%3D.H4sIAAAAAAAAA02SPy9DURjG3%2F65vW1v47TXVbeqqF7EQtIIBomRJswsYmISH8BgkFhqFZ9AwlALMYitkXQyEF2k4SMYJNK0dV7PK7nc5ck55%2Fm9z%2FnznpBeJqLvECQbM4hUjZnjO5hyWGfFikAGGjGiku0QuddhQCNdZmdWM9snsDmih4REOdlnNvz9DrPrJIicPdSwoZf8pAnTIpq8x7DYADS%2Bi5DERY85%2BYqpmkc6x%2FWGf6beKCR3YBIZFZCxCgrtczjuOmo4qTf94F4KYuxhz5jjEhXmUJNexFrpIUo02ALN1j9u1JMgD%2FMga1GfbMNRd9iHUwGy%2BpspZF3IBSGvMFJluS%2FuR24FJ2KlV%2Fxju6sQq4lhRsQTUVUJTgegLtS6EUjEE1HPAmUC0KdAjwKJeCKqD8zoURx72gHyDW9nvQhJGHkyUscS1x%2BAZnAlqwU%2FI%2BKJKEvextXrf93eQrR1KUlS5HWwGC61mfOn0oN3IM4OHoBzuuIHj33hS5jT8KeamIYa0sjhgH%2BLfplP4kcwD5Xl3xR1wfeHtqWzBHHX8I9SH9Je%2FgGvXxeungIAAA%3D%3D&bn=Imported%20Federal%20Corvette'); }); - it('imports a valid v4 build', function() { + it('imports a valid companion API build', function() { const importData = require('./fixtures/companion-api-import-2'); pasteText(JSON.stringify(importData)); @@ -252,6 +252,18 @@ describe('Import Modal', function() { expect(MockRouter.go.mock.calls.length).toBe(1); expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/beluga?code=A0pktsFplCdpsnf70t0t2727270004040404043c4fmimlmm04mc0iv62i2f.AwRj4yukg%3D%3D%3D.CwRgDBldHi8IUA%3D%3D.H4sIAAAAAAAAA2P8Z8%2FAwPCXEUiIKTMxMPCv%2F%2Ff%2FP8cFIPGf6Z8YTEr0GjMDg%2FJWICERBOTzn%2Fn7%2F7%2FIO5Ai5n9SIEWsQEIoSxAolfbt%2F3%2BJPk4GBhE7YQYGYVmgcuVnf4Aq%2FwMAIrEcGGsAAAA%3D&bn=Imported%20Beluga%20Liner'); }); + + it('imports a valid companion API build', function() { + const importData = require('./fixtures/companion-api-import-3'); + pasteText(JSON.stringify(importData)); + + expect(modal.state.importValid).toBeTruthy(); + expect(modal.state.errorMsg).toEqual(null); + expect(modal.state.singleBuild).toBe(true); + clickProceed(); + expect(MockRouter.go.mock.calls.length).toBe(1); + expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/type_7_transport?code=A0patfFflidasdf5----0404040005050504044d2402.AwRj4yrI.CwRgDBlVK7EiA%3D%3D%3D.&bn=Imported%20Type-7%20Transporter'); + }); }); describe('Import E:D Shipyard Builds', function() { diff --git a/src/app/utils/CompanionApiUtils.js b/src/app/utils/CompanionApiUtils.js index 674ef674..c55e334a 100644 --- a/src/app/utils/CompanionApiUtils.js +++ b/src/app/utils/CompanionApiUtils.js @@ -253,11 +253,15 @@ export function shipFromJson(json) { internalSlot = json.modules[internalName]; militarySlotNum++; } else { + // Slot numbers are not contiguous so handle skips. while (internalSlot === null && internalSlotNum < 99) { - // Slot numbers are not contiguous so handle skips - const internalName = 'Slot' + (internalSlotNum <= 9 ? '0' : '') + internalSlotNum + '_Size' + internalClassNum; - if (json.modules[internalName]) { - internalSlot = json.modules[internalName]; + // Slot sizes have no relationship to the actual size, either, so check all possibilities + for (let slotsize = 0; slotsize < 9; slotsize++) { + const internalName = 'Slot' + (internalSlotNum <= 9 ? '0' : '') + internalSlotNum + '_Size' + slotsize; + if (json.modules[internalName]) { + internalSlot = json.modules[internalName]; + break; + } } internalSlotNum++; }