@sprucelabs/spruce-cli 24.1.2 → 24.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/build/__tests__/behavioral/tests/migrationToInstance/StaticToInstanceTestFileMigrator.test.d.ts +2 -0
- package/build/__tests__/behavioral/tests/migrationToInstance/StaticToInstanceTestFileMigrator.test.js +16 -0
- package/build/__tests__/behavioral/tests/migrationToInstance/StaticToInstanceTestFileMigrator.test.js.map +1 -1
- package/build/__tests__/behavioral/tests/migrationToInstance/support/AbstractInstanceTest2.txt +24 -0
- package/build/__tests__/behavioral/tests/migrationToInstance/support/AbstractStaticTest2.txt +24 -0
- package/build/__tests__/behavioral/tests/migrationToInstance/support/InstanceTest.txt +8 -0
- package/build/__tests__/behavioral/tests/migrationToInstance/support/InstanceTest3.txt +1 -1
- package/build/__tests__/behavioral/tests/migrationToInstance/support/InstanceTest4.txt +520 -0
- package/build/__tests__/behavioral/tests/migrationToInstance/support/StaticTest.txt +8 -0
- package/build/__tests__/behavioral/tests/migrationToInstance/support/StaticTest3.txt +1 -1
- package/build/__tests__/behavioral/tests/migrationToInstance/support/StaticTest4.txt +519 -0
- package/build/tests/staticToInstanceMigration/StaticToInstanceTestFileMigrator.js +17 -13
- package/build/tests/staticToInstanceMigration/StaticToInstanceTestFileMigrator.js.map +1 -1
- package/package.json +3 -3
- package/src/__tests__/behavioral/tests/migrationToInstance/StaticToInstanceTestFileMigrator.test.ts +16 -0
- package/src/__tests__/behavioral/tests/migrationToInstance/support/AbstractInstanceTest2.txt +24 -0
- package/src/__tests__/behavioral/tests/migrationToInstance/support/AbstractStaticTest2.txt +24 -0
- package/src/__tests__/behavioral/tests/migrationToInstance/support/InstanceTest.txt +8 -0
- package/src/__tests__/behavioral/tests/migrationToInstance/support/InstanceTest3.txt +1 -1
- package/src/__tests__/behavioral/tests/migrationToInstance/support/InstanceTest4.txt +520 -0
- package/src/__tests__/behavioral/tests/migrationToInstance/support/StaticTest.txt +8 -0
- package/src/__tests__/behavioral/tests/migrationToInstance/support/StaticTest3.txt +1 -1
- package/src/__tests__/behavioral/tests/migrationToInstance/support/StaticTest4.txt +519 -0
- package/src/tests/staticToInstanceMigration/StaticToInstanceTestFileMigrator.ts +23 -16
|
@@ -8,10 +8,13 @@ import AbstractSpruceTest, {
|
|
|
8
8
|
|
|
9
9
|
@fake.login()
|
|
10
10
|
export default class StaticTestFinderTest extends AbstractSpruceTest {
|
|
11
|
+
private static responseBody = generateId()
|
|
12
|
+
|
|
11
13
|
@test()
|
|
12
14
|
protected static async throwsWithMissing() {
|
|
13
15
|
const finder = StaticTestFinder.Finder()
|
|
14
16
|
const err = await assert.doesThrowAsync(() => finder.find())
|
|
17
|
+
console.log(this.responseBody)
|
|
15
18
|
errorAssert.assertError(err, 'MISSING_PARAMETERS', {
|
|
16
19
|
parameters: ['lookupDir'],
|
|
17
20
|
})
|
|
@@ -21,6 +24,11 @@ export default class StaticTestFinderTest extends AbstractSpruceTest {
|
|
|
21
24
|
protected static async yourNextTest() {
|
|
22
25
|
assert.isTrue(false)
|
|
23
26
|
}
|
|
27
|
+
|
|
28
|
+
@test('this has a description')
|
|
29
|
+
protected static async anotherTest() {
|
|
30
|
+
assert.isFalse(true)
|
|
31
|
+
}
|
|
24
32
|
}
|
|
25
33
|
|
|
26
34
|
class StaticTestFinder {
|
|
@@ -20,7 +20,7 @@ export default class RootSkillViewTest extends AbstractGoogleTest {
|
|
|
20
20
|
private static fakedIntegrations: PublicHomeIntegration[] = []
|
|
21
21
|
protected static names: string[] = []
|
|
22
22
|
|
|
23
|
-
protected static async beforeEach()
|
|
23
|
+
protected static async beforeEach() {
|
|
24
24
|
await super.beforeEach()
|
|
25
25
|
|
|
26
26
|
this.fakedIntegrations = []
|
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FormViewController,
|
|
3
|
+
SkillViewControllerId,
|
|
4
|
+
buttonAssert,
|
|
5
|
+
formAssert,
|
|
6
|
+
interactor,
|
|
7
|
+
navigationAssert,
|
|
8
|
+
vcAssert,
|
|
9
|
+
} from '@sprucelabs/heartwood-view-controllers'
|
|
10
|
+
import { selectAssert } from '@sprucelabs/schema'
|
|
11
|
+
import { SelectChoice } from '@sprucelabs/spruce-core-schemas'
|
|
12
|
+
import { FormCardViewController } from '@sprucelabs/spruce-form-utils'
|
|
13
|
+
import { eventFaker, fake, seed } from '@sprucelabs/spruce-test-fixtures'
|
|
14
|
+
import { assert, generateId, test } from '@sprucelabs/test-utils'
|
|
15
|
+
import GenerateSkillViewController, {
|
|
16
|
+
CurrentChallengeSchema,
|
|
17
|
+
GenerateStorySchema,
|
|
18
|
+
} from '../../../generation/Generate.svc'
|
|
19
|
+
import { storyElements } from '../../../generation/storyElements'
|
|
20
|
+
import AbstractEightBitTest from '../../support/AbstractEightBitTest'
|
|
21
|
+
import {
|
|
22
|
+
GenerateStoryTargetAndPayload,
|
|
23
|
+
GetStoryStatusTargetAndPayload,
|
|
24
|
+
} from '../../support/EventFaker'
|
|
25
|
+
|
|
26
|
+
@fake.login()
|
|
27
|
+
export default class GenerateSkillViewTest extends AbstractEightBitTest {
|
|
28
|
+
private static vc: SpyGenerateSkillView
|
|
29
|
+
private static checkStatusIntervalCb: undefined | (() => Promise<void>)
|
|
30
|
+
private static checkStatusIntervalMs: number | undefined
|
|
31
|
+
private static intervalId: string
|
|
32
|
+
private static passedIntervalIdToClear?: string
|
|
33
|
+
|
|
34
|
+
@seed('familyMembers', 3)
|
|
35
|
+
protected static async beforeEach(): Promise<void> {
|
|
36
|
+
await super.beforeEach()
|
|
37
|
+
|
|
38
|
+
this.views.setController(
|
|
39
|
+
'eightbitstories.generate',
|
|
40
|
+
SpyGenerateSkillView
|
|
41
|
+
)
|
|
42
|
+
this.views.setController('forms.card', SpyFormCard)
|
|
43
|
+
|
|
44
|
+
this.vc = this.Vc()
|
|
45
|
+
|
|
46
|
+
await this.eventFaker.fakeListFamilyMembers(() => this.members.find({}))
|
|
47
|
+
await this.loadVc()
|
|
48
|
+
|
|
49
|
+
delete this.checkStatusIntervalCb
|
|
50
|
+
delete this.checkStatusIntervalMs
|
|
51
|
+
delete this.passedIntervalIdToClear
|
|
52
|
+
|
|
53
|
+
this.intervalId = generateId()
|
|
54
|
+
|
|
55
|
+
//@ts-ignore
|
|
56
|
+
GenerateSkillViewController.setInterval = (
|
|
57
|
+
cb: () => Promise<void>,
|
|
58
|
+
intervalMs: number
|
|
59
|
+
) => {
|
|
60
|
+
this.checkStatusIntervalMs = intervalMs
|
|
61
|
+
this.checkStatusIntervalCb = cb
|
|
62
|
+
return this.intervalId
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
//@ts-ignore
|
|
66
|
+
GenerateSkillViewController.clearInterval = (id: string) => {
|
|
67
|
+
this.passedIntervalIdToClear = id
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@test()
|
|
72
|
+
protected static async requiresLogin() {
|
|
73
|
+
await vcAssert.assertLoginIsRequired(this.vc)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@test()
|
|
77
|
+
protected static async rendersExpectedCards() {
|
|
78
|
+
vcAssert.assertSkillViewRendersCards(this.vc, [
|
|
79
|
+
'elements',
|
|
80
|
+
'members',
|
|
81
|
+
'currentChallenge',
|
|
82
|
+
'controls',
|
|
83
|
+
])
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@test()
|
|
87
|
+
protected static async controlsCardRendersExpectedButtons() {
|
|
88
|
+
buttonAssert.cardRendersButtons(this.controlsVc, ['back', 'generate'])
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@test()
|
|
92
|
+
protected static async rendersAlertAndRedirectsIfNoMembers() {
|
|
93
|
+
await this.eventFaker.fakeListFamilyMembers(() => [])
|
|
94
|
+
this.vc = this.Vc()
|
|
95
|
+
await vcAssert.assertRendersAlertThenRedirects({
|
|
96
|
+
vc: this.vc,
|
|
97
|
+
router: this.views.getRouter(),
|
|
98
|
+
destination: {
|
|
99
|
+
id: 'eightbitstories.root',
|
|
100
|
+
},
|
|
101
|
+
action: () => this.loadVc(),
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
@test()
|
|
106
|
+
protected static async clickingBackGoesBackToRoot() {
|
|
107
|
+
await vcAssert.assertActionRedirects({
|
|
108
|
+
action: () => interactor.clickButton(this.controlsVc, 'back'),
|
|
109
|
+
destination: {
|
|
110
|
+
id: 'eightbitstories.root',
|
|
111
|
+
},
|
|
112
|
+
router: this.views.getRouter(),
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
@test()
|
|
117
|
+
protected static elementsAndMembersCardsRendersForms() {
|
|
118
|
+
formAssert.cardRendersForm(this.elementsVc)
|
|
119
|
+
formAssert.cardRendersForm(this.membersVc)
|
|
120
|
+
formAssert.cardRendersForm(this.currentChallengeVc)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
@test()
|
|
124
|
+
protected static async formCardsDoNotRenderButtons() {
|
|
125
|
+
assert.isFalse(this.elementsFormVc.getShouldRenderSubmitControls())
|
|
126
|
+
assert.isFalse(this.membersFormVc.getShouldRenderSubmitControls())
|
|
127
|
+
assert.isFalse(
|
|
128
|
+
this.currentChallengeFormVc.getShouldRenderSubmitControls()
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
@test()
|
|
133
|
+
protected static async elementsFormRendersExpectedFields() {
|
|
134
|
+
formAssert.formRendersFields(this.elementsFormVc, ['elements'])
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
@test()
|
|
138
|
+
protected static async elementsFormRendersExpectedChoices() {
|
|
139
|
+
const schema = this.elementsFormVc.getSchema()
|
|
140
|
+
selectAssert.assertSelectChoicesMatch(
|
|
141
|
+
schema.fields.elements.options.choices,
|
|
142
|
+
storyElements.map((element) => element.id)
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
@test()
|
|
147
|
+
protected static async rendersElementsAsTags() {
|
|
148
|
+
formAssert.formFieldRendersAs(this.elementsFormVc, 'elements', 'tags')
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
@test()
|
|
152
|
+
protected static async membersFormRendersExpectedFields() {
|
|
153
|
+
formAssert.formRendersFields(this.membersFormVc, ['members'])
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
@test()
|
|
157
|
+
protected static async membersFormRendersAsTags() {
|
|
158
|
+
formAssert.formFieldRendersAs(this.membersFormVc, 'members', 'tags')
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
@test()
|
|
162
|
+
protected static async membersRendersExpectedChoices() {
|
|
163
|
+
const members = await this.getAllMembers()
|
|
164
|
+
const expected = members.map((member) => member.id)
|
|
165
|
+
|
|
166
|
+
const schema = this.membersFormVc.getSchema()
|
|
167
|
+
selectAssert.assertSelectChoicesMatch(
|
|
168
|
+
schema.fields.members.options.choices as SelectChoice[],
|
|
169
|
+
expected
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
@test()
|
|
174
|
+
protected static async currentChallengeFormRendersAsExpected() {
|
|
175
|
+
formAssert.formRendersField(
|
|
176
|
+
this.currentChallengeFormVc,
|
|
177
|
+
'currentChallenge'
|
|
178
|
+
)
|
|
179
|
+
formAssert.formFieldRendersAs(
|
|
180
|
+
this.currentChallengeFormVc,
|
|
181
|
+
'currentChallenge',
|
|
182
|
+
'textarea'
|
|
183
|
+
)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
@test()
|
|
187
|
+
protected static async clickingGenerateSetsControlsToBusy() {
|
|
188
|
+
await this.eventFaker.fakeGenerateStory(() => {})
|
|
189
|
+
|
|
190
|
+
await this.selectFirstMember()
|
|
191
|
+
await this.selectFirstElement()
|
|
192
|
+
|
|
193
|
+
const promise = this.clickGenerateAndAssertRedirect()
|
|
194
|
+
this.assertFooterIsBusy()
|
|
195
|
+
|
|
196
|
+
await promise
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
@test()
|
|
200
|
+
protected static async rendersAlertIfFailsToGenerateStory() {
|
|
201
|
+
await eventFaker.makeEventThrow(
|
|
202
|
+
'eightbitstories.generate-story::v2023_09_05'
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
const alertVc = await vcAssert.assertRendersAlert(this.vc, () =>
|
|
206
|
+
this.clickGenerate()
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
this.assertFooterIsBusy()
|
|
210
|
+
|
|
211
|
+
await alertVc.hide()
|
|
212
|
+
|
|
213
|
+
this.assertFooterIsNotBusy()
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
@test('submits selected members and elements 1', [0], [0])
|
|
217
|
+
@test('submits selected members and elements 2', [1], [2])
|
|
218
|
+
@test('submits selected members and elements 3', [0, 1], [2, 3])
|
|
219
|
+
protected static async generatePassesSelectedMembersAndElements(
|
|
220
|
+
memberIdxs: number[],
|
|
221
|
+
elementIdxs: number[]
|
|
222
|
+
) {
|
|
223
|
+
let passedPayload: GenerateStoryTargetAndPayload['payload'] | undefined
|
|
224
|
+
|
|
225
|
+
await this.eventFaker.fakeGenerateStory(({ payload }) => {
|
|
226
|
+
passedPayload = payload
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
const selectedMembers = await this.selectMembers(memberIdxs)
|
|
230
|
+
const selectedElements = await this.selectElements(elementIdxs)
|
|
231
|
+
|
|
232
|
+
const currentChallenge = generateId()
|
|
233
|
+
await this.currentChallengeFormVc.setValue(
|
|
234
|
+
'currentChallenge',
|
|
235
|
+
currentChallenge
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
await this.clickGenerateAndAssertRedirect()
|
|
239
|
+
|
|
240
|
+
assert.isEqualDeep(passedPayload, {
|
|
241
|
+
familyMembers: selectedMembers,
|
|
242
|
+
storyElements: selectedElements,
|
|
243
|
+
currentChallenge,
|
|
244
|
+
storyHash: this.vc.getHash(),
|
|
245
|
+
})
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
@test()
|
|
249
|
+
protected static async generatingStoryRedirectsToStoryWithArgs() {
|
|
250
|
+
await this.eventFaker.fakeGenerateStory()
|
|
251
|
+
|
|
252
|
+
await this.selectFirstElement()
|
|
253
|
+
await this.selectFirstMember()
|
|
254
|
+
|
|
255
|
+
const destination = {
|
|
256
|
+
id: 'eightbitstories.story' as SkillViewControllerId,
|
|
257
|
+
args: {
|
|
258
|
+
story: generateId(),
|
|
259
|
+
},
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
await this.clickGenerateAndAssertRedirect(destination)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
@test()
|
|
266
|
+
protected static async callingDestroyRemovesDidGenerateListener() {
|
|
267
|
+
await this.vc.destroy()
|
|
268
|
+
|
|
269
|
+
await eventFaker.handleReactiveEvent(
|
|
270
|
+
'eightbitstories.did-generate-story::v2023_09_05'
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
await this.emitDidGenerate()
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
@test()
|
|
277
|
+
protected static async rendersNullNavigation() {
|
|
278
|
+
navigationAssert.skillViewDoesNotRenderNavigation(this.vc)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
@test()
|
|
282
|
+
protected static async checksForGeneratedStoryAfterSubmitting() {
|
|
283
|
+
let passedTarget: GetStoryStatusTargetAndPayload['target'] | undefined
|
|
284
|
+
await this.eventFaker.fakeGetStoryGenerationStatus(({ target }) => {
|
|
285
|
+
passedTarget = target
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
await this.fakeGenerateSelectEverythingClickGenerateAndInvokeIntervalCb()
|
|
289
|
+
|
|
290
|
+
assert.isEqualDeep(passedTarget, {
|
|
291
|
+
storyHash: this.vc.getHash(),
|
|
292
|
+
})
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
@test()
|
|
296
|
+
protected static async passesExpectedIntervalToChecksAfterSubmit() {
|
|
297
|
+
await this.eventFaker.fakeGenerateStory()
|
|
298
|
+
await this.selectElementFamilyMemberAndClickGenerate()
|
|
299
|
+
assert.isEqual(this.checkStatusIntervalMs, 1000 * 10)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
@test()
|
|
303
|
+
protected static async doesNotSetIntervalIfGenerateThrows() {
|
|
304
|
+
await eventFaker.makeEventThrow(
|
|
305
|
+
'eightbitstories.generate-story::v2023_09_05'
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
await vcAssert.assertRendersAlert(this.vc, () =>
|
|
309
|
+
this.selectElementFamilyMemberAndClickGenerate()
|
|
310
|
+
)
|
|
311
|
+
assert.isUndefined(
|
|
312
|
+
this.checkStatusIntervalCb,
|
|
313
|
+
'should not have been set'
|
|
314
|
+
)
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
@test()
|
|
318
|
+
protected static async redirectsIfResponseIsStoryGenerated() {
|
|
319
|
+
const storyId = generateId()
|
|
320
|
+
await this.eventFaker.fakeGetStoryGenerationStatus(() => {
|
|
321
|
+
return {
|
|
322
|
+
status: 'ready',
|
|
323
|
+
storyId,
|
|
324
|
+
}
|
|
325
|
+
})
|
|
326
|
+
|
|
327
|
+
await this.fakeGenerateSelectEverythingAndClickGenerate()
|
|
328
|
+
await vcAssert.assertActionRedirects({
|
|
329
|
+
action: () => this.checkStatusIntervalCb?.(),
|
|
330
|
+
destination: {
|
|
331
|
+
id: 'eightbitstories.story',
|
|
332
|
+
args: {
|
|
333
|
+
story: storyId,
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
router: this.views.getRouter(),
|
|
337
|
+
})
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
@test()
|
|
341
|
+
protected static async clearsTimeoutOnBlur() {
|
|
342
|
+
await this.fakeGenerateSelectEverythingAndClickGenerate()
|
|
343
|
+
assert.isFalsy(this.passedIntervalIdToClear)
|
|
344
|
+
await interactor.blur(this.vc)
|
|
345
|
+
assert.isEqual(
|
|
346
|
+
this.passedIntervalIdToClear,
|
|
347
|
+
this.intervalId,
|
|
348
|
+
'did not pass response to setInterval to clearInterval'
|
|
349
|
+
)
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
private static async fakeGenerateSelectEverythingClickGenerateAndInvokeIntervalCb() {
|
|
353
|
+
await this.fakeGenerateSelectEverythingAndClickGenerate()
|
|
354
|
+
await this.checkStatusIntervalCb?.()
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
private static async fakeGenerateSelectEverythingAndClickGenerate() {
|
|
358
|
+
await this.eventFaker.fakeGenerateStory()
|
|
359
|
+
await this.selectElementFamilyMemberAndClickGenerate()
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
private static async selectElementFamilyMemberAndClickGenerate() {
|
|
363
|
+
await this.selectFirstElement()
|
|
364
|
+
await this.selectFirstMember()
|
|
365
|
+
await this.clickGenerate()
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
private static async clickGenerateAndAssertRedirect(destination?: {
|
|
369
|
+
id: SkillViewControllerId
|
|
370
|
+
args: { story: string }
|
|
371
|
+
}) {
|
|
372
|
+
await vcAssert.assertActionRedirects({
|
|
373
|
+
action: async () => {
|
|
374
|
+
await this.clickGenerate()
|
|
375
|
+
await this.emitDidGenerate(destination?.args?.story)
|
|
376
|
+
},
|
|
377
|
+
router: this.views.getRouter(),
|
|
378
|
+
destination,
|
|
379
|
+
})
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
private static async emitDidGenerate(storyId?: string) {
|
|
383
|
+
await this.fakedClient.emitAndFlattenResponses(
|
|
384
|
+
'eightbitstories.did-generate-story::v2023_09_05',
|
|
385
|
+
{
|
|
386
|
+
target: {
|
|
387
|
+
personId: generateId(),
|
|
388
|
+
},
|
|
389
|
+
payload: {
|
|
390
|
+
storyId: storyId ?? generateId(),
|
|
391
|
+
},
|
|
392
|
+
}
|
|
393
|
+
)
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
private static async selectFirstElement() {
|
|
397
|
+
const selectedElement = await this.selectElement(0)
|
|
398
|
+
return selectedElement
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
private static async selectElement(idx: number) {
|
|
402
|
+
const selectedElements = await this.selectElements([idx])
|
|
403
|
+
return selectedElements[0]
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
private static async selectElements(allIdxs: number[]) {
|
|
407
|
+
const selectedElements = allIdxs.map((idx) => storyElements[idx].id)
|
|
408
|
+
await this.elementsFormVc.setValue('elements', selectedElements)
|
|
409
|
+
return selectedElements
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
private static async selectFirstMember() {
|
|
413
|
+
const selectedMember = await this.selectMember(0)
|
|
414
|
+
return selectedMember
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
private static async selectMember(idx: number) {
|
|
418
|
+
const selectedMembers = await this.selectMembers([idx])
|
|
419
|
+
return selectedMembers[0]
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
private static async selectMembers(allIdxs: number[]) {
|
|
423
|
+
const members = await this.getAllMembers()
|
|
424
|
+
const selectedMembers = allIdxs.map((idx) => members[idx].id)
|
|
425
|
+
await this.membersFormVc.setValue('members', selectedMembers as any)
|
|
426
|
+
return selectedMembers
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
private static async getAllMembers() {
|
|
430
|
+
return await this.members.find({})
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
private static assertFooterIsNotBusy() {
|
|
434
|
+
assert.isFalse(this.getIsFooterBusy())
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
private static assertFooterIsBusy() {
|
|
438
|
+
assert.isTrue(this.getIsFooterBusy())
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
private static getIsFooterBusy(): boolean | null | undefined {
|
|
442
|
+
return this.controlsVc.getFooter()?.isBusy
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
private static async clickGenerate() {
|
|
446
|
+
await interactor.clickButton(this.controlsVc, 'generate')
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
private static get membersFormVc() {
|
|
450
|
+
return this.vc.getMembersFormVc()
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
private static get elementsFormVc() {
|
|
454
|
+
return this.vc.getElementsFormVc()
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
private static async loadVc() {
|
|
458
|
+
await this.views.load(this.vc)
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
private static get membersVc() {
|
|
462
|
+
return this.vc.getMembersVc()
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
private static get currentChallengeVc() {
|
|
466
|
+
return this.vc.getCurrentChallengeVc()
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
private static get currentChallengeFormVc() {
|
|
470
|
+
return this.currentChallengeVc.getFormVc() as FormViewController<CurrentChallengeSchema>
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
private static get elementsVc() {
|
|
474
|
+
return this.vc.getElementsVc()
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
private static get controlsVc() {
|
|
478
|
+
return this.vc.getControlsCardVc()
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
private static Vc(): SpyGenerateSkillView {
|
|
482
|
+
return this.views.Controller(
|
|
483
|
+
'eightbitstories.generate',
|
|
484
|
+
{}
|
|
485
|
+
) as SpyGenerateSkillView
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
class SpyGenerateSkillView extends GenerateSkillViewController {
|
|
490
|
+
public getHash() {
|
|
491
|
+
return this.storyHash!
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
public getCurrentChallengeVc() {
|
|
495
|
+
return this.currentChallengeVc
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
public getMembersFormVc() {
|
|
499
|
+
return this.getMembersVc().getFormVc() as FormViewController<GenerateStorySchema>
|
|
500
|
+
}
|
|
501
|
+
public getElementsFormVc() {
|
|
502
|
+
return this.getElementsVc().getFormVc() as FormViewController<GenerateStorySchema>
|
|
503
|
+
}
|
|
504
|
+
public getElementsVc() {
|
|
505
|
+
return this.elementsVc as SpyFormCard
|
|
506
|
+
}
|
|
507
|
+
public getMembersVc() {
|
|
508
|
+
return this.membersVc as SpyFormCard
|
|
509
|
+
}
|
|
510
|
+
public getControlsCardVc() {
|
|
511
|
+
return this.controlsVc
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
class SpyFormCard extends FormCardViewController {
|
|
516
|
+
public getFormVc() {
|
|
517
|
+
return this.formVc
|
|
518
|
+
}
|
|
519
|
+
}
|
|
@@ -12,14 +12,14 @@ class StaticToInstanceTestFileMigratorImpl {
|
|
|
12
12
|
// that has the `@test()` decorator
|
|
13
13
|
// 1b. If the contents include `export default abstract class`,
|
|
14
14
|
// remove `static` from all methods
|
|
15
|
-
const
|
|
16
|
-
let cleanedUp =
|
|
15
|
+
const isAbstractTest = /export\s+default\s+(?:abstract\s+class\b|class\s+(Abstract\w*))/m.test(contents);
|
|
16
|
+
let cleanedUp = isAbstractTest
|
|
17
17
|
? contents.replaceAll(' static ', ' ')
|
|
18
18
|
: contents.replace(
|
|
19
19
|
// Matches @test() or @seed(...) followed (on next line) by optional visibility and `static`.
|
|
20
|
-
/(@(?:test
|
|
20
|
+
/(@(?:(?:test|seed)\([\s\S]*?\))\s*\n\s*(?:public|protected)\s+)static\s+/g, '$1');
|
|
21
21
|
// 2. Add `@suite()` above `export default class` if it's not already present
|
|
22
|
-
if (!cleanedUp.includes('@suite')) {
|
|
22
|
+
if (!isAbstractTest && !cleanedUp.includes('@suite')) {
|
|
23
23
|
cleanedUp = cleanedUp.replace(/export default class/, '@suite()\nexport default class');
|
|
24
24
|
}
|
|
25
25
|
// 3. Ensure `suite` is imported from `@sprucelabs/test-utils`
|
|
@@ -41,9 +41,11 @@ class StaticToInstanceTestFileMigratorImpl {
|
|
|
41
41
|
// 4. lifecicle methods
|
|
42
42
|
const methods = ['beforeEach', 'afterEach'];
|
|
43
43
|
for (const method of methods) {
|
|
44
|
-
cleanedUp = cleanedUp.replace(`
|
|
44
|
+
cleanedUp = cleanedUp.replace(`static async ${method}()`, `async ${method}()`);
|
|
45
45
|
}
|
|
46
46
|
cleanedUp = this.fixNonNullAssertions(cleanedUp);
|
|
47
|
+
cleanedUp = cleanedUp.replaceAll('= >', '=>');
|
|
48
|
+
cleanedUp = cleanedUp.replaceAll('! =', ' =');
|
|
47
49
|
return cleanedUp;
|
|
48
50
|
}
|
|
49
51
|
hasSuiteImport(text) {
|
|
@@ -51,8 +53,9 @@ class StaticToInstanceTestFileMigratorImpl {
|
|
|
51
53
|
return pattern.test(text);
|
|
52
54
|
}
|
|
53
55
|
findThisCalls(contents) {
|
|
54
|
-
// Matches `this.myProp`
|
|
55
|
-
|
|
56
|
+
// Matches either `this.myProp` or `delete this.myProp`
|
|
57
|
+
// if followed by space, punctuation, parentheses, or end of string
|
|
58
|
+
const thisPropertyRegex = /(?:delete\s+)?this\.(\w+)(?=[\s.(),;]|$)/g;
|
|
56
59
|
const names = [];
|
|
57
60
|
let match;
|
|
58
61
|
while ((match = thisPropertyRegex.exec(contents)) !== null) {
|
|
@@ -83,19 +86,20 @@ class StaticToInstanceTestFileMigratorImpl {
|
|
|
83
86
|
return `${g1 ?? ''}${asyncPart}${accessorPart}${g4}(`;
|
|
84
87
|
});
|
|
85
88
|
/**
|
|
86
|
-
* 2) Remove `static` from property declarations and add a non-null assertion
|
|
89
|
+
* 2) Remove `static` from property declarations and add a non-null assertion
|
|
90
|
+
* if the property is not optional.
|
|
87
91
|
* e.g.
|
|
88
92
|
* private static myProp: Type => private myProp!: Type
|
|
93
|
+
* private static passedIntervalIdToClear?: string => private passedIntervalIdToClear?: string
|
|
89
94
|
*/
|
|
90
95
|
const propertyPattern = new RegExp(`((?:public|protected|private)?\\s+)?` + // group 1: optional visibility
|
|
91
96
|
`static\\s+` + // literal "static "
|
|
92
|
-
`(${name})
|
|
97
|
+
`(${name})(\\?)?` + // group 2: property name, group 3: optional "?"
|
|
93
98
|
`(?=[\\s=:\\[;]|$)`, // lookahead: space, '=', ':', '[', ';', or end-of-string
|
|
94
99
|
'g');
|
|
95
|
-
updated = updated.replace(propertyPattern, (match, g1, g2) => {
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
return `${g1 ?? ''}${g2}!`;
|
|
100
|
+
updated = updated.replace(propertyPattern, (match, g1, g2, g3) => {
|
|
101
|
+
// If the property is optional (g3 is "?"), leave it. Otherwise, add a non-null assertion.
|
|
102
|
+
return `${g1 ?? ''}${g2}${g3 !== undefined ? g3 : '!'}`;
|
|
99
103
|
});
|
|
100
104
|
return updated;
|
|
101
105
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StaticToInstanceTestFileMigrator.js","sourceRoot":"","sources":["../../../src/tests/staticToInstanceMigration/StaticToInstanceTestFileMigrator.ts"],"names":[],"mappings":";;AAAA,+CAAkD;AAElD,MAAqB,oCAAoC;IAG9C,MAAM,CAAC,KAAK,CAA6C;IAEzD,MAAM,CAAC,QAAQ;QAClB,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,EAAE,CAAA;IACrC,CAAC;IAEM,OAAO,CAAC,QAAgB;QAC3B,IAAA,sBAAa,EAAC,EAAE,QAAQ,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAA;QAEzC,wEAAwE;QACxE,sCAAsC;QACtC,+DAA+D;QAC/D,uCAAuC;QACvC,MAAM,
|
|
1
|
+
{"version":3,"file":"StaticToInstanceTestFileMigrator.js","sourceRoot":"","sources":["../../../src/tests/staticToInstanceMigration/StaticToInstanceTestFileMigrator.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
|
-
"version": "24.1.
|
|
7
|
+
"version": "24.1.3",
|
|
8
8
|
"skill": {
|
|
9
9
|
"namespace": "spruce-cli",
|
|
10
10
|
"upgradeIgnoreList": [
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"@sprucelabs/spruce-core-schemas": "^40.1.645",
|
|
85
85
|
"@sprucelabs/spruce-event-utils": "^40.2.44",
|
|
86
86
|
"@sprucelabs/spruce-skill-utils": "^31.2.60",
|
|
87
|
-
"@sprucelabs/spruce-templates": "^24.1.
|
|
87
|
+
"@sprucelabs/spruce-templates": "^24.1.3",
|
|
88
88
|
"@typescript-eslint/eslint-plugin": "^7.7.1",
|
|
89
89
|
"@typescript-eslint/parser": "^7.7.1",
|
|
90
90
|
"cfonts": "^3.3.0",
|
|
@@ -571,5 +571,5 @@
|
|
|
571
571
|
"open"
|
|
572
572
|
]
|
|
573
573
|
},
|
|
574
|
-
"gitHead": "
|
|
574
|
+
"gitHead": "16cc2438adb35cf10ace9348d722839749707ecc"
|
|
575
575
|
}
|