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.
- package/.github/workflows/acceptance-tests.yml +2 -0
- package/.github/workflows/acceptance-v6.yml +2 -0
- package/.github/workflows/lint.yml +4 -1
- package/.github/workflows/publish-canary.yml +2 -0
- package/.github/workflows/publish-latest.yml +1 -0
- package/.github/workflows/self-hosted.yml +102 -0
- package/.prettierignore +1 -0
- package/.vscode/settings.json +4 -1
- package/agent/events.js +1 -10
- package/agent/index.js +98 -55
- package/agent/interface.js +43 -6
- package/agent/lib/censorship.js +15 -10
- package/agent/lib/commander.js +31 -18
- package/agent/lib/commands.js +62 -17
- package/agent/lib/debugger-server.js +0 -5
- package/agent/lib/generator.js +2 -2
- package/agent/lib/sdk.js +2 -1
- package/agent/lib/source-mapper.js +1 -1
- package/debugger/index.html +1 -1
- package/docs/account/enterprise.mdx +8 -12
- package/docs/account/pricing.mdx +2 -2
- package/docs/account/projects.mdx +5 -0
- package/docs/apps/tauri-apps.mdx +361 -0
- package/docs/cli/overview.mdx +6 -6
- package/docs/commands/assert.mdx +1 -0
- package/docs/commands/hover-text.mdx +3 -1
- package/docs/commands/match-image.mdx +5 -4
- package/docs/commands/press-keys.mdx +6 -8
- package/docs/commands/scroll-until-image.mdx +8 -7
- package/docs/commands/scroll-until-text.mdx +7 -6
- package/docs/commands/wait-for-image.mdx +5 -4
- package/docs/commands/wait-for-text.mdx +6 -5
- package/docs/docs.json +42 -40
- package/docs/getting-started/playwright.mdx +342 -0
- package/docs/getting-started/self-hosting.mdx +370 -0
- package/docs/getting-started/vscode.mdx +67 -56
- package/docs/guide/dashcam.mdx +118 -0
- package/docs/guide/environment-variables.mdx +5 -5
- package/docs/images/content/self-hosted/launchtemplateid.png +0 -0
- package/docs/images/content/vscode/ide-full.png +0 -0
- package/docs/images/content/vscode/running.png +0 -0
- package/docs/overview/comparison.mdx +22 -39
- package/docs/overview/quickstart.mdx +84 -32
- package/docs/styles.css +10 -1
- package/interfaces/cli/commands/generate.js +3 -0
- package/interfaces/cli/lib/base.js +27 -5
- package/interfaces/cli/utils/factory.js +17 -4
- package/interfaces/logger.js +4 -4
- package/interfaces/readline.js +1 -1
- package/package.json +3 -3
- package/schema.json +21 -0
- package/setup/aws/cloudformation.yaml +463 -0
- package/setup/aws/spawn-runner.sh +190 -0
- package/testdriver/acceptance/hover-text.yaml +2 -1
- package/testdriver/acceptance/prompt.yaml +4 -1
- package/testdriver/acceptance/scroll-until-image.yaml +5 -0
- package/testdriver/edge-cases/js-exception.yaml +8 -0
- package/testdriver/edge-cases/js-promise.yaml +19 -0
- package/testdriver/edge-cases/lifecycle/postrun.yaml +10 -0
- package/testdriver/edge-cases/success-test.yaml +9 -0
- package/testdriver/examples/web/lifecycle/postrun.yaml +7 -0
- package/testdriver/examples/web/lifecycle/{provision.yaml → prerun.yaml} +6 -0
- package/testdriver/lifecycle/postrun.yaml +7 -0
- 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:
|
|
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:
|
|
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
|