Quick start
Start with the path that matches your repository. New projects should be generated, existing projects should be upgraded, MCP should be connected only after the project runs, and manual setup is the fallback for constrained repos.
Choose your path
Workflow map
Three-minute useful test
The fastest useful evaluation is: generate a project, run the included sample, open the evidence, then decide whether SHAFT removed enough setup work to keep.
- Create a generated project from Installation.
- Run the project from its root:
mvn test
- Confirm Maven reports passing tests.
- Open the generated evidence under
allure-results and the generated Allure report under the project target output. - If the sample test, screenshots, logs, and report match what you expected, Star SHAFT on GitHub so you can find the project again and track releases.
For a browser-first smoke run, keep the generated defaults or use this minimal configuration:
targetBrowserName=CHROME
headlessExecution=true
createAnimatedGif=true
allure.singleFile=true
New project generation
Use this when you are creating a SHAFT project from scratch.
The SHAFT Project Generator is the recommended way to create a new project.
- Open the Project Generator.
- Choose the test runner: TestNG, JUnit, or Cucumber.
- Choose the test surface: Web, Mobile, or API.
- Download and extract the generated project.
- Run the first build from the project root:
mvn test
Generated projects include matching sample tests, reporting setup, optional
GitHub Actions and Dependabot files, and shared IntelliJ IDEA run templates
configured with @argFiles.
Existing project upgrade
Use this when you already have Selenium, Appium, REST Assured, Cucumber, legacy SHAFT, or modular SHAFT code.
The automated upgrade tool updates Maven dependencies, validates the result, and rolls changes back if compilation fails.
Open the automated upgrade and rollback guide from the existing project and run its canonical upgrade command. That page owns the script download, OS-neutral invocation, flags, rollback behavior, and optional repair flow.
MCP integration
Use this after a generated, upgraded, or manual project already compiles. MCP is for agent operations around a working SHAFT project, not a replacement for project setup.
- Connect your client from the SHAFT MCP guide.
- Keep model credentials in the MCP client or provider configuration, not in source files.
- Use Capture and Doctor output as evidence, then review generated code before committing it.
Use the MCP command reference for Capture, Doctor, and remote-server commands. Use the manual configuration section only when the installer cannot update your selected client.
Manual creation
Use this only when the Project Generator or upgrader cannot be used, such as a locked enterprise repository where you must edit files by hand.
Add SHAFT dependencies
Add the BOM and required engine dependency to pom.xml:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.github.shafthq</groupId>
<artifactId>shaft-bom</artifactId>
<version>10.2.20260628</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.github.shafthq</groupId>
<artifactId>shaft-engine</artifactId>
</dependency>
</dependencies>
Add optional modules only when a test uses that capability. For example,
reference-image assertions need shaft-visual; ordinary browser, mobile, API,
database, CLI, screenshots, reporting, and test data workflows use
shaft-engine.
Create the test skeleton
- Create
src/test/java/testPackage. - Create
src/test/resources/testDataFiles/simpleJSON.json. - Copy this test data:
{
"searchQuery": "SHAFT_Engine",
"expectedTitle": "DuckDuckGo",
"unexpectedInFirstResult": "Nope"
}
- Pick one runner sample below.
- Run the selected test from your IDE or with Maven.
On the first test run, SHAFT creates src/main/resources/properties, generates
default properties, runs in minimalistic test run mode, and self-configures
listeners under src/test/resources/META-INF/services. On later runs, the
Allure execution report opens automatically in your default browser.
Project Generator projects already include shared IntelliJ IDEA run templates.
For older or manually created projects, open Run > Edit Configurations > Edit
configuration templates and set Shorten command line to
@argFiles (Java 9+) for the Java, JUnit, TestNG, and Cucumber Java templates
you use. Delete stale temporary runs created before changing the template.
TestNG sample
Create src/test/java/testPackage/TestClass.java:
package testPackage;
import com.shaft.driver.SHAFT;
import com.shaft.gui.internal.locator.Locator;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class TestClass {
SHAFT.GUI.WebDriver driver;
SHAFT.TestData.JSON testData;
String targetUrl = "https://duckduckgo.com/";
By logo = By.xpath("//div[contains(@class,'container_fullWidth__1H_L8')]//img");
By searchBox = Locator.hasAnyTagName().hasAttribute("name", "q").build();
By firstSearchResult = Locator.hasTagName("article").isFirst().build();
@Test
public void navigateToDuckDuckGoAndAssertBrowserTitleIsDisplayedCorrectly() {
driver.browser().navigateToURL(targetUrl)
.and().assertThat().title().contains(testData.get("expectedTitle"));
}
@Test
public void navigateToDuckDuckGoAndAssertLogoIsDisplayedCorrectly() {
driver.browser().navigateToURL(targetUrl)
.and().element().assertThat(logo).isVisible();
}
@Test
public void searchForQueryAndAssert() {
driver.browser().navigateToURL(targetUrl)
.and().element().type(searchBox, testData.get("searchQuery") + Keys.ENTER)
.and().assertThat(firstSearchResult).text().doesNotEqual(testData.get("unexpectedInFirstResult"));
}
@BeforeClass
public void beforeClass() {
testData = new SHAFT.TestData.JSON("simpleJSON.json");
}
@BeforeMethod
public void beforeMethod() {
driver = new SHAFT.GUI.WebDriver();
}
@AfterMethod
public void afterMethod() {
driver.quit();
}
}
JUnit sample
Generated JUnit projects enable the SHAFT listener and extension through
src/test/resources/junit-platform.properties. Existing projects should keep
junit.jupiter.extensions.autodetection.enabled=true in that file.
Create src/test/java/testPackage/TestClass.java:
package testPackage;
import com.shaft.driver.SHAFT;
import com.shaft.gui.internal.locator.Locator;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
public class TestClass {
private SHAFT.GUI.WebDriver driver;
private static SHAFT.TestData.JSON testData;
String targetUrl = "https://duckduckgo.com/";
By logo = By.xpath("//div[contains(@class,'container_fullWidth__1H_L8')]//img");
By searchBox = Locator.hasAnyTagName().hasAttribute("name", "q").build();
By firstSearchResult = Locator.hasTagName("article").isFirst().build();
@Test
public void navigateToDuckDuckGoAndAssertBrowserTitleIsDisplayedCorrectly() {
driver.browser().navigateToURL(targetUrl)
.and().assertThat().title().contains(testData.get("expectedTitle"));
}
@Test
public void navigateToDuckDuckGoAndAssertLogoIsDisplayedCorrectly() {
driver.browser().navigateToURL(targetUrl)
.and().element().assertThat(logo).isVisible();
}
@Test
public void searchForQueryAndAssert() {
driver.browser().navigateToURL(targetUrl)
.and().element().type(searchBox, testData.get("searchQuery") + Keys.ENTER)
.and().assertThat(firstSearchResult).text().doesNotEqual(testData.get("unexpectedInFirstResult"));
}
@BeforeAll
public static void beforeAll() {
testData = new SHAFT.TestData.JSON("simpleJSON.json");
}
@BeforeEach
public void beforeEach() {
driver = new SHAFT.GUI.WebDriver();
}
@AfterEach
public void afterEach() {
driver.quit();
}
}
Cucumber sample
Create src/test/java/cucumberTestRunner/CucumberTests.java:
package cucumberTestRunner;
import io.cucumber.testng.AbstractTestNGCucumberTests;
import org.testng.annotations.Listeners;
@Listeners({com.shaft.listeners.TestNGListener.class})
public class CucumberTests extends AbstractTestNGCucumberTests {
}
Create src/test/java/customCucumberSteps/StepDefinitions.java:
package customCucumberSteps;
import com.shaft.driver.SHAFT;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
public class StepDefinitions {
private SHAFT.GUI.WebDriver driver;
private SHAFT.TestData.JSON testData;
@Given("I open the target browser")
public void i_open_the_target_browser() {
driver = new SHAFT.GUI.WebDriver();
testData = new SHAFT.TestData.JSON("simpleJSON.json");
}
@When("I navigate to {string}")
public void i_navigate_to(String url) {
driver.browser().navigateToURL(url);
}
@When("I search for {string}")
public void i_search_for(String query) {
By searchBox = By.name("q");
driver.element().type(searchBox, query + Keys.ENTER);
}
@Then("I should see the page title contains {string}")
public void i_should_see_the_page_title_contains(String expectedTitle) {
driver.assertThat().browser().title().contains(expectedTitle);
}
@Then("I close the browser")
public void i_close_the_browser() {
driver.quit();
}
}
Create src/test/resources/features/search.feature:
Feature: Search functionality
Scenario: Verify DuckDuckGo search
Given I open the target browser
When I navigate to "https://duckduckgo.com/"
Then I should see the page title contains "DuckDuckGo"
When I search for "SHAFT_Engine"
Then I close the browser
For manually created Cucumber projects, edit the Cucumber Java run configuration template and add:
--plugin com.shaft.listeners.CucumberFeatureListener
Run and configure
Use the properties reference to
configure SHAFT. After upgrading to a new major release, it is sometimes
recommended to delete src/main/resources/properties and allow SHAFT to
regenerate the defaults by running any test method.
Join our
to get notified by email when a new release is pushed out.
Optional modular integrations
Use shaft-engine first. Add optional modules only when a test calls their
capability.
| Capability | Add this module |
|---|---|
| Reference-image assertions, negative reference-image assertions, image-path touch actions | shaft-visual |
| BrowserStack SDK interception and YAML orchestration | shaft-browserstack |
| Local non-headless desktop recording | shaft-video |
| SikuliX image-based desktop automation | shaft-sikulix |
| Deterministic web locator recovery | shaft-heal plus healing.strategy=shaft-heal |
Reference-image assertions require shaft-visual:
driver.browser().navigateToURL(targetUrl)
.and().element().assertThat(logo).matchesReferenceImage();
Ordinary screenshots, highlighted report screenshots, API tests, Appium locator
actions, database tests, CLI tests, direct BrowserStack sessions, and
Appium-native recording need only shaft-engine.
Windows desktop Appium sessions also stay in shaft-engine; use
shaft-sikulix only when the test needs SikuliX image matching.
Use the module selection and migration guide for the complete method matrix.