testdriverai 6.2.0 → 6.2.1

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.
Files changed (64) hide show
  1. package/.github/workflows/acceptance-tests.yml +2 -0
  2. package/.github/workflows/acceptance-v6.yml +2 -0
  3. package/.github/workflows/lint.yml +4 -1
  4. package/.github/workflows/publish-canary.yml +2 -0
  5. package/.github/workflows/publish-latest.yml +1 -0
  6. package/.github/workflows/self-hosted.yml +102 -0
  7. package/.prettierignore +1 -0
  8. package/.vscode/settings.json +4 -1
  9. package/agent/events.js +1 -10
  10. package/agent/index.js +98 -55
  11. package/agent/interface.js +43 -6
  12. package/agent/lib/censorship.js +15 -10
  13. package/agent/lib/commander.js +31 -18
  14. package/agent/lib/commands.js +62 -17
  15. package/agent/lib/debugger-server.js +0 -5
  16. package/agent/lib/generator.js +2 -2
  17. package/agent/lib/sdk.js +2 -1
  18. package/agent/lib/source-mapper.js +1 -1
  19. package/debugger/index.html +1 -1
  20. package/docs/account/enterprise.mdx +8 -12
  21. package/docs/account/pricing.mdx +2 -2
  22. package/docs/account/projects.mdx +5 -0
  23. package/docs/apps/tauri-apps.mdx +361 -0
  24. package/docs/cli/overview.mdx +6 -6
  25. package/docs/commands/assert.mdx +1 -0
  26. package/docs/commands/hover-text.mdx +3 -1
  27. package/docs/commands/match-image.mdx +5 -4
  28. package/docs/commands/press-keys.mdx +6 -8
  29. package/docs/commands/scroll-until-image.mdx +8 -7
  30. package/docs/commands/scroll-until-text.mdx +7 -6
  31. package/docs/commands/wait-for-image.mdx +5 -4
  32. package/docs/commands/wait-for-text.mdx +6 -5
  33. package/docs/docs.json +42 -40
  34. package/docs/getting-started/playwright.mdx +342 -0
  35. package/docs/getting-started/self-hosting.mdx +370 -0
  36. package/docs/getting-started/vscode.mdx +67 -56
  37. package/docs/guide/dashcam.mdx +118 -0
  38. package/docs/guide/environment-variables.mdx +5 -5
  39. package/docs/images/content/self-hosted/launchtemplateid.png +0 -0
  40. package/docs/images/content/vscode/ide-full.png +0 -0
  41. package/docs/images/content/vscode/running.png +0 -0
  42. package/docs/overview/comparison.mdx +22 -39
  43. package/docs/overview/quickstart.mdx +84 -32
  44. package/docs/styles.css +10 -1
  45. package/interfaces/cli/commands/generate.js +3 -0
  46. package/interfaces/cli/lib/base.js +27 -5
  47. package/interfaces/cli/utils/factory.js +17 -4
  48. package/interfaces/logger.js +4 -4
  49. package/interfaces/readline.js +1 -1
  50. package/package.json +3 -3
  51. package/schema.json +21 -0
  52. package/setup/aws/cloudformation.yaml +463 -0
  53. package/setup/aws/spawn-runner.sh +190 -0
  54. package/testdriver/acceptance/hover-text.yaml +2 -1
  55. package/testdriver/acceptance/prompt.yaml +4 -1
  56. package/testdriver/acceptance/scroll-until-image.yaml +5 -0
  57. package/testdriver/edge-cases/js-exception.yaml +8 -0
  58. package/testdriver/edge-cases/js-promise.yaml +19 -0
  59. package/testdriver/edge-cases/lifecycle/postrun.yaml +10 -0
  60. package/testdriver/edge-cases/success-test.yaml +9 -0
  61. package/testdriver/examples/web/lifecycle/postrun.yaml +7 -0
  62. package/testdriver/examples/web/lifecycle/{provision.yaml → prerun.yaml} +6 -0
  63. package/testdriver/lifecycle/postrun.yaml +7 -0
  64. package/testdriver/lifecycle/prerun.yaml +17 -0
