Skip to content

Commit e5b2842

Browse files
sobolkakshbhu
authored andcommitted
fix: multi select race conditions (#12356)
1 parent 0fb0256 commit e5b2842

File tree

6 files changed

+41
-41
lines changed

6 files changed

+41
-41
lines changed

packages/amplify-e2e-core/src/categories/api.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -531,17 +531,14 @@ function protectAPI(settings: RestAPISettings, chain: ExecutionContext) {
531531
.sendKeyDown()
532532
.sendCarriageReturn() // Authenticated and Guest users
533533
.wait('What permissions do you want to grant to Authenticated users')
534-
.sendCtrlA() // CRUD permissions for authenticated users
535-
.sendCarriageReturn()
534+
.selectAll() // CRUD permissions for authenticated users
536535
.wait('What permissions do you want to grant to Guest users')
537-
.sendCtrlA() // CRUD permissions for guest users
538-
.sendCarriageReturn();
536+
.selectAll(); // CRUD permissions for guest users
539537
} else {
540538
chain
541539
.sendCarriageReturn() // Authenticated users only
542540
.wait('What permissions do you want to grant to Authenticated users')
543-
.sendCtrlA() // CRUD permissions
544-
.sendCarriageReturn();
541+
.selectAll(); // CRUD permissions
545542
}
546543
} else {
547544
chain.sendNo();

packages/amplify-e2e-core/src/categories/auth.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -389,17 +389,15 @@ export function updateAuthSignInSignOutUrl(cwd: string, settings: any): Promise<
389389
.send(KEY_DOWN_ARROW)
390390
.sendCarriageReturn()
391391
.wait('Which redirect signin URIs do you want to edit?')
392-
.sendCtrlA()
393-
.sendCarriageReturn()
392+
.selectAll()
394393
.wait(`Update ${settings.signinUrl}`)
395394
.sendCarriageReturn()
396395
.send(settings.updatesigninUrl)
397396
.sendCarriageReturn()
398397
.wait('Do you want to add redirect signin URIs?')
399398
.sendConfirmNo()
400399
.wait('Which redirect signout URIs do you want to edit?')
401-
.sendCtrlA()
402-
.sendCarriageReturn()
400+
.selectAll()
403401
.wait(`Update ${settings.signoutUrl}`)
404402
.send(settings.updatesignoutUrl)
405403
.sendCarriageReturn()

packages/amplify-e2e-core/src/categories/custom.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ export const addCFNCustomResource = async (cwd: string, settings: any, testingWi
2424
.wait('Do you want to access Amplify generated resources in your custom CloudFormation file?')
2525
.sendYes();
2626
if (settings.promptForCategorySelection) {
27-
chain.wait('Select the categories you want this custom resource to have access to').sendCtrlA().sendCarriageReturn();
27+
chain.wait('Select the categories you want this custom resource to have access to').selectAll();
2828
}
2929
if (settings.promptForCustomResourcesSelection) {
30-
chain.wait('Select the one you would like your custom resource to access').sendCtrlA().sendCarriageReturn();
30+
chain.wait('Select the one you would like your custom resource to access').selectAll();
3131
}
3232
await chain.wait('Do you want to edit the CloudFormation stack now?').sendNo().sendEof().runAsync();
3333
};

packages/amplify-e2e-core/src/categories/geo.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,10 @@ export function addGeofenceCollectionWithDefault(cwd: string, groupNames: string
9797
.wait('Provide a name for the Geofence Collection:')
9898
.sendLine(config.resourceName)
9999
.wait('Select one or more cognito groups to give access:')
100-
.sendCtrlA()
101-
.sendCarriageReturn();
100+
.selectAll();
102101

103102
for (const groupName of groupNames) {
104-
chain.wait(`What kind of access do you want for ${groupName} users? Select ALL that apply:`).sendCtrlA().sendCarriageReturn();
103+
chain.wait(`What kind of access do you want for ${groupName} users? Select ALL that apply:`).selectAll();
105104
}
106105

107106
if (config.isAdditional === true) {

packages/amplify-e2e-core/src/categories/storage.ts

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -414,8 +414,7 @@ export function addS3AndAuthWithAuthOnlyAccess(cwd: string): Promise<void> {
414414
.wait('Who should have access')
415415
.sendCarriageReturn() // Auth users only
416416
.wait('What kind of access do you want for Authenticated users?')
417-
.sendCtrlA()
418-
.sendCarriageReturn()
417+
.selectAll()
419418
.wait('Do you want to add a Lambda Trigger for your S3 Bucket')
420419
.sendConfirmNo()
421420
.sendEof()
@@ -442,11 +441,9 @@ export function addS3WithGuestAccess(cwd: string): Promise<void> {
442441
.sendKeyDown()
443442
.sendCarriageReturn() // Auth and guest users
444443
.wait('What kind of access do you want for Authenticated users?')
445-
.sendCtrlA()
446-
.sendCarriageReturn()
444+
.selectAll()
447445
.wait('What kind of access do you want for Guest users?')
448-
.sendCtrlA()
449-
.sendCarriageReturn()
446+
.selectAll()
450447
.wait('Do you want to add a Lambda Trigger for your S3 Bucket')
451448
.sendConfirmNo()
452449
.sendEof()
@@ -474,14 +471,11 @@ export function addS3WithGroupAccess(cwd: string, settings: any): Promise<void>
474471
.sendKeyDown()
475472
.sendCarriageReturn() // Individual groups
476473
.wait('Select groups')
477-
.sendCtrlA() // select all groups
478-
.sendCarriageReturn()
474+
.selectAll() // select all groups
479475
.wait(`What kind of access do you want for ${settings?.userGroup1 ?? 'Admins'} users`) // for <UserGroup1> users?
480-
.sendCtrlA() // Select all permissions
481-
.sendCarriageReturn()
476+
.selectAll() // Select all permissions
482477
.wait(`What kind of access do you want for ${settings?.userGroup2 ?? 'Users'} users`) // for <UserGroup2> users?
483-
.sendCtrlA() // Select all permissions
484-
.sendCarriageReturn()
478+
.selectAll() // Select all permissions
485479
.wait('Do you want to add a Lambda Trigger for your S3 Bucket')
486480
.sendConfirmNo()
487481
.sendEof()
@@ -668,11 +662,9 @@ export function addS3Storage(projectDir: string): Promise<void> {
668662
.send(' ') //Auth and guest
669663
.sendCarriageReturn()
670664
.wait('What kind of access do you want for Authenticated users?') //Auth
671-
.sendCtrlA()
672-
.sendCarriageReturn()
665+
.selectAll()
673666
.wait('What kind of access do you want for Guest users?') //Guest
674-
.sendCtrlA()
675-
.sendCarriageReturn()
667+
.selectAll()
676668
.wait('Do you want to add a Lambda Trigger for your S3 Bucket?')
677669
.sendConfirmNo()
678670
.run((err: Error) => {
@@ -698,8 +690,7 @@ export function addS3StorageWithAuthOnly(projectDir: string): Promise<void> {
698690
.wait('Who should have access:')
699691
.sendCarriageReturn() //Auth users only
700692
.wait('What kind of access do you want for Authenticated users?') //Auth
701-
.sendCtrlA()
702-
.sendCarriageReturn()
693+
.selectAll()
703694
.wait('Do you want to add a Lambda Trigger for your S3 Bucket?')
704695
.sendConfirmNo()
705696
.run((err: Error) => {
@@ -746,9 +737,9 @@ export function addS3StorageWithSettings(projectDir: string, settings: AddStorag
746737

747738
chain.wait('Who should have access:').sendKeyDown().send(' ').sendCarriageReturn();
748739

749-
chain.wait('What kind of access do you want for Authenticated users?').sendCtrlA().sendCarriageReturn();
740+
chain.wait('What kind of access do you want for Authenticated users?').selectAll();
750741

751-
chain.wait('What kind of access do you want for Guest users?').sendCtrlA().sendCarriageReturn();
742+
chain.wait('What kind of access do you want for Guest users?').selectAll();
752743

753744
chain.wait('Do you want to add a Lambda Trigger for your S3 Bucket?').sendConfirmNo();
754745

packages/amplify-e2e-core/src/utils/nexpect.ts

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ export type ExecutionContext = {
9090
sendNo: () => ExecutionContext;
9191
sendCtrlC: () => ExecutionContext;
9292
sendCtrlA: () => ExecutionContext;
93+
selectAll: () => ExecutionContext;
9394
sendEof: () => ExecutionContext;
9495
delay: (milliseconds: number) => ExecutionContext;
9596
/**
@@ -340,6 +341,15 @@ function chain(context: Context): ExecutionContext {
340341
context.queue.push(_send);
341342
return chain(context);
342343
},
344+
selectAll(): ExecutionContext {
345+
/*
346+
Delays are added because multi-select re-renders when making transitions.
347+
Sending Ctrl+A or confirmation while prompter state settles might not be reflected on the CLI side.
348+
Delays are arbitrary. The alternative of tracking prompt transitions would be much more complicated
349+
given variety of rendering styles, but should be pursued if this solution stops working.
350+
*/
351+
return this.delay(1000).sendCtrlA().delay(1000).sendCarriageReturn();
352+
},
343353
sendEof(): ExecutionContext {
344354
const _sendEof: ExecutionStep = {
345355
fn: () => {
@@ -357,12 +367,17 @@ function chain(context: Context): ExecutionContext {
357367
delay(milliseconds: number): ExecutionContext {
358368
const _delay: ExecutionStep = {
359369
fn: () => {
360-
const startCallback = Date.now();
361-
362-
while (Date.now() - startCallback < milliseconds) {
363-
// empty
364-
}
365-
370+
/*
371+
Code below is workaround for lack of synchronous sleep() in JS.
372+
It has nothing to do with atomicity nor with buffers. It's just using these
373+
built-in APIs to wait synchronously. I.e. it waits number of milliseconds for
374+
byte transition in the buffer that never happens.
375+
This replaced active spin-lock that was here before
376+
and was burning 100% CPU while waiting that led to spawned CLI starvation.
377+
If this way of delaying becomes insufficient then this module should
378+
be refactored and implement fully asynchronous input handlers.
379+
*/
380+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds);
366381
return true;
367382
},
368383
shift: true,

0 commit comments

Comments
 (0)