Skip to content

Commit ccecd69

Browse files
CopilotMohabMohie
andauthored
docs: add 7 medium-priority undocumented SHAFT_ENGINE features (#444)
* Initial plan * Add medium-priority SHAFT documentation: YAML test data, GraphQL API, async element actions, XPath axis/ARIA/smart locators, mobile emulation, wrap existing WebDriver Agent-Logs-Url: https://github.com/ShaftHQ/shafthq.github.io/sessions/e8df246f-484a-4211-84d4-026a24ba9f23 Co-authored-by: MohabMohie <19201898+MohabMohie@users.noreply.github.com> * Address code review feedback: fix British spelling, clarify synchronize() blocking behavior, add Role import to example Agent-Logs-Url: https://github.com/ShaftHQ/shafthq.github.io/sessions/e8df246f-484a-4211-84d4-026a24ba9f23 Co-authored-by: MohabMohie <19201898+MohabMohie@users.noreply.github.com> --------- Signed-off-by: Mohab Mohie <Mohab.MohieElDeen@outlook.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: MohabMohie <19201898+MohabMohie@users.noreply.github.com> Co-authored-by: Mohab Mohie <Mohab.MohieElDeen@outlook.com>
1 parent 8a877b3 commit ccecd69

8 files changed

Lines changed: 984 additions & 196 deletions

File tree

docs/Keywords/API/Request_Builder.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,79 @@ api.post("serviceName").appendDefaultContentCharsetToContentTypeIfUndefined(fals
299299

300300
#### _** \*Please check the [Response Validations](https://shafthq.github.io/docs/Keywords/API/Response_Validations) as we can make many assertions and verifications on API response by using the Class [RestValidationsBuilder](https://shafthq.github.io/SHAFT_ENGINE/apidocs/com/shaft/validation/RestValidationsBuilder.html)\* **_
301301

302+
## GraphQL API Testing
303+
304+
SHAFT supports GraphQL requests out of the box through `sendGraphQlRequest()`. You can send queries, mutations, and fragments without any additional dependencies.
305+
306+
### Simple GraphQL Query
307+
308+
```java title="GraphQLSimpleQuery.java"
309+
SHAFT.API api = new SHAFT.API("https://api.example.com");
310+
311+
Response response = api.sendGraphQlRequest(
312+
"/graphql",
313+
"{ users { id name email } }"
314+
).perform();
315+
316+
api.assertThatResponse()
317+
.extractedJsonValue("$.data.users[0].name")
318+
.isEqualTo("Alice")
319+
.perform();
320+
```
321+
322+
### GraphQL Query with Variables
323+
324+
```java title="GraphQLWithVariables.java"
325+
SHAFT.API api = new SHAFT.API("https://api.example.com");
326+
327+
String query = "query GetUser($id: ID!) { user(id: $id) { name email role } }";
328+
String variables = "{\"id\": \"123\"}";
329+
330+
api.sendGraphQlRequest("/graphql", query, variables).perform();
331+
332+
api.assertThatResponse()
333+
.extractedJsonValue("$.data.user.email")
334+
.contains("@example.com")
335+
.perform();
336+
```
337+
338+
### GraphQL with Authentication Header
339+
340+
```java title="GraphQLWithAuth.java"
341+
SHAFT.API api = new SHAFT.API("https://api.example.com");
342+
343+
api.sendGraphQlRequest("/graphql", "{ me { name } }")
344+
.addHeader("Authorization", "Bearer mytoken123")
345+
.perform();
346+
347+
api.assertThatResponse().body().contains("\"name\"").perform();
348+
```
349+
350+
### GraphQL Mutation
351+
352+
```java title="GraphQLMutation.java"
353+
SHAFT.API api = new SHAFT.API("https://api.example.com");
354+
355+
String mutation = "mutation CreateUser($input: CreateUserInput!) { createUser(input: $input) { id name } }";
356+
String variables = "{\"input\": {\"name\": \"Bob\", \"email\": \"bob@example.com\"}}";
357+
358+
api.sendGraphQlRequest("/graphql", mutation, variables)
359+
.addHeader("Authorization", "Bearer admintoken")
360+
.setTargetStatusCode(200)
361+
.perform();
362+
363+
api.assertThatResponse()
364+
.extractedJsonValue("$.data.createUser.name")
365+
.isEqualTo("Bob")
366+
.perform();
367+
```
368+
369+
:::tip
370+
GraphQL always uses HTTP `POST` under the hood. SHAFT automatically sets the `Content-Type: application/json` header and wraps your query and variables in the correct payload format.
371+
:::
372+
373+
---
374+
302375
## Sample Code Snippet
303376
```java
304377
public class Test_Api {
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
---
2+
id: Async_Element_Actions
3+
title: Async Element Actions
4+
sidebar_label: Async Element Actions
5+
description: "Run SHAFT element actions concurrently using Java virtual threads — improve test speed with non-blocking parallel interactions."
6+
keywords: [SHAFT, async element actions, parallel element actions, virtual threads, concurrent selenium, non-blocking actions]
7+
tags: [web, element, async, performance, java21]
8+
---
9+
10+
## Overview
11+
12+
SHAFT supports non-blocking element actions via Java 21 virtual threads through `driver.async().element()`. Actions queued on the async executor run in parallel and return immediately, letting you kick off multiple interactions concurrently and synchronize at a chosen point.
13+
14+
This is useful when:
15+
- Filling a long form where fields are independent and can be typed into simultaneously
16+
- Triggering multiple UI actions that don't need to happen in strict sequence
17+
- Reducing wall-clock test execution time without sacrificing accuracy
18+
19+
---
20+
21+
## Basic Usage
22+
23+
### Parallel Form Fill
24+
25+
```java title="AsyncFormFill.java"
26+
// Queue multiple element actions — they start concurrently
27+
driver.async().element()
28+
.type(By.id("firstName"), "Alice")
29+
.type(By.id("lastName"), "Smith")
30+
.type(By.id("email"), "alice@example.com")
31+
.select(By.id("country"), "United States")
32+
.synchronize(); // blocks the current thread until all virtual threads complete; propagates any action failures as exceptions
33+
```
34+
35+
### Parallel Click and Screenshot
36+
37+
```java title="AsyncClickAndCapture.java"
38+
driver.async().element()
39+
.click(By.id("submitBtn"))
40+
.captureScreenshot(By.id("confirmationMessage"))
41+
.join(); // alternative to synchronize()
42+
```
43+
44+
:::note
45+
`.synchronize()` and `.join()` are equivalent — use whichever reads more naturally in context.
46+
:::
47+
48+
---
49+
50+
## Synchronisation Methods
51+
52+
After queuing async actions, call one of the following to block until all actions complete:
53+
54+
| Method | Description |
55+
|:-------|:------------|
56+
| `.synchronize()` | Waits for all queued async actions to complete |
57+
| `.join()` | Alias for `.synchronize()` |
58+
| `.sync()` | Alias for `.synchronize()` |
59+
60+
```java title="AsyncSynchronise.java"
61+
driver.async().element()
62+
.type(By.id("fieldA"), "value1")
63+
.type(By.id("fieldB"), "value2")
64+
.type(By.id("fieldC"), "value3")
65+
.sync(); // equivalent to .synchronize() and .join()
66+
```
67+
68+
---
69+
70+
## Complete Example: Registration Form
71+
72+
```java title="AsyncRegistrationTest.java"
73+
import com.shaft.driver.SHAFT;
74+
import org.openqa.selenium.By;
75+
import org.testng.annotations.*;
76+
77+
public class AsyncRegistrationTest {
78+
SHAFT.GUI.WebDriver driver;
79+
80+
@BeforeMethod
81+
public void setup() {
82+
driver = new SHAFT.GUI.WebDriver();
83+
}
84+
85+
@Test
86+
public void registerUserWithAsyncActions() {
87+
driver.browser().navigateToURL("https://example.com/register");
88+
89+
// Fill all fields in parallel using virtual threads
90+
driver.async().element()
91+
.type(By.id("firstName"), "Alice")
92+
.type(By.id("lastName"), "Smith")
93+
.type(By.id("email"), "alice@example.com")
94+
.type(By.id("phone"), "+1-555-0100")
95+
.select(By.id("country"), "United States")
96+
.synchronize();
97+
98+
// Proceed sequentially once the form is filled
99+
driver.element().click(By.id("submitBtn"));
100+
101+
driver.assertThat().browser().url().contains("/success").perform();
102+
}
103+
104+
@AfterMethod
105+
public void teardown() {
106+
driver.quit();
107+
}
108+
}
109+
```
110+
111+
---
112+
113+
## When to Use Async vs. Synchronous Actions
114+
115+
| Scenario | Recommendation |
116+
|:---------|:---------------|
117+
| Independent form fields with no DOM side-effects | ✅ Use async |
118+
| Fields where typing in one triggers a validation on another | ❌ Use standard sequential actions |
119+
| Bulk data entry where order doesn't matter | ✅ Use async |
120+
| Actions that depend on the result of a previous action | ❌ Use standard sequential actions |
121+
| Capturing screenshots while another action runs | ✅ Use async |
122+
123+
:::tip
124+
Async actions are best suited for **write** operations (type, select, click) that operate on independent elements. Avoid using them when one action's outcome affects the next element's state.
125+
:::
126+
127+
---
128+
129+
## Additional Resources
130+
131+
- [Element Actions](./Element_Actions) — standard synchronous element API
132+
- [SHAFT_ENGINE source — AsyncElementActions](https://github.com/ShaftHQ/SHAFT_ENGINE/tree/master/src/main/java/com/shaft/gui/element)

0 commit comments

Comments
 (0)