@@ -0,0 +1,463 @@
1
+ AWSTemplateFormatVersion: "2010-09-09"
2
+ Description: >-
3
+ Baseline artifacts (NO EC2 instance): Creates a dedicated VPC with public subnet, Security Group,
4
+ IAM Role/Profile, optional KeyPair, and an EC2 Launch Template so you can spawn many instances
5
+ programmatically. Writes handy IDs into SSM Parameter Store for easy lookup in scripts or automation.
6
+
7
+ Metadata:
8
+ AWS::CloudFormation::Interface:
9
+ ParameterGroups:
10
+ - Label:
11
+ default: "Notification Configuration"
12
+ Parameters:
13
+ - NotificationEmail
14
+ - Label:
15
+ default: "Project Configuration"
16
+ Parameters:
17
+ - ProjectTag
18
+ - Label:
19
+ default: "Network Configuration"
20
+ Parameters:
21
+ - AllowedIngressCidr
22
+ - Label:
23
+ default: "Instance Configuration"
24
+ Parameters:
25
+ - InstanceType
26
+ - Label:
27
+ default: "Key Pair Configuration"
28
+ Parameters:
29
+ - CreateKeyPair
30
+ - ExistingKeyName
31
+ ParameterLabels:
32
+ ProjectTag:
33
+ default: "Project Tag"
34
+ InstanceType:
35
+ default: "Instance Type"
36
+ AllowedIngressCidr:
37
+ default: "Allowed Ingress CIDR"
38
+ CreateKeyPair:
39
+ default: "Create New Key Pair"
40
+ ExistingKeyName:
41
+ default: "Existing Key Name (only required if 'Create New Key Pair' is 'no')"
42
+ NotificationEmail:
43
+ default: "Notification Email (optional)"
44
+
45
+ Rules:
46
+ ValidateKeyPairConfiguration:
47
+ RuleCondition: !Equals [!Ref CreateKeyPair, "no"]
48
+ Assertions:
49
+ - Assert: !Not [!Equals [!Ref ExistingKeyName, ""]]
50
+ AssertDescription: "ExistingKeyName must be provided when CreateKeyPair is 'no'"
51
+
52
+ Parameters:
53
+ NotificationEmail:
54
+ Type: String
55
+ Default: ""
56
+ Description: Email address to receive deployment completion notifications (optional)
57
+
58
+ ProjectTag:
59
+ Type: String
60
+ Default: testdriver
61
+
62
+ InstanceType:
63
+ Type: String
64
+ Default: c5.xlarge
65
+ AllowedValues:
66
+ - c5.xlarge
67
+ - c5.2xlarge
68
+ - c5.4xlarge
69
+ - c5.9xlarge
70
+ - c5.12xlarge
71
+ - c5.18xlarge
72
+ - c5.24xlarge
73
+ - c5.metal
74
+ - i3.metal
75
+ Description: Instance type - only c5.xlarge or larger, plus c5.metal and i3.metal allowed
76
+
77
+ AllowedIngressCidr:
78
+ Type: String
79
+ Default: 0.0.0.0/0
80
+ Description: CIDR allowed to access inbound ports (0.0.0.0/0 means "anyone", we recommend tightening this in production).
81
+
82
+ CreateKeyPair:
83
+ Type: String
84
+ AllowedValues: [yes, no]
85
+ Default: yes
86
+ Description: Create a new key pair for instance access? (If 'no', you must provide an existing key name)
87
+ ExistingKeyName:
88
+ Type: String
89
+ Default: ""
90
+ Description: Name of existing EC2 Key Pair (only required when CreateKeyPair is 'no')
91
+
92
+ Conditions:
93
+ UseExistingKeyProvided: !Not [!Equals [!Ref ExistingKeyName, ""]]
94
+ CreateKey: !Equals [!Ref CreateKeyPair, "yes"]
95
+ SendNotification: !Not [!Equals [!Ref NotificationEmail, ""]]
96
+
97
+ Resources:
98
+ # VPC for TestDriver
99
+ TestDriverVpc:
100
+ Type: AWS::EC2::VPC
101
+ Properties:
102
+ CidrBlock: 10.0.0.0/16
103
+ EnableDnsHostnames: true
104
+ EnableDnsSupport: true
105
+ Tags:
106
+ - { Key: Name, Value: !Sub "${AWS::StackName}-vpc" }
107
+ - { Key: Project, Value: !Ref ProjectTag }
108
+
109
+ # Public subnet for EC2 instances
110
+ PublicSubnet:
111
+ Type: AWS::EC2::Subnet
112
+ Properties:
113
+ VpcId: !Ref TestDriverVpc
114
+ CidrBlock: 10.0.1.0/24
115
+ AvailabilityZone: !Select [0, !GetAZs ""]
116
+ MapPublicIpOnLaunch: true
117
+ Tags:
118
+ - { Key: Name, Value: !Sub "${AWS::StackName}-public-subnet" }
119
+ - { Key: Project, Value: !Ref ProjectTag }
120
+
121
+ # Internet Gateway
122
+ InternetGateway:
123
+ Type: AWS::EC2::InternetGateway
124
+ Properties:
125
+ Tags:
126
+ - { Key: Name, Value: !Sub "${AWS::StackName}-igw" }
127
+ - { Key: Project, Value: !Ref ProjectTag }
128
+
129
+ # Attach Internet Gateway to VPC
130
+ AttachGateway:
131
+ Type: AWS::EC2::VPCGatewayAttachment
132
+ Properties:
133
+ VpcId: !Ref TestDriverVpc
134
+ InternetGatewayId: !Ref InternetGateway
135
+
136
+ # Route table for public subnet
137
+ PublicRouteTable:
138
+ Type: AWS::EC2::RouteTable
139
+ Properties:
140
+ VpcId: !Ref TestDriverVpc
141
+ Tags:
142
+ - { Key: Name, Value: !Sub "${AWS::StackName}-public-rt" }
143
+ - { Key: Project, Value: !Ref ProjectTag }
144
+
145
+ # Route to Internet Gateway
146
+ PublicRoute:
147
+ Type: AWS::EC2::Route
148
+ DependsOn: AttachGateway
149
+ Properties:
150
+ RouteTableId: !Ref PublicRouteTable
151
+ DestinationCidrBlock: 0.0.0.0/0
152
+ GatewayId: !Ref InternetGateway
153
+
154
+ # Associate route table with public subnet
155
+ SubnetRouteTableAssociation:
156
+ Type: AWS::EC2::SubnetRouteTableAssociation
157
+ Properties:
158
+ SubnetId: !Ref PublicSubnet
159
+ RouteTableId: !Ref PublicRouteTable
160
+
161
+ SecurityGroup:
162
+ Type: AWS::EC2::SecurityGroup
163
+ Properties:
164
+ GroupDescription: SG for QA desktop testing (RDP/HTTPS/NGINX/pyautogui + VNC)
165
+ VpcId: !Ref TestDriverVpc
166
+ SecurityGroupIngress:
167
+ - {
168
+ IpProtocol: tcp,
169
+ FromPort: 8765,
170
+ ToPort: 8765,
171
+ CidrIp: !Ref AllowedIngressCidr,
172
+ Description: "pyautogui-cli WebSockets",
173
+ }
174
+ - {
175
+ IpProtocol: tcp,
176
+ FromPort: 8443,
177
+ ToPort: 8443,
178
+ CidrIp: !Ref AllowedIngressCidr,
179
+ Description: "Custom 8443",
180
+ }
181
+ - {
182
+ IpProtocol: tcp,
183
+ FromPort: 8080,
184
+ ToPort: 8080,
185
+ CidrIp: !Ref AllowedIngressCidr,
186
+ Description: "NGINX 8080",
187
+ }
188
+ - {
189
+ IpProtocol: tcp,
190
+ FromPort: 80,
191
+ ToPort: 80,
192
+ CidrIp: !Ref AllowedIngressCidr,
193
+ Description: "HTTP 80",
194
+ }
195
+ - {
196
+ IpProtocol: tcp,
197
+ FromPort: 443,
198
+ ToPort: 443,
199
+ CidrIp: !Ref AllowedIngressCidr,
200
+ Description: "HTTPS 443",
201
+ }
202
+ - {
203
+ IpProtocol: tcp,
204
+ FromPort: 3389,
205
+ ToPort: 3389,
206
+ CidrIp: !Ref AllowedIngressCidr,
207
+ Description: "RDP 3389",
208
+ }
209
+ - {
210
+ IpProtocol: tcp,
211
+ FromPort: 5900,
212
+ ToPort: 5900,
213
+ CidrIp: !Ref AllowedIngressCidr,
214
+ Description: "TightVNC 5900",
215
+ }
216
+ - {
217
+ IpProtocol: tcp,
218
+ FromPort: 5901,
219
+ ToPort: 5901,
220
+ CidrIp: !Ref AllowedIngressCidr,
221
+ Description: "noVNC Websockify 5901",
222
+ }
223
+ - {
224
+ IpProtocol: tcp,
225
+ FromPort: 6080,
226
+ ToPort: 6080,
227
+ CidrIp: !Ref AllowedIngressCidr,
228
+ Description: "noVNC HTTP 6080",
229
+ }
230
+ SecurityGroupEgress:
231
+ - IpProtocol: -1
232
+ CidrIp: 0.0.0.0/0
233
+ Description: Allow all outbound
234
+ Tags:
235
+ - { Key: Project, Value: !Ref ProjectTag }
236
+
237
+ InstanceRole:
238
+ Type: AWS::IAM::Role
239
+ Properties:
240
+ AssumeRolePolicyDocument:
241
+ Version: "2012-10-17"
242
+ Statement:
243
+ - Effect: Allow
244
+ Principal: { Service: ec2.amazonaws.com }
245
+ Action: sts:AssumeRole
246
+ ManagedPolicyArns:
247
+ - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
248
+ Tags:
249
+ - { Key: Project, Value: !Ref ProjectTag }
250
+
251
+ InstanceProfile:
252
+ Type: AWS::IAM::InstanceProfile
253
+ Properties:
254
+ Roles: [!Ref InstanceRole]
255
+
256
+ KeyPair:
257
+ Type: AWS::EC2::KeyPair
258
+ Condition: CreateKey
259
+ Properties:
260
+ KeyName: !Sub "${AWS::StackName}-key"
261
+ KeyType: rsa
262
+
263
+ LaunchTemplate:
264
+ Type: AWS::EC2::LaunchTemplate
265
+ Properties:
266
+ LaunchTemplateName: !Sub "${AWS::StackName}-lt"
267
+ LaunchTemplateData:
268
+ InstanceType: !Ref InstanceType
269
+ IamInstanceProfile:
270
+ Name: !Ref InstanceProfile
271
+ # Lock SG + Subnet to the same VPC
272
+ NetworkInterfaces:
273
+ - DeviceIndex: 0
274
+ SubnetId: !Ref PublicSubnet
275
+ Groups: [!Ref SecurityGroup]
276
+ AssociatePublicIpAddress: true
277
+ KeyName: !If
278
+ - CreateKey
279
+ - !Ref KeyPair
280
+ - !If
281
+ - UseExistingKeyProvided
282
+ - !Ref ExistingKeyName
283
+ - !Ref AWS::NoValue
284
+ TagSpecifications:
285
+ - ResourceType: instance
286
+ Tags: [{ Key: Project, Value: !Ref ProjectTag }]
287
+ - ResourceType: volume
288
+ Tags: [{ Key: Project, Value: !Ref ProjectTag }]
289
+
290
+ # SNS Topic for deployment notifications
291
+ DeploymentNotificationTopic:
292
+ Type: AWS::SNS::Topic
293
+ Condition: SendNotification
294
+ Properties:
295
+ TopicName: !Sub "${AWS::StackName}-deployment-notifications"
296
+ DisplayName: !Sub "${AWS::StackName} Deployment Notifications"
297
+ Tags:
298
+ - { Key: Project, Value: !Ref ProjectTag }
299
+
300
+ # SNS Subscription for email notifications
301
+ EmailSubscription:
302
+ Type: AWS::SNS::Subscription
303
+ Condition: SendNotification
304
+ Properties:
305
+ Protocol: email
306
+ TopicArn: !Ref DeploymentNotificationTopic
307
+ Endpoint: !Ref NotificationEmail
308
+
309
+ # Custom resource to send completion notification
310
+ DeploymentCompleteNotification:
311
+ Type: AWS::CloudFormation::CustomResource
312
+ Condition: SendNotification
313
+ Properties:
314
+ ServiceToken: !GetAtt NotificationLambda.Arn
315
+ StackName: !Ref AWS::StackName
316
+ TopicArn: !Ref DeploymentNotificationTopic
317
+ DependsOn:
318
+ - SsmParamSg
319
+ - SsmParamIp
320
+ - SsmParamLt
321
+ - SsmParamLtLatest
322
+ - SsmParamVpc
323
+ - SsmParamSubnet
324
+
325
+ # Lambda function to send the notification
326
+ NotificationLambda:
327
+ Type: AWS::Lambda::Function
328
+ Condition: SendNotification
329
+ Properties:
330
+ FunctionName: !Sub "${AWS::StackName}-deployment-notifier"
331
+ Runtime: python3.11
332
+ Handler: index.lambda_handler
333
+ Role: !GetAtt NotificationLambdaRole.Arn
334
+ Code:
335
+ ZipFile: |
336
+ import boto3
337
+ import json
338
+ import cfnresponse
339
+
340
+ def lambda_handler(event, context):
341
+ try:
342
+ if event['RequestType'] == 'Create':
343
+ sns = boto3.client('sns')
344
+ stack_name = event['ResourceProperties']['StackName']
345
+ topic_arn = event['ResourceProperties']['TopicArn']
346
+
347
+ message = f"""
348
+ TestDriver Infrastructure Deployment Complete!
349
+
350
+ Stack Name: {stack_name}
351
+ Status: CREATE_COMPLETE
352
+
353
+ Your TestDriver infrastructure is now ready to use.
354
+ Check the CloudFormation outputs for resource IDs and configuration details.
355
+ """
356
+
357
+ sns.publish(
358
+ TopicArn=topic_arn,
359
+ Subject=f'TestDriver Stack {stack_name} - Deployment Complete',
360
+ Message=message
361
+ )
362
+
363
+ cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
364
+ except Exception as e:
365
+ print(f"Error: {e}")
366
+ cfnresponse.send(event, context, cfnresponse.FAILED, {})
367
+ Tags:
368
+ - { Key: Project, Value: !Ref ProjectTag }
369
+
370
+ # IAM Role for the notification Lambda
371
+ NotificationLambdaRole:
372
+ Type: AWS::IAM::Role
373
+ Condition: SendNotification
374
+ Properties:
375
+ AssumeRolePolicyDocument:
376
+ Version: "2012-10-17"
377
+ Statement:
378
+ - Effect: Allow
379
+ Principal:
380
+ Service: lambda.amazonaws.com
381
+ Action: sts:AssumeRole
382
+ ManagedPolicyArns:
383
+ - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
384
+ Policies:
385
+ - PolicyName: SNSPublishPolicy
386
+ PolicyDocument:
387
+ Version: "2012-10-17"
388
+ Statement:
389
+ - Effect: Allow
390
+ Action:
391
+ - sns:Publish
392
+ Resource: !Ref DeploymentNotificationTopic
393
+ Tags:
394
+ - { Key: Project, Value: !Ref ProjectTag }
395
+
396
+ SsmParamSg:
397
+ Type: AWS::SSM::Parameter
398
+ Properties:
399
+ Name: !Sub "/testdriver/infra/${AWS::StackName}/security-group-id"
400
+ Type: String
401
+ Value: !Ref SecurityGroup
402
+
403
+ SsmParamIp:
404
+ Type: AWS::SSM::Parameter
405
+ Properties:
406
+ Name: !Sub "/testdriver/infra/${AWS::StackName}/instance-profile-name"
407
+ Type: String
408
+ Value: !Ref InstanceProfile
409
+
410
+ SsmParamLt:
411
+ Type: AWS::SSM::Parameter
412
+ Properties:
413
+ Name: !Sub "/testdriver/infra/${AWS::StackName}/launch-template-id"
414
+ Type: String
415
+ Value: !Ref LaunchTemplate
416
+
417
+ SsmParamLtLatest:
418
+ Type: AWS::SSM::Parameter
419
+ Properties:
420
+ Name: !Sub "/testdriver/infra/${AWS::StackName}/launch-template-latest-version"
421
+ Type: String
422
+ Value: !GetAtt LaunchTemplate.LatestVersionNumber
423
+
424
+ SsmParamVpc:
425
+ Type: AWS::SSM::Parameter
426
+ Properties:
427
+ Name: !Sub "/testdriver/infra/${AWS::StackName}/testdriver-vpc-id"
428
+ Type: String
429
+ Value: !Ref TestDriverVpc
430
+
431
+ SsmParamSubnet:
432
+ Type: AWS::SSM::Parameter
433
+ Properties:
434
+ Name: !Sub "/testdriver/infra/${AWS::StackName}/testdriver-public-subnet-id"
435
+ Type: String
436
+ Value: !Ref PublicSubnet
437
+
438
+ Outputs:
439
+ VpcId:
440
+ Value: !Ref TestDriverVpc
441
+ Description: VPC ID created for TestDriver
442
+ SubnetId:
443
+ Value: !Ref PublicSubnet
444
+ Description: Public subnet ID for TestDriver instances
445
+ SecurityGroupId:
446
+ Value: !Ref SecurityGroup
447
+ Description: Security Group for QA desktop testing
448
+ InstanceProfileName:
449
+ Value: !Ref InstanceProfile
450
+ Description: Instance Profile to attach to instances
451
+ LaunchTemplateId:
452
+ Value: !Ref LaunchTemplate
453
+ Description: EC2 Launch Template ID
454
+ LaunchTemplateLatestVersion:
455
+ Value: !GetAtt LaunchTemplate.LatestVersionNumber
456
+ Description: Latest Launch Template version
457
+ KeyPairSsmParam:
458
+ Condition: CreateKey
459
+ Value: !Sub "/ec2/keypair/${KeyPair.KeyPairId}"
460
+ Description: SSM parameter that stores the generated private key
461
+ SsmNamespaceUsed:
462
+ Value: !Sub "/testdriver/infra/${AWS::StackName}"
463
+ Description: Prefix where IDs are stored for discovery
@@ -0,0 +1,190 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # --- Config (reads from env) ---
5
+ : "${AWS_REGION:?Set AWS_REGION}"
6
+ : "${AMI_ID:?Set AMI_ID (TestDriver Ami)}"
7
+ : "${AWS_LAUNCH_TEMPLATE_ID:?Set AWS_LAUNCH_TEMPLATE_ID}"
8
+ : "${AWS_LAUNCH_TEMPLATE_VERSION:=\$Latest}"
9
+ : "${AWS_TAG_PREFIX:=td}"
10
+ : "${RUNNER_CLASS_ID:=default}"
11
+ : "${RESOLUTION:=1440x900}"
12
+
13
+ TAG_NAME="${AWS_TAG_PREFIX}-"$(date +%s)
14
+ WS_CONFIG_PATH='C:\Windows\Temp\pyautogui-ws.json'
15
+
16
+ echo "Launching AWS Instance..."
17
+
18
+ # --- 1) Launch instance ---
19
+ RUN_JSON=$(aws ec2 run-instances \
20
+ --region "$AWS_REGION" \
21
+ --image-id "$AMI_ID" \
22
+ --launch-template "LaunchTemplateId=$AWS_LAUNCH_TEMPLATE_ID,Version=$AWS_LAUNCH_TEMPLATE_VERSION" \
23
+ --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=${TAG_NAME}},{Key=Class,Value=${RUNNER_CLASS_ID}},{Key=TD_RESOLUTION,Value=${RESOLUTION}}]" \
24
+ --output json)
25
+
26
+ INSTANCE_ID=$(jq -r '.Instances[0].InstanceId' <<<"$RUN_JSON")
27
+
28
+ echo "Launched: $INSTANCE_ID"
29
+ echo "Instance details:"
30
+ echo " Region: $AWS_REGION"
31
+ echo " AMI ID: $AMI_ID"
32
+ echo " Launch Template ID: $AWS_LAUNCH_TEMPLATE_ID"
33
+ echo " Launch Template Version: $AWS_LAUNCH_TEMPLATE_VERSION"
34
+
35
+ echo "Waiting for instance to be running..."
36
+
37
+ # --- 2) Wait for running + status checks ---
38
+ aws ec2 wait instance-running --region "$AWS_REGION" --instance-ids "$INSTANCE_ID"
39
+ echo "✓ Instance is now running"
40
+
41
+ echo "Waiting for instance to pass status checks..."
42
+
43
+ aws ec2 wait instance-status-ok --region "$AWS_REGION" --instance-ids "$INSTANCE_ID"
44
+ echo "✓ Instance passed all status checks"
45
+
46
+ # Additional validation - check instance state details
47
+ echo "Validating instance readiness..."
48
+ INSTANCE_STATE=$(aws ec2 describe-instances --region "$AWS_REGION" --instance-ids "$INSTANCE_ID" \
49
+ --query 'Reservations[0].Instances[0].{State:State.Name,StatusChecks:StateTransitionReason}' \
50
+ --output json)
51
+ echo "Instance state details: $INSTANCE_STATE"
52
+
53
+ # --- 3) Ensure SSM connectivity ---
54
+ echo "Waiting for SSM connectivity..."
55
+ echo "This can take several minutes for the SSM agent to be fully ready..."
56
+
57
+ # First, check if the instance is registered with SSM
58
+ echo "Checking SSM instance registration..."
59
+ TRIES=0; MAX_TRIES=60
60
+ while :; do
61
+ echo "Attempt $((TRIES+1))/$MAX_TRIES: Checking if instance is registered with SSM..."
62
+
63
+ # Check if instance appears in SSM managed instances
64
+ if aws ssm describe-instance-information \
65
+ --region "$AWS_REGION" \
66
+ --filters "Key=InstanceIds,Values=$INSTANCE_ID" \
67
+ --query 'InstanceInformationList[0].InstanceId' \
68
+ --output text 2>/dev/null | grep -q "$INSTANCE_ID"; then
69
+ echo "✓ Instance is registered with SSM"
70
+ break
71
+ fi
72
+
73
+ TRIES=$((TRIES+1))
74
+ if [ $TRIES -ge $MAX_TRIES ]; then
75
+ echo "❌ SSM registration timeout - instance may not have proper IAM role or SSM agent"
76
+ echo "Checking instance details for debugging..."
77
+ aws ec2 describe-instances --region "$AWS_REGION" --instance-ids "$INSTANCE_ID" \
78
+ --query 'Reservations[0].Instances[0].{State:State.Name,IAMProfile:IamInstanceProfile.Arn,SecurityGroups:SecurityGroups[].GroupId}' \
79
+ --output table
80
+ exit 2
81
+ fi
82
+ echo "Instance not yet registered with SSM, waiting..."
83
+ sleep 10
84
+ done
85
+
86
+ # Now test SSM command execution
87
+ echo "Testing SSM command execution..."
88
+ TRIES=0; MAX_TRIES=30
89
+ while :; do
90
+ echo "Attempt $((TRIES+1))/$MAX_TRIES: Sending test SSM command..."
91
+
92
+ if CMD_JSON=$(aws ssm send-command \
93
+ --region "$AWS_REGION" \
94
+ --targets "Key=instanceIds,Values=$INSTANCE_ID" \
95
+ --document-name "AWS-RunPowerShellScript" \
96
+ --parameters 'commands=["echo SSM connectivity test successful"]' \
97
+ --output json 2>/dev/null); then
98
+
99
+ COMMAND_ID=$(jq -r '.Command.CommandId' <<<"$CMD_JSON")
100
+ echo "✓ SSM command sent successfully (Command ID: $COMMAND_ID)"
101
+
102
+ # Wait for command to complete and check status
103
+ echo "Waiting for command execution..."
104
+ if aws ssm wait command-executed --region "$AWS_REGION" --command-id "$COMMAND_ID" --instance-id "$INSTANCE_ID" 2>/dev/null; then
105
+ echo "✓ SSM connectivity confirmed"
106
+ break
107
+ else
108
+ echo "⚠ Command execution may have failed, checking status..."
109
+ CMD_STATUS=$(aws ssm get-command-invocation \
110
+ --region "$AWS_REGION" \
111
+ --command-id "$COMMAND_ID" \
112
+ --instance-id "$INSTANCE_ID" \
113
+ --query 'Status' \
114
+ --output text 2>/dev/null || echo "Unknown")
115
+ echo "Command status: $CMD_STATUS"
116
+
117
+ if [ "$CMD_STATUS" = "Success" ]; then
118
+ echo "✓ Command actually succeeded"
119
+ break
120
+ fi
121
+ fi
122
+ else
123
+ echo "⚠ Failed to send SSM command"
124
+ fi
125
+
126
+ TRIES=$((TRIES+1))
127
+ if [ $TRIES -ge $MAX_TRIES ]; then
128
+ echo "❌ SSM command execution timeout"
129
+ echo "Final debugging information:"
130
+
131
+ # Get SSM agent status
132
+ echo "SSM Agent status on instance:"
133
+ aws ssm describe-instance-information \
134
+ --region "$AWS_REGION" \
135
+ --filters "Key=InstanceIds,Values=$INSTANCE_ID" \
136
+ --query 'InstanceInformationList[0].{PingStatus:PingStatus,LastPingDateTime:LastPingDateTime,AgentVersion:AgentVersion}' \
137
+ --output table 2>/dev/null || echo "Could not retrieve SSM status"
138
+
139
+ exit 2
140
+ fi
141
+ echo "Retrying in 20 seconds..."
142
+ sleep 20
143
+ done
144
+
145
+ echo "Getting Public IP..."
146
+
147
+ # # --- 5) Get instance Public IP ---
148
+ DESC_JSON=$(aws ec2 describe-instances --region "$AWS_REGION" --instance-ids "$INSTANCE_ID" --output json)
149
+ PUBLIC_IP=$(jq -r '.Reservations[0].Instances[0].PublicIpAddress // empty' <<<"$DESC_JSON")
150
+ [ -n "$PUBLIC_IP" ] || PUBLIC_IP="No public IP assigned"
151
+
152
+ # echo "Getting Websocket Port..."
153
+
154
+
155
+ # --- 6) Read WebSocket config JSON ---
156
+ echo "Reading WebSocket configuration from: $WS_CONFIG_PATH"
157
+ READ_JSON=$(aws ssm send-command \
158
+ --region "$AWS_REGION" \
159
+ --instance-ids "$INSTANCE_ID" \
160
+ --document-name "AWS-RunPowerShellScript" \
161
+ --parameters "commands=[\"if (Test-Path '${WS_CONFIG_PATH}') { Get-Content -Raw '${WS_CONFIG_PATH}' } else { Write-Output 'Config file not found at ${WS_CONFIG_PATH}' }\"]" \
162
+ --output json)
163
+
164
+ READ_CMD_ID=$(jq -r '.Command.CommandId' <<<"$READ_JSON")
165
+ echo "WebSocket config read command ID: $READ_CMD_ID"
166
+
167
+ echo "Waiting for WebSocket config command to complete..."
168
+ aws ssm wait command-executed --region "$AWS_REGION" --command-id "$READ_CMD_ID" --instance-id "$INSTANCE_ID"
169
+
170
+ INVOC=$(aws ssm get-command-invocation \
171
+ --region "$AWS_REGION" \
172
+ --command-id "$READ_CMD_ID" \
173
+ --instance-id "$INSTANCE_ID" \
174
+ --output json)
175
+
176
+ STDOUT=$(jq -r '.StandardOutputContent // ""' <<<"$INVOC")
177
+ STDERR=$(jq -r '.StandardErrorContent // ""' <<<"$INVOC")
178
+ CMD_STATUS=$(jq -r '.Status // ""' <<<"$INVOC")
179
+
180
+ echo "WebSocket config command status: $CMD_STATUS"
181
+ if [ -n "$STDERR" ] && [ "$STDERR" != "null" ]; then
182
+ echo "WebSocket config stderr: $STDERR"
183
+ fi
184
+ echo "WebSocket config raw output: $STDOUT"
185
+
186
+ # --- 7) Output results ---
187
+ echo "Setup complete!"
188
+ echo "PUBLIC_IP=$PUBLIC_IP"
189
+ echo "INSTANCE_ID=$INSTANCE_ID"
190
+ echo "AWS_REGION=$AWS_REGION"
@@ -1,4 +1,4 @@
1
- version: 5.7.7
1
+ version: 6.0.0
2
2
  session: 682f5aab811bd5a322c0e5a1
