Passed
Push — main ( 0f249b...e7fb5c )
by Eduardo
02:45 queued 12s
created

SoFloC.test.ts ➔ zipBack   F

Complexity

Conditions 20

Size

Total Lines 7
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 7
dl 0
loc 7
c 0
b 0
f 0
rs 0
cc 20

How to fix   Complexity   

Complexity

Complex classes like SoFloC.test.ts ➔ zipBack often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/* eslint-disable @typescript-eslint/no-var-requires */
2
/** global: jest */
3
import { readFileSync } from 'fs'
4
import JSZip, { loadAsync } from 'jszip'
5
import { join } from 'path'
6
import { SoFloC } from './SoFloC'
7
import expectedDataCopy from './_jest/expectedDataCopy.json'
8
import expectedZipCopy from './_jest/expectedZipCopy'
9
import expectedZipDelete from './_jest/expectedZipDelete'
10
const crypto = require('crypto')
11
12
jest.setTimeout(10 * 60 * 1000)
13
14
describe('SoFloC', () => {
15
  let file: Buffer
16
  const name = 'TestSolution_2_0_0_0.zip'
17
  const path = join(__dirname, '_jest', name)
18
19
  beforeEach(() => {
20
    jest.mock('crypto')
21
    crypto.randomUUID = jest.fn()
22
      .mockReturnValueOnce('01234567-xxxx-yyyy-zzzz-987654321098')
23
      .mockReturnValueOnce('12345678-yyyy-zzzz-xxxx-876543210987')
24
      .mockReturnValueOnce('23456789-zzzz-xxxx-yyyy-765432109876')
25
    file = readFileSync(path)
26
  })
27
  describe('copyFlow', () => {
28
    test('happy path', async () => {
29
      const sofloc = new SoFloC(file, name)
30
      await sofloc.copyFlow('f4910f26-8210-ec11-b6e6-002248842287', 'First Copy Flow', '2.1.0.0')
31
      await sofloc.copyFlow('0f48cba9-ef0c-ed11-82e4-000d3a64f6f2', 'Second Copy Flow')
32
      await sofloc.copyFlow('0f48cba9-ef0c-ed11-82e4-000d3a64f6f2', 'Third Copy Flow')
33
34
      const zip = await loadAsync(sofloc.data, { base64: true })
35
      expect(zip.files).toMatchObject(expectedZipCopy)
36
37
      expect(sofloc.name).toEqual('TestSolution_2_1_0_0.zip')
38
      expect(sofloc.version).toEqual('2.1.0.0')
39
40
      for (const key in zip.files) {
41
        if (Object.prototype.hasOwnProperty.call(zip.files, key)) {
42
          if (key !== 'Workflows/') {
43
            const file = zip.files[key]
44
            const data = await file.async('string')
45
46
            const expectedData = readFileSync(join(__dirname, '_jest', 'unzipedCopy', key)).toString()
47
            expect(cleanLineBreak(data)).toEqual(cleanLineBreak(expectedData))
48
          }
49
        }
50
      }
51
52
      expect(sofloc.workflows).toEqual([
53
        { id: '0f48cba9-ef0c-ed11-82e4-000d3a64f6f2', name: 'First Test Flow' },
54
        { id: '23456789-zzzz-xxxx-yyyy-765432109876', name: 'Third Copy Flow' },
55
        { id: '12345678-yyyy-zzzz-xxxx-876543210987', name: 'Second Copy Flow' },
56
        { id: 'f4910f26-8210-ec11-b6e6-002248842287', name: 'Second Test Flow' },
57
        { id: '01234567-xxxx-yyyy-zzzz-987654321098', name: 'First Copy Flow' },
58
      ])
59
    })
60
    test('failed to load', async () => {
61
      const sofloc = new SoFloC(null as any, null as any)
62
      let err: any
63
64
      try {
65
        await sofloc.copyFlow('0f48cba9-ef0c-ed11-82e4-000d3a64f6f2', 'First Copy Flow')
66
      } catch (error) {
67
        err = error
68
      }
69
      expect(err.message).toBe('Failed to unzip the file')
70
    })
71
    test('Workflow not on the solution', async () => {
72
      const zip = await loadAsync(file, { base64: true })
73
      delete zip.files['Workflows/FirstTestFlow-0F48CBA9-EF0C-ED11-82E4-000D3A64F6F2.json']
74
75
      const data = await zipBack(zip)
76
77
      const sofloc = new SoFloC(data, name)
78
79
      let err: any
80
      try {
81
        await sofloc.copyFlow('0f48cba9-ef0c-ed11-82e4-000d3a64f6f2', 'First Copy Flow')
82
      } catch (error) {
83
        err = error
84
      }
85
      expect(err.message).toBe("Workflow file with GUID '0f48cba9-ef0c-ed11-82e4-000d3a64f6f2' does not exist in this Solution or the Solution was changed without updating 'solution.xml' or 'customizations.xml'")
86
    })
87
    test('GUID not found on solution.xml', async () => {
88
      const zip = await loadAsync(file, { base64: true })
89
      let solution = await zip.files['solution.xml'].async('string')
90
      solution = solution.replace('0f48cba9-ef0c-ed11-82e4-000d3a64f6f2', '000000000-aaaa-bbbb-cccc-000000000000')
91
      zip.file('solution.xml', solution)
92
93
      const data = await zipBack(zip)
94
95
      const sofloc = new SoFloC(data, name)
96
97
      let err: any
98
      try {
99
        await sofloc.copyFlow('0f48cba9-ef0c-ed11-82e4-000d3a64f6f2', 'First Copy Flow')
100
      } catch (error) {
101
        err = error
102
      }
103
      expect(err.message).toBe("Workflow file with GUID '0f48cba9-ef0c-ed11-82e4-000d3a64f6f2' does not exist in this Solution or the Solution was changed without updating 'solution.xml' or 'customizations.xml'")
104
    })
105
    test('GUID not found on customizations.xml', async () => {
106
      const zip = await loadAsync(file, { base64: true })
107
      let customisations = await zip.files['customizations.xml'].async('string')
108
      customisations = customisations.replace('0f48cba9-ef0c-ed11-82e4-000d3a64f6f2', '000000000-aaaa-bbbb-cccc-000000000000')
109
      zip.file('customizations.xml', customisations)
110
111
      const data = await zipBack(zip)
112
113
      const sofloc = new SoFloC(data, name)
114
115
      let err: any
116
      try {
117
        await sofloc.copyFlow('0f48cba9-ef0c-ed11-82e4-000d3a64f6f2', 'First Copy Flow')
118
      } catch (error) {
119
        err = error
120
      }
121
      expect(err.message).toBe("Workflow file with GUID '0f48cba9-ef0c-ed11-82e4-000d3a64f6f2' does not exist in this Solution or the Solution was changed without updating 'solution.xml' or 'customizations.xml'")
122
    })
123
  })
124
  describe('deleteFlow', () => {
125
    test('happy path', async () => {
126
      const sofloc = new SoFloC(file, name)
127
      await sofloc.deleteFlow('f4910f26-8210-ec11-b6e6-002248842287')
128
129
      const zip = await loadAsync(sofloc.data, { base64: true })
130
      expect(zip.files).toMatchObject(expectedZipDelete)
131
132
      expect(sofloc.name).toEqual('TestSolution_2_0_0_0.zip')
133
      expect(sofloc.version).toEqual('2.0.0.0')
134
135
      for (const key in zip.files) {
136
        if (Object.prototype.hasOwnProperty.call(zip.files, key)) {
137
          if (key !== 'Workflows/') {
138
            const file = zip.files[key]
139
            const data = await file.async('string')
140
141
            const expectedData = readFileSync(join(__dirname, '_jest', 'unzipedDelete', key)).toString()
142
            expect(cleanLineBreak(data)).toEqual(cleanLineBreak(expectedData))
143
          }
144
        }
145
      }
146
147
      expect(sofloc.workflows).toEqual([
148
        { id: '0f48cba9-ef0c-ed11-82e4-000d3a64f6f2', name: 'First Test Flow' },
149
      ])
150
    })
151
    test('failed to load', async () => {
152
      const sofloc = new SoFloC(null as any, null as any)
153
      let err: any
154
155
      try {
156
        await sofloc.deleteFlow('0f48cba9-ef0c-ed11-82e4-000d3a64f6f2')
157
      } catch (error) {
158
        err = error
159
      }
160
      expect(err.message).toBe('Failed to unzip the file')
161
    })
162
    test('Workflow not on the solution', async () => {
163
      const zip = await loadAsync(file, { base64: true })
164
      delete zip.files['Workflows/FirstTestFlow-0F48CBA9-EF0C-ED11-82E4-000D3A64F6F2.json']
165
166
      const data = await zipBack(zip)
167
168
      const sofloc = new SoFloC(data, name)
169
170
      let err: any
171
      try {
172
        await sofloc.deleteFlow('0f48cba9-ef0c-ed11-82e4-000d3a64f6f2')
173
      } catch (error) {
174
        err = error
175
      }
176
      expect(err.message).toBe("Workflow file with GUID '0f48cba9-ef0c-ed11-82e4-000d3a64f6f2' does not exist in this Solution or the Solution was changed without updating 'solution.xml' or 'customizations.xml'")
177
    })
178
    test('GUID not found on solution.xml', async () => {
179
      const zip = await loadAsync(file, { base64: true })
180
      let solution = await zip.files['solution.xml'].async('string')
181
      solution = solution.replace('0f48cba9-ef0c-ed11-82e4-000d3a64f6f2', '000000000-aaaa-bbbb-cccc-000000000000')
182
      zip.file('solution.xml', solution)
183
184
      const data = await zipBack(zip)
185
186
      const sofloc = new SoFloC(data, name)
187
188
      let err: any
189
      try {
190
        await sofloc.deleteFlow('0f48cba9-ef0c-ed11-82e4-000d3a64f6f2')
191
      } catch (error) {
192
        err = error
193
      }
194
      expect(err.message).toBe("Workflow file with GUID '0f48cba9-ef0c-ed11-82e4-000d3a64f6f2' does not exist in this Solution or the Solution was changed without updating 'solution.xml' or 'customizations.xml'")
195
    })
196
    test('GUID not found on customizations.xml', async () => {
197
      const zip = await loadAsync(file, { base64: true })
198
      let customisations = await zip.files['customizations.xml'].async('string')
199
      customisations = customisations.replace('0f48cba9-ef0c-ed11-82e4-000d3a64f6f2', '000000000-aaaa-bbbb-cccc-000000000000')
200
      zip.file('customizations.xml', customisations)
201
202
      const data = await zipBack(zip)
203
204
      const sofloc = new SoFloC(data, name)
205
206
      let err: any
207
      try {
208
        await sofloc.deleteFlow('0f48cba9-ef0c-ed11-82e4-000d3a64f6f2')
209
      } catch (error) {
210
        err = error
211
      }
212
      expect(err.message).toBe("Workflow file with GUID '0f48cba9-ef0c-ed11-82e4-000d3a64f6f2' does not exist in this Solution or the Solution was changed without updating 'solution.xml' or 'customizations.xml'")
213
    })
214
  })
215
  describe('updateVersion', () => {
216
    test('invalid version', async () => {
217
      const sofloc = new SoFloC(file, name)
218
219
      let err: any
220
      try {
221
        await sofloc.updateVersion('1.7.')
222
      } catch (error) {
223
        err = error
224
      }
225
      expect(err.message).toBe('Version \'1.7.\' is not valid. It should follow the format <major>.<minor>.<build>.<revision>.')
226
    })
227
    test('smaller version', async () => {
228
      const sofloc = new SoFloC(file, name)
229
230
      let err: any
231
      try {
232
        await sofloc.updateVersion('1.99.99.99')
233
      } catch (error) {
234
        err = error
235
      }
236
      expect(err.message).toBe('Version \'1.99.99.99\' is smaller than \'2.0.0.0\'')
237
    })
238
  })
239
  describe('get workflows', () => {
240
    test('not loaded', async () => {
241
      const sofloc = new SoFloC(file, name)
242
243
      const wfs = sofloc.workflows
244
      expect(wfs).toEqual([])
245
    })
246
  })
247
  describe('#load', () => {
248
    test('load valid zip', async () => {
249
      const sofloc = new SoFloC(file, name)
250
      await sofloc.updateVersion('2.1.0.0')
251
252
      expect(sofloc).toEqual(expectedDataCopy)
253
      expect(sofloc.workflows).toEqual([
254
        { id: '0f48cba9-ef0c-ed11-82e4-000d3a64f6f2', name: 'First Test Flow' },
255
        { id: 'f4910f26-8210-ec11-b6e6-002248842287', name: 'Second Test Flow' },
256
      ])
257
    })
258
    test('version not found on Solution)', async () => {
259
      const zip = await loadAsync(file, { base64: true })
260
      let solution = await zip.files['solution.xml'].async('string')
261
      solution = solution.replace('<Version>2.0.0.0</Version>', '')
262
      zip.file('solution.xml', solution)
263
264
      const data = await zipBack(zip)
265
      const sofloc = new SoFloC(data, name)
266
267
      let err: any
268
      try {
269
        await sofloc.updateVersion('2.1.0.0')
270
      } catch (error) {
271
        err = error
272
      }
273
      expect(err.message).toBe('Failed to retrieve the version')
274
    })
275
    test('solution.xml not found', async () => {
276
      const zip = await loadAsync(file, { base64: true })
277
      delete zip.files['solution.xml']
278
279
      const data = await zipBack(zip)
280
      const sofloc = new SoFloC(data, name)
281
282
      let err: any
283
      try {
284
        await sofloc.updateVersion('2.1.0.0')
285
      } catch (error) {
286
        err = error
287
      }
288
      expect(err.message).toBe("'solution.xml' was not found in the Solution zip")
289
    })
290
    test('customizations.xml not found', async () => {
291
      const zip = await loadAsync(file, { base64: true })
292
      delete zip.files['customizations.xml']
293
294
      const data = await zipBack(zip)
295
      const sofloc = new SoFloC(data, name)
296
297
      let err: any
298
      try {
299
        await sofloc.updateVersion('2.1.0.0')
300
      } catch (error) {
301
        err = error
302
      }
303
      expect(err.message).toBe("'customizations.xml' was not found in the Solution zip")
304
    })
305
  })
306
})
307
308
async function zipBack (zip: JSZip) {
309
  return await zip.generateAsync({
310
    type:               'base64',
311
    compression:        'DEFLATE',
312
    compressionOptions: {
313
      level: 9,
314
    },
315
  })
316
}
317
318
function cleanLineBreak (content: string) {
319
  return content.replace(/\r\n/gm, '\n')
320
}
321