Skip to content

Commit c678d01

Browse files
committed
releases: allow multiple releases, added tests for release script funcs
1 parent 6144b5c commit c678d01

File tree

2 files changed

+449
-63
lines changed

2 files changed

+449
-63
lines changed

scripts/__tests__/releases.test.js

Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
// @flow
2+
/* eslint-disable */
3+
4+
/**
5+
* Simple test file for releases.js functions
6+
* Tests the individual utility functions without complex mocking
7+
*/
8+
9+
describe('Releases Script Utility Functions', () => {
10+
// Define the functions locally for testing
11+
// These are copies of the functions from releases.js
12+
13+
/**
14+
* Extract plugin name from tag (removes version suffix like -v1.0.0)
15+
*/
16+
function extractPluginNameFromTag(tagName) {
17+
return tagName.replace(/-v\d+(\.\d+)*(\.\d+)*(?:-[a-zA-Z0-9.-]+)?$/, '')
18+
}
19+
20+
/**
21+
* Extract version from tag
22+
*/
23+
function extractVersionFromTag(tagName) {
24+
const match = tagName.match(/-v(\d+(?:\.\d+)*(?:\.\d+)*(?:-[a-zA-Z0-9.-]+)?)$/)
25+
return match ? match[1] : null
26+
}
27+
28+
/**
29+
* Generate release tag name from plugin name and version
30+
*/
31+
function getReleaseTagName(pluginName, version) {
32+
return `${pluginName}-v${version}`
33+
}
34+
35+
/**
36+
* Get a field value from plugin data
37+
*/
38+
function getPluginDataField(pluginData, field) {
39+
if (!pluginData || typeof pluginData !== 'object') {
40+
console.log(`Could not find value for "${field}" in plugin.json`)
41+
process.exit(0)
42+
return null // This line won't be reached due to process.exit, but added for completeness
43+
}
44+
const data = pluginData[field] || null
45+
if (!data) {
46+
console.log(`Could not find value for "${field}" in plugin.json`)
47+
process.exit(0)
48+
return null // This line won't be reached due to process.exit, but added for completeness
49+
}
50+
return data
51+
}
52+
53+
/**
54+
* Ensure the version being released is new and not a duplicate
55+
*/
56+
function ensureVersionIsNew(existingReleases, versionedTagName) {
57+
if (existingReleases && existingReleases.length > 0 && versionedTagName) {
58+
const duplicateRelease = existingReleases.find((release) => release.tag === versionedTagName)
59+
if (duplicateRelease) {
60+
return false // Duplicate found
61+
}
62+
}
63+
return true // No duplicate found
64+
}
65+
66+
describe('extractPluginNameFromTag', () => {
67+
test('should extract plugin name from tag with version', () => {
68+
expect(extractPluginNameFromTag('dwertheimer.TaskAutomations-v1.0.0')).toBe('dwertheimer.TaskAutomations')
69+
expect(extractPluginNameFromTag('jgclark.Dashboard-v2.1.3')).toBe('jgclark.Dashboard')
70+
expect(extractPluginNameFromTag('np.Templating-v3.0.0')).toBe('np.Templating')
71+
})
72+
73+
test('should handle different version formats', () => {
74+
expect(extractPluginNameFromTag('plugin.name-v1')).toBe('plugin.name')
75+
expect(extractPluginNameFromTag('plugin.name-v1.0')).toBe('plugin.name')
76+
expect(extractPluginNameFromTag('plugin.name-v1.0.0')).toBe('plugin.name')
77+
expect(extractPluginNameFromTag('plugin.name-v1.2.3.4')).toBe('plugin.name')
78+
})
79+
80+
test('should return original string if no version pattern found', () => {
81+
expect(extractPluginNameFromTag('plugin.name')).toBe('plugin.name')
82+
expect(extractPluginNameFromTag('plugin-name')).toBe('plugin-name')
83+
expect(extractPluginNameFromTag('plugin_name')).toBe('plugin_name')
84+
})
85+
86+
test('should handle edge cases', () => {
87+
expect(extractPluginNameFromTag('')).toBe('')
88+
expect(extractPluginNameFromTag('v1.0.0')).toBe('v1.0.0') // No plugin name
89+
})
90+
})
91+
92+
describe('extractVersionFromTag', () => {
93+
test('should extract version from tag', () => {
94+
expect(extractVersionFromTag('dwertheimer.TaskAutomations-v1.0.0')).toBe('1.0.0')
95+
expect(extractVersionFromTag('jgclark.Dashboard-v2.1.3')).toBe('2.1.3')
96+
expect(extractVersionFromTag('np.Templating-v3.0.0')).toBe('3.0.0')
97+
})
98+
99+
test('should handle different version formats', () => {
100+
expect(extractVersionFromTag('plugin.name-v1')).toBe('1')
101+
expect(extractVersionFromTag('plugin.name-v1.0')).toBe('1.0')
102+
expect(extractVersionFromTag('plugin.name-v1.0.0')).toBe('1.0.0')
103+
expect(extractVersionFromTag('plugin.name-v1.2.3.4')).toBe('1.2.3.4')
104+
})
105+
106+
test('should handle pre-release versions', () => {
107+
expect(extractVersionFromTag('plugin.name-v1.0.0-beta.1')).toBe('1.0.0-beta.1')
108+
expect(extractVersionFromTag('plugin.name-v2.0.0-alpha')).toBe('2.0.0-alpha')
109+
expect(extractVersionFromTag('plugin.name-v1.0.0-rc.1')).toBe('1.0.0-rc.1')
110+
})
111+
112+
test('should return null if no version pattern found', () => {
113+
expect(extractVersionFromTag('plugin.name')).toBeNull()
114+
expect(extractVersionFromTag('plugin-name')).toBeNull()
115+
expect(extractVersionFromTag('plugin_name')).toBeNull()
116+
expect(extractVersionFromTag('')).toBeNull()
117+
})
118+
119+
test('should handle edge cases', () => {
120+
expect(extractVersionFromTag('v1.0.0')).toBeNull() // No plugin name
121+
expect(extractVersionFromTag('plugin-v')).toBeNull() // Incomplete version
122+
expect(extractVersionFromTag('plugin-v.1.0')).toBeNull() // Invalid version format
123+
})
124+
})
125+
126+
describe('getReleaseTagName', () => {
127+
test('should generate correct release tag name', () => {
128+
expect(getReleaseTagName('dwertheimer.TaskAutomations', '1.0.0')).toBe('dwertheimer.TaskAutomations-v1.0.0')
129+
expect(getReleaseTagName('jgclark.Dashboard', '2.1.3')).toBe('jgclark.Dashboard-v2.1.3')
130+
expect(getReleaseTagName('np.Templating', '3.0.0')).toBe('np.Templating-v3.0.0')
131+
})
132+
133+
test('should handle different version formats', () => {
134+
expect(getReleaseTagName('plugin.name', '1')).toBe('plugin.name-v1')
135+
expect(getReleaseTagName('plugin.name', '1.0')).toBe('plugin.name-v1.0')
136+
expect(getReleaseTagName('plugin.name', '1.0.0')).toBe('plugin.name-v1.0.0')
137+
})
138+
139+
test('should handle edge cases', () => {
140+
expect(getReleaseTagName('', '1.0.0')).toBe('-v1.0.0')
141+
expect(getReleaseTagName('plugin.name', '')).toBe('plugin.name-v')
142+
})
143+
})
144+
145+
describe('getPluginDataField', () => {
146+
const mockPluginData = {
147+
'plugin.name': 'Test Plugin',
148+
'plugin.version': '1.0.0',
149+
'plugin.description': 'A test plugin',
150+
'plugin.author': 'Test Author',
151+
}
152+
153+
// Mock process.exit to prevent actual exit during tests
154+
const originalExit = process.exit
155+
beforeAll(() => {
156+
process.exit = jest.fn()
157+
})
158+
159+
beforeEach(() => {
160+
process.exit.mockClear()
161+
})
162+
163+
afterAll(() => {
164+
process.exit = originalExit
165+
})
166+
167+
test('should return field value when field exists', () => {
168+
expect(getPluginDataField(mockPluginData, 'plugin.name')).toBe('Test Plugin')
169+
expect(getPluginDataField(mockPluginData, 'plugin.version')).toBe('1.0.0')
170+
expect(getPluginDataField(mockPluginData, 'plugin.description')).toBe('A test plugin')
171+
})
172+
173+
test('should call process.exit for non-existent fields', () => {
174+
getPluginDataField(mockPluginData, 'plugin.nonexistent')
175+
expect(process.exit).toHaveBeenCalledWith(0)
176+
})
177+
178+
test('should handle empty plugin data', () => {
179+
// Test with empty object
180+
getPluginDataField({}, 'plugin.name')
181+
expect(process.exit).toHaveBeenCalledWith(0)
182+
183+
// Reset the mock for next calls
184+
process.exit.mockClear()
185+
186+
// Test with null
187+
getPluginDataField(null, 'plugin.name')
188+
expect(process.exit).toHaveBeenCalledWith(0)
189+
190+
// Reset the mock for next calls
191+
process.exit.mockClear()
192+
193+
// Test with undefined
194+
getPluginDataField(undefined, 'plugin.name')
195+
expect(process.exit).toHaveBeenCalledWith(0)
196+
})
197+
198+
test('should handle falsy values correctly', () => {
199+
const dataWithFalsyValues = {
200+
'plugin.name': '',
201+
'plugin.version': 0,
202+
'plugin.enabled': false,
203+
'plugin.null': null,
204+
}
205+
206+
getPluginDataField(dataWithFalsyValues, 'plugin.name') // Empty string
207+
expect(process.exit).toHaveBeenCalledWith(0)
208+
209+
getPluginDataField(dataWithFalsyValues, 'plugin.version') // 0
210+
expect(process.exit).toHaveBeenCalledWith(0)
211+
212+
getPluginDataField(dataWithFalsyValues, 'plugin.enabled') // false
213+
expect(process.exit).toHaveBeenCalledWith(0)
214+
215+
getPluginDataField(dataWithFalsyValues, 'plugin.null') // null
216+
expect(process.exit).toHaveBeenCalledWith(0)
217+
})
218+
})
219+
220+
describe('ensureVersionIsNew', () => {
221+
const mockReleases = [
222+
{ name: 'test.plugin', tag: 'test.plugin-v1.0.0', version: '1.0.0', publishedAt: '2023-01-01T00:00:00Z' },
223+
{ name: 'test.plugin', tag: 'test.plugin-v1.1.0', version: '1.1.0', publishedAt: '2023-02-01T00:00:00Z' },
224+
{ name: 'test.plugin', tag: 'test.plugin-v2.0.0', version: '2.0.0', publishedAt: '2023-03-01T00:00:00Z' },
225+
]
226+
227+
test('should return true for new version', () => {
228+
expect(ensureVersionIsNew(mockReleases, 'test.plugin-v1.2.0')).toBe(true)
229+
expect(ensureVersionIsNew(mockReleases, 'test.plugin-v3.0.0')).toBe(true)
230+
expect(ensureVersionIsNew(mockReleases, 'different.plugin-v1.0.0')).toBe(true)
231+
})
232+
233+
test('should return false for duplicate version', () => {
234+
expect(ensureVersionIsNew(mockReleases, 'test.plugin-v1.0.0')).toBe(false)
235+
expect(ensureVersionIsNew(mockReleases, 'test.plugin-v1.1.0')).toBe(false)
236+
expect(ensureVersionIsNew(mockReleases, 'test.plugin-v2.0.0')).toBe(false)
237+
})
238+
239+
test('should handle empty or null releases array', () => {
240+
expect(ensureVersionIsNew(null, 'test.plugin-v1.0.0')).toBe(true)
241+
expect(ensureVersionIsNew(undefined, 'test.plugin-v1.0.0')).toBe(true)
242+
expect(ensureVersionIsNew([], 'test.plugin-v1.0.0')).toBe(true)
243+
})
244+
245+
test('should handle empty versionedTagName', () => {
246+
expect(ensureVersionIsNew(mockReleases, '')).toBe(true)
247+
expect(ensureVersionIsNew(mockReleases, null)).toBe(true)
248+
expect(ensureVersionIsNew(mockReleases, undefined)).toBe(true)
249+
})
250+
251+
test('should handle releases with different plugin names', () => {
252+
const mixedReleases = [
253+
{ name: 'plugin1', tag: 'plugin1-v1.0.0', version: '1.0.0', publishedAt: '2023-01-01T00:00:00Z' },
254+
{ name: 'plugin2', tag: 'plugin2-v1.0.0', version: '1.0.0', publishedAt: '2023-01-01T00:00:00Z' },
255+
]
256+
257+
expect(ensureVersionIsNew(mixedReleases, 'plugin1-v1.0.0')).toBe(false)
258+
expect(ensureVersionIsNew(mixedReleases, 'plugin2-v1.0.0')).toBe(false)
259+
expect(ensureVersionIsNew(mixedReleases, 'plugin1-v1.1.0')).toBe(true)
260+
expect(ensureVersionIsNew(mixedReleases, 'plugin3-v1.0.0')).toBe(true)
261+
})
262+
})
263+
264+
describe('Integration tests', () => {
265+
test('should work together for complete workflow', () => {
266+
const pluginName = 'test.plugin'
267+
const version = '1.2.3'
268+
const tagName = getReleaseTagName(pluginName, version)
269+
270+
expect(tagName).toBe('test.plugin-v1.2.3')
271+
272+
const extractedPluginName = extractPluginNameFromTag(tagName)
273+
const extractedVersion = extractVersionFromTag(tagName)
274+
275+
expect(extractedPluginName).toBe(pluginName)
276+
expect(extractedVersion).toBe(version)
277+
})
278+
279+
test('should handle complex plugin names and versions', () => {
280+
const testCases = [
281+
{ plugin: 'dwertheimer.TaskAutomations', version: '1.0.0' },
282+
{ plugin: 'jgclark.Dashboard', version: '2.1.3' },
283+
{ plugin: 'np.Templating', version: '3.0.0-beta.1' },
284+
{ plugin: 'codedungeon.Toolbox', version: '4.5.6' },
285+
]
286+
287+
testCases.forEach(({ plugin, version }) => {
288+
const tagName = getReleaseTagName(plugin, version)
289+
const extractedPlugin = extractPluginNameFromTag(tagName)
290+
const extractedVersion = extractVersionFromTag(tagName)
291+
292+
expect(extractedPlugin).toBe(plugin)
293+
expect(extractedVersion).toBe(version)
294+
})
295+
})
296+
})
297+
})

0 commit comments

Comments
 (0)