3
3
  steps:
4
4
  - prompt: click on sign in
@@ -9,5 +9,6 @@ steps:
9
9
  text: Sign In
10
10
  description: black button below the password field
11
11
  action: click
12
+ timeout: 5000
12
13
  - command: assert
13
14
  expect: an error shows that fields are required
@@ -1,7 +1,10 @@
1
1
  version: 6.0.0
2
2
  session: 6869c3e61445b2acbbdc0018
3
3
  steps:
4
- - prompt: enter a valid username, password, and sign in
4
+ - prompt: log in
5
+ - prompt: add an item to the cart
6
+ - prompt: click on the cart icon
7
+ - prompt: complete checkout
5
8
  - prompt: assert
6
9
  commands:
7
10
  - command: assert
@@ -19,3 +19,8 @@ steps:
19
19
  - command: scroll-until-image
20
20
  description: a brown colored house
21
21
  direction: down
22
+
23
+ - prompt: assert image of brown colored house appears on screen
24
+ commands:
25
+ - command: assert
26
+ expect: image of brown colored house appears on screen
@@ -0,0 +1,8 @@
1
+ version: 6.0.0
2
+ steps:
3
+ - prompt: asfdsdf
4
+ commands:
5
+ - command: exec
6
+ lang: js
7
+ code: |
8
+ throw new Error("test error");