Repository: karatelabs/karate Branch: master Commit: c07caae8b5fa Files: 1264 Total size: 3.0 MB Directory structure: gitextract_0ovktbaz/ ├── .github/ │ ├── CODE_OF_CONDUCT.md │ ├── CONTRIBUTING.md │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE.md │ ├── PULL_REQUEST_TEMPLATE.md │ ├── dependabot.yml │ └── workflows/ │ ├── codeql.yml │ ├── delete-workflow-runs.yml │ ├── jdk-compat.yml │ ├── maven-build.yml │ └── maven-release.yml ├── .gitignore ├── LICENSE ├── README.md ├── SECURITY.md ├── _config.yml ├── _includes/ │ ├── nav.html │ └── toc.html ├── build-docker.sh ├── examples/ │ ├── README.md │ ├── consumer-driven-contracts/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── payment-consumer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ └── java/ │ │ │ │ └── payment/ │ │ │ │ └── consumer/ │ │ │ │ └── Consumer.java │ │ │ └── test/ │ │ │ └── java/ │ │ │ ├── logback-test.xml │ │ │ └── payment/ │ │ │ └── consumer/ │ │ │ ├── ConsumerIntegrationAgainstMockTest.java │ │ │ └── ConsumerIntegrationTest.java │ │ ├── payment-producer/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ └── java/ │ │ │ │ └── payment/ │ │ │ │ └── producer/ │ │ │ │ ├── Payment.java │ │ │ │ ├── PaymentService.java │ │ │ │ └── ServerStartedInitializingBean.java │ │ │ └── test/ │ │ │ └── java/ │ │ │ ├── karate-config.js │ │ │ ├── logback-test.xml │ │ │ └── payment/ │ │ │ └── producer/ │ │ │ ├── contract/ │ │ │ │ ├── PaymentContractTest.java │ │ │ │ └── payment-contract.feature │ │ │ └── mock/ │ │ │ ├── PaymentContractAgainstMockTest.java │ │ │ ├── payment-mock.feature │ │ │ └── test.feature │ │ └── pom.xml │ ├── gatling/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── build.gradle │ │ ├── pom.xml │ │ └── src/ │ │ └── test/ │ │ └── java/ │ │ ├── karate-config.js │ │ ├── logback-test.xml │ │ └── mock/ │ │ ├── CatsGatlingSimulation.scala │ │ ├── CatsKarateSimulation.scala │ │ ├── MockUtils.java │ │ ├── cats-create.feature │ │ ├── cats-delete-one.feature │ │ ├── cats-delete.feature │ │ ├── custom-rpc.feature │ │ ├── feeder.feature │ │ └── mock.feature │ ├── image-comparison/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── test/ │ │ └── java/ │ │ ├── karate-config.js │ │ ├── logback-test.xml │ │ └── ui/ │ │ ├── 1_establish_baseline.feature │ │ ├── 2_compare_baseline.feature │ │ ├── 3_custom_rebase.feature │ │ ├── 4_generic_rebase.feature │ │ ├── 5_custom_config.feature │ │ ├── 6_outline.feature │ │ ├── 7_api.feature │ │ ├── ImageComparisonRunner.java │ │ ├── MockRunner.java │ │ ├── html/ │ │ │ └── index.html │ │ └── screenshots/ │ │ └── config/ │ │ ├── loaded_phone.json │ │ ├── loaded_tablet.json │ │ ├── loading_phone.json │ │ └── loading_tablet.json │ ├── mobile-test/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── test/ │ │ └── java/ │ │ ├── android/ │ │ │ ├── AndroidTest.java │ │ │ └── android.feature │ │ ├── karate-config.js │ │ └── logback-test.xml │ ├── profiling-test/ │ │ ├── pom.xml │ │ └── src/ │ │ └── test/ │ │ └── java/ │ │ ├── karate-config.js │ │ ├── logback-test.xml │ │ └── perf/ │ │ ├── Main.java │ │ ├── TestSimulation.scala │ │ ├── TestUtils.java │ │ ├── called.feature │ │ ├── main.feature │ │ └── mock.feature │ ├── robot-test/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── test/ │ │ └── java/ │ │ ├── karate-config.js │ │ ├── logback-test.xml │ │ ├── mac/ │ │ │ ├── ChromeRunner.java │ │ │ └── chrome.feature │ │ └── win/ │ │ ├── CalcRunner.java │ │ └── calc.feature │ ├── ui-test/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── test/ │ │ └── java/ │ │ ├── logback-test.xml │ │ └── ui/ │ │ ├── MockRunner.java │ │ ├── UiRunner.java │ │ ├── html/ │ │ │ ├── index.html │ │ │ ├── karate.js │ │ │ └── page-01.html │ │ └── test.feature │ └── zip-release/ │ └── src/ │ └── demo/ │ ├── api/ │ │ ├── httpbin.feature │ │ └── users.feature │ ├── mock/ │ │ ├── cats-mock.feature │ │ ├── cats-test.feature │ │ └── cats.html │ ├── robot/ │ │ ├── calc-old.feature │ │ └── calc.feature │ └── web/ │ └── google.feature ├── jbang-catalog.json ├── karate-archetype/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── resources/ │ ├── META-INF/ │ │ └── maven/ │ │ └── archetype-metadata.xml │ └── archetype-resources/ │ ├── .gitignore │ ├── pom.xml │ └── src/ │ └── test/ │ └── java/ │ ├── examples/ │ │ ├── ExamplesTest.java │ │ └── users/ │ │ ├── UsersRunner.java │ │ └── users.feature │ ├── karate-config.js │ └── logback-test.xml ├── karate-core/ │ ├── README.md │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── antlr4/ │ │ │ └── com/ │ │ │ └── intuit/ │ │ │ └── karate/ │ │ │ └── core/ │ │ │ ├── KarateLexer.g4 │ │ │ └── KarateParser.g4 │ │ └── java/ │ │ ├── com/ │ │ │ └── intuit/ │ │ │ └── karate/ │ │ │ ├── Actions.java │ │ │ ├── Constants.java │ │ │ ├── FileUtils.java │ │ │ ├── Http.java │ │ │ ├── ImageComparison.java │ │ │ ├── Json.java │ │ │ ├── JsonUtils.java │ │ │ ├── KarateException.java │ │ │ ├── LogAppender.java │ │ │ ├── Logger.java │ │ │ ├── Main.java │ │ │ ├── Match.java │ │ │ ├── MatchOperation.java │ │ │ ├── MatchOperator.java │ │ │ ├── MatchStep.java │ │ │ ├── PerfContext.java │ │ │ ├── PerfHook.java │ │ │ ├── Results.java │ │ │ ├── Runner.java │ │ │ ├── RuntimeHook.java │ │ │ ├── ScenarioActions.java │ │ │ ├── StringUtils.java │ │ │ ├── Suite.java │ │ │ ├── XmlUtils.java │ │ │ ├── core/ │ │ │ │ ├── Action.java │ │ │ │ ├── AfterHookType.java │ │ │ │ ├── AssignType.java │ │ │ │ ├── AutoDef.java │ │ │ │ ├── Background.java │ │ │ │ ├── Channel.java │ │ │ │ ├── ChannelFactory.java │ │ │ │ ├── Config.java │ │ │ │ ├── Embed.java │ │ │ │ ├── ExamplesTable.java │ │ │ │ ├── Feature.java │ │ │ │ ├── FeatureCall.java │ │ │ │ ├── FeatureParser.java │ │ │ │ ├── FeatureResult.java │ │ │ │ ├── FeatureRuntime.java │ │ │ │ ├── FeatureSection.java │ │ │ │ ├── MockHandler.java │ │ │ │ ├── MockInterceptor.java │ │ │ │ ├── MockServer.java │ │ │ │ ├── ParallelProcessor.java │ │ │ │ ├── ParserErrorListener.java │ │ │ │ ├── PerfEvent.java │ │ │ │ ├── Plugin.java │ │ │ │ ├── PluginFactory.java │ │ │ │ ├── Result.java │ │ │ │ ├── RuntimeHookFactory.java │ │ │ │ ├── Scenario.java │ │ │ │ ├── ScenarioBridge.java │ │ │ │ ├── ScenarioCall.java │ │ │ │ ├── ScenarioEngine.java │ │ │ │ ├── ScenarioFileReader.java │ │ │ │ ├── ScenarioIterator.java │ │ │ │ ├── ScenarioOutline.java │ │ │ │ ├── ScenarioOutlineResult.java │ │ │ │ ├── ScenarioResult.java │ │ │ │ ├── ScenarioRuntime.java │ │ │ │ ├── Step.java │ │ │ │ ├── StepResult.java │ │ │ │ ├── StepRuntime.java │ │ │ │ ├── SyncExecutorService.java │ │ │ │ ├── Table.java │ │ │ │ ├── Tag.java │ │ │ │ ├── TagResults.java │ │ │ │ ├── Tags.java │ │ │ │ ├── TimelineResults.java │ │ │ │ ├── Variable.java │ │ │ │ └── When.java │ │ │ ├── driver/ │ │ │ │ ├── DevToolsDriver.java │ │ │ │ ├── DevToolsMessage.java │ │ │ │ ├── DevToolsMock.java │ │ │ │ ├── DevToolsWait.java │ │ │ │ ├── DockerTarget.java │ │ │ │ ├── Driver.java │ │ │ │ ├── DriverElement.java │ │ │ │ ├── DriverMouse.java │ │ │ │ ├── DriverOptions.java │ │ │ │ ├── DriverRunner.java │ │ │ │ ├── Element.java │ │ │ │ ├── ElementFinder.java │ │ │ │ ├── Finder.java │ │ │ │ ├── Frame.java │ │ │ │ ├── Input.java │ │ │ │ ├── Key.java │ │ │ │ ├── Keys.java │ │ │ │ ├── MissingElement.java │ │ │ │ ├── MissingFinder.java │ │ │ │ ├── Mouse.java │ │ │ │ ├── Target.java │ │ │ │ ├── WebDriver.java │ │ │ │ ├── appium/ │ │ │ │ │ ├── AndroidDriver.java │ │ │ │ │ ├── AppiumDriver.java │ │ │ │ │ ├── IosDriver.java │ │ │ │ │ └── MobileDriverOptions.java │ │ │ │ ├── chrome/ │ │ │ │ │ ├── Chrome.java │ │ │ │ │ └── ChromeWebDriver.java │ │ │ │ ├── firefox/ │ │ │ │ │ └── GeckoWebDriver.java │ │ │ │ ├── microsoft/ │ │ │ │ │ ├── EdgeChromium.java │ │ │ │ │ ├── IeWebDriver.java │ │ │ │ │ ├── MsEdgeDriver.java │ │ │ │ │ ├── MsWebDriver.java │ │ │ │ │ └── WinAppDriver.java │ │ │ │ ├── playwright/ │ │ │ │ │ ├── PlaywrightDriver.java │ │ │ │ │ ├── PlaywrightMessage.java │ │ │ │ │ └── PlaywrightWait.java │ │ │ │ └── safari/ │ │ │ │ └── SafariWebDriver.java │ │ │ ├── graal/ │ │ │ │ ├── JsArray.java │ │ │ │ ├── JsEngine.java │ │ │ │ ├── JsFunction.java │ │ │ │ ├── JsLambda.java │ │ │ │ ├── JsList.java │ │ │ │ ├── JsMap.java │ │ │ │ ├── JsValue.java │ │ │ │ ├── JsXml.java │ │ │ │ └── Methods.java │ │ │ ├── http/ │ │ │ │ ├── ApacheHttpClient.java │ │ │ │ ├── ArmeriaHttpClient.java │ │ │ │ ├── AwsLambdaHandler.java │ │ │ │ ├── Cookies.java │ │ │ │ ├── CustomHttpRequestRetryHandler.java │ │ │ │ ├── GenericHttpHeaderTracking.java │ │ │ │ ├── HttpClient.java │ │ │ │ ├── HttpClientFactory.java │ │ │ │ ├── HttpConstants.java │ │ │ │ ├── HttpHeaderTracking.java │ │ │ │ ├── HttpLogModifier.java │ │ │ │ ├── HttpLogger.java │ │ │ │ ├── HttpRequest.java │ │ │ │ ├── HttpRequestBuilder.java │ │ │ │ ├── HttpServer.java │ │ │ │ ├── HttpServerHandler.java │ │ │ │ ├── HttpUtils.java │ │ │ │ ├── JvmSessionStore.java │ │ │ │ ├── LenientTrustManager.java │ │ │ │ ├── MultiPartBuilder.java │ │ │ │ ├── ProxyClientHandler.java │ │ │ │ ├── ProxyContext.java │ │ │ │ ├── ProxyRemoteHandler.java │ │ │ │ ├── ProxyRequest.java │ │ │ │ ├── ProxyResponse.java │ │ │ │ ├── ProxyServer.java │ │ │ │ ├── RedirectException.java │ │ │ │ ├── Request.java │ │ │ │ ├── RequestCycle.java │ │ │ │ ├── RequestFilter.java │ │ │ │ ├── RequestHandler.java │ │ │ │ ├── ResourceType.java │ │ │ │ ├── Response.java │ │ │ │ ├── ResponseBuilder.java │ │ │ │ ├── ResponseFilter.java │ │ │ │ ├── ServerConfig.java │ │ │ │ ├── ServerContext.java │ │ │ │ ├── ServerHandler.java │ │ │ │ ├── Session.java │ │ │ │ ├── SessionStore.java │ │ │ │ ├── SslContextFactory.java │ │ │ │ ├── WebSocketClient.java │ │ │ │ ├── WebSocketClientHandler.java │ │ │ │ ├── WebSocketListener.java │ │ │ │ ├── WebSocketOptions.java │ │ │ │ ├── WebSocketProxyHandler.java │ │ │ │ ├── WebSocketProxyServer.java │ │ │ │ ├── WebSocketServerBase.java │ │ │ │ └── cert/ │ │ │ │ ├── SelfSignedCertGenerator.java │ │ │ │ └── ThreadLocalInsecureRandom.java │ │ │ ├── report/ │ │ │ │ ├── Report.java │ │ │ │ ├── ReportUtils.java │ │ │ │ ├── Resemble.js │ │ │ │ ├── SuiteReports.java │ │ │ │ ├── karate-feature.html │ │ │ │ ├── karate-leftnav.html │ │ │ │ ├── karate-posthog.html │ │ │ │ ├── karate-report.css │ │ │ │ ├── karate-report.js │ │ │ │ ├── karate-step.html │ │ │ │ ├── karate-summary.html │ │ │ │ ├── karate-tags.html │ │ │ │ └── karate-timeline.html │ │ │ ├── resource/ │ │ │ │ ├── FileResource.java │ │ │ │ ├── JarResource.java │ │ │ │ ├── MemoryResource.java │ │ │ │ ├── Resource.java │ │ │ │ ├── ResourceResolver.java │ │ │ │ └── ResourceUtils.java │ │ │ ├── shell/ │ │ │ │ ├── Command.java │ │ │ │ ├── Console.java │ │ │ │ ├── FileLogAppender.java │ │ │ │ ├── StopListenerThread.java │ │ │ │ ├── Stoppable.java │ │ │ │ └── StringLogAppender.java │ │ │ └── template/ │ │ │ ├── KaHxAnyAttrProcessor.java │ │ │ ├── KaHxMethodAttrProcessor.java │ │ │ ├── KaHxValsAttrProcessor.java │ │ │ ├── KaLinkAttrProcessor.java │ │ │ ├── KaScriptAttrProcessor.java │ │ │ ├── KaScriptElemProcessor.java │ │ │ ├── KaSetElemProcessor.java │ │ │ ├── KarateAttributeTagProcessor.java │ │ │ ├── KarateEachTagProcessor.java │ │ │ ├── KarateEngineContext.java │ │ │ ├── KarateExpression.java │ │ │ ├── KarateScriptDialect.java │ │ │ ├── KarateServerDialect.java │ │ │ ├── KarateStandardDialect.java │ │ │ ├── KarateTemplateEngine.java │ │ │ ├── KarateTemplateResource.java │ │ │ ├── KarateWithTagProcessor.java │ │ │ ├── ResourceHtmlTemplateResolver.java │ │ │ ├── ServerHtmlTemplateResolver.java │ │ │ ├── StringHtmlTemplateResolver.java │ │ │ ├── StringTemplateResource.java │ │ │ ├── TemplateContext.java │ │ │ └── TemplateUtils.java │ │ ├── karate-meta.properties │ │ ├── logback-fatjar.xml │ │ ├── logback-nofile.xml │ │ └── nodebug/ │ │ └── io/ │ │ └── karatelabs/ │ │ └── LenientSslConnectionSocketFactory.java │ └── test/ │ ├── java/ │ │ ├── com/ │ │ │ └── intuit/ │ │ │ └── karate/ │ │ │ ├── ComplexPojo.java │ │ │ ├── FileUtilsTest.java │ │ │ ├── ImageComparisonTest.java │ │ │ ├── JsonTest.java │ │ │ ├── JsonUtilsTest.java │ │ │ ├── MainTest.java │ │ │ ├── MatchStepTest.java │ │ │ ├── MatchTest.java │ │ │ ├── SimplePojo.java │ │ │ ├── StringUtilsTest.java │ │ │ ├── SystemPropertiesTest.java │ │ │ ├── TestUtils.java │ │ │ ├── ThreadCountTest.java │ │ │ ├── XmlUtilsTest.java │ │ │ ├── core/ │ │ │ │ ├── CallResponseTest.java │ │ │ │ ├── Cat.java │ │ │ │ ├── ComplexPojo.java │ │ │ │ ├── DummyClient.java │ │ │ │ ├── DummyUiTest.java │ │ │ │ ├── FeatureFailRunner.java │ │ │ │ ├── FeatureResultTest.java │ │ │ │ ├── FeatureRuntimeTest.java │ │ │ │ ├── HttpMockHandlerRunner.java │ │ │ │ ├── HttpMockHandlerTest.java │ │ │ │ ├── JsStubGenerator.java │ │ │ │ ├── KarateHttpMockHandlerTest.java │ │ │ │ ├── KarateMockHandlerTest.java │ │ │ │ ├── MockClient.java │ │ │ │ ├── MockHandlerTest.java │ │ │ │ ├── MockUtils.java │ │ │ │ ├── PaymentsMockRunner.java │ │ │ │ ├── PaymentsRunner.java │ │ │ │ ├── PerfHookTest.java │ │ │ │ ├── PrintTest.java │ │ │ │ ├── ScenarioBridgeTest.java │ │ │ │ ├── ScenarioEngineTest.java │ │ │ │ ├── ScenarioOutlineResultTest.java │ │ │ │ ├── ScenarioRuntimeTest.java │ │ │ │ ├── SimplePojo.java │ │ │ │ ├── StaticUtils.java │ │ │ │ ├── StepRuntimeTest.java │ │ │ │ ├── TagsTest.java │ │ │ │ ├── TestLogAppender.java │ │ │ │ ├── VariableTest.java │ │ │ │ ├── abort.feature │ │ │ │ ├── align.feature │ │ │ │ ├── big-decimal.feature │ │ │ │ ├── call-arg-called.feature │ │ │ │ ├── call-arg-common.feature │ │ │ │ ├── call-arg-null-called.feature │ │ │ │ ├── call-arg-null.feature │ │ │ │ ├── call-arg.feature │ │ │ │ ├── call-by-tag-called.feature │ │ │ │ ├── call-by-tag.feature │ │ │ │ ├── call-feature-called.feature │ │ │ │ ├── call-feature.feature │ │ │ │ ├── call-js-called.feature │ │ │ │ ├── call-js.feature │ │ │ │ ├── call-jsonpath-called.feature │ │ │ │ ├── call-jsonpath.feature │ │ │ │ ├── call-response-called.feature │ │ │ │ ├── call-response-mock.feature │ │ │ │ ├── call-response.feature │ │ │ │ ├── call-self.feature │ │ │ │ ├── call-single-fail-called.feature │ │ │ │ ├── call-single-fail.feature │ │ │ │ ├── call-single-tag-called.feature │ │ │ │ ├── call-single-tag.feature │ │ │ │ ├── callSingleFeature.feature │ │ │ │ ├── callSingleScenario.feature │ │ │ │ ├── called1.feature │ │ │ │ ├── called2.feature │ │ │ │ ├── called3-caller1.js │ │ │ │ ├── called3-caller2.js │ │ │ │ ├── called3.js │ │ │ │ ├── callonce-bg-called.feature │ │ │ │ ├── callonce-bg-outline.feature │ │ │ │ ├── callonce-bg.feature │ │ │ │ ├── callonce-config-called.feature │ │ │ │ ├── callonce-config.feature │ │ │ │ ├── callonce-global-called.feature │ │ │ │ ├── callonce-global.feature │ │ │ │ ├── configure-in-js.feature │ │ │ │ ├── copy-called-nested.feature │ │ │ │ ├── copy-called-overwrite.feature │ │ │ │ ├── copy-called.feature │ │ │ │ ├── copy.feature │ │ │ │ ├── csv.feature │ │ │ │ ├── data.json │ │ │ │ ├── dummy-ui-google.feature │ │ │ │ ├── dummy.feature │ │ │ │ ├── eval-and-set.feature │ │ │ │ ├── eval-assign.feature │ │ │ │ ├── exec.feature │ │ │ │ ├── extract.feature │ │ │ │ ├── extract.html │ │ │ │ ├── fail-api.feature │ │ │ │ ├── fail-js.feature │ │ │ │ ├── fail-tag-failure.feature │ │ │ │ ├── fail-tag.feature │ │ │ │ ├── fail1.feature │ │ │ │ ├── feature-result-called.feature │ │ │ │ ├── feature-result-cucumber.json │ │ │ │ ├── feature-result.feature │ │ │ │ ├── feature-result.json │ │ │ │ ├── features/ │ │ │ │ │ ├── RemainingFeaturesTest.java │ │ │ │ │ ├── feature1.feature │ │ │ │ │ ├── feature2.feature │ │ │ │ │ └── karate-config.js │ │ │ │ ├── fork-listener.feature │ │ │ │ ├── fork.feature │ │ │ │ ├── ignore-step-failure.feature │ │ │ │ ├── increment.js │ │ │ │ ├── js-arrays-products.json │ │ │ │ ├── js-arrays.feature │ │ │ │ ├── js-map-repeat.feature │ │ │ │ ├── jscall/ │ │ │ │ │ ├── JsCallTest.java │ │ │ │ │ ├── JsCallonceTest.java │ │ │ │ │ ├── dummy.feature │ │ │ │ │ ├── js-call.feature │ │ │ │ │ ├── js-called.feature │ │ │ │ │ ├── js-callonce.feature │ │ │ │ │ ├── karate-config.js │ │ │ │ │ └── utils.feature │ │ │ │ ├── jscall2/ │ │ │ │ │ ├── JsCall2Test.java │ │ │ │ │ ├── all.feature │ │ │ │ │ ├── call-once.feature │ │ │ │ │ ├── call-single.feature │ │ │ │ │ ├── call.feature │ │ │ │ │ ├── karate-config.js │ │ │ │ │ ├── local.feature │ │ │ │ │ └── mock.feature │ │ │ │ ├── jsread/ │ │ │ │ │ ├── js-read-2.feature │ │ │ │ │ ├── js-read-2.json │ │ │ │ │ ├── js-read-3.feature │ │ │ │ │ ├── js-read-3.json │ │ │ │ │ ├── js-read-4.feature │ │ │ │ │ ├── js-read-4.json │ │ │ │ │ ├── js-read-5.json │ │ │ │ │ ├── js-read-called-2.feature │ │ │ │ │ ├── js-read-called-3.feature │ │ │ │ │ ├── js-read-called.feature │ │ │ │ │ ├── js-read-reuse-2.feature │ │ │ │ │ ├── js-read-reuse-only-background.feature │ │ │ │ │ ├── js-read-reuse.feature │ │ │ │ │ ├── js-read.feature │ │ │ │ │ └── js-read.json │ │ │ │ ├── karate-config-callonce.js │ │ │ │ ├── karate-config-callsingle.js │ │ │ │ ├── karate-config-callsingletag.js │ │ │ │ ├── karate-config-csfail.js │ │ │ │ ├── karate-config-dev.js │ │ │ │ ├── karate-config-fn.feature │ │ │ │ ├── karate-config-frombase.feature │ │ │ │ ├── karate-config-frombase.js │ │ │ │ ├── karate-config-getscenario.feature │ │ │ │ ├── karate-config-getscenario.js │ │ │ │ ├── karate-config-utils.feature │ │ │ │ ├── karate-config.js │ │ │ │ ├── karate-get-called.feature │ │ │ │ ├── karate-get.feature │ │ │ │ ├── lower-case.feature │ │ │ │ ├── match-each-magic-variables.feature │ │ │ │ ├── match-step.feature │ │ │ │ ├── mock/ │ │ │ │ │ ├── MockRunner.java │ │ │ │ │ ├── MockSslTest.java │ │ │ │ │ ├── MockTest.java │ │ │ │ │ ├── SimpleMockRunner.java │ │ │ │ │ ├── _mock.feature │ │ │ │ │ ├── _simple.feature │ │ │ │ │ ├── binary.feature │ │ │ │ │ ├── brotli.feature │ │ │ │ │ ├── call.feature │ │ │ │ │ ├── called.feature │ │ │ │ │ ├── cookies.feature │ │ │ │ │ ├── delete.feature │ │ │ │ │ ├── download.feature │ │ │ │ │ ├── form.feature │ │ │ │ │ ├── german.feature │ │ │ │ │ ├── headers.feature │ │ │ │ │ ├── hello-data-driven.feature │ │ │ │ │ ├── hello-world.feature │ │ │ │ │ ├── html.feature │ │ │ │ │ ├── invalid-cookie.feature │ │ │ │ │ ├── json-bad.feature │ │ │ │ │ ├── json-order-mock-test.feature │ │ │ │ │ ├── json-order-mock.feature │ │ │ │ │ ├── karate-config.js │ │ │ │ │ ├── malformed.feature │ │ │ │ │ ├── malformed.txt │ │ │ │ │ ├── multi-params.feature │ │ │ │ │ ├── no-headers.feature │ │ │ │ │ ├── no-match.feature │ │ │ │ │ ├── param-commas.feature │ │ │ │ │ ├── patch.feature │ │ │ │ │ ├── test.xlsx │ │ │ │ │ ├── upload.feature │ │ │ │ │ ├── url-encoding.feature │ │ │ │ │ └── white-space.feature │ │ │ │ ├── not-equals.feature │ │ │ │ ├── ntlm-authentication.feature │ │ │ │ ├── outline-background.feature │ │ │ │ ├── outline-cat.json │ │ │ │ ├── outline-cats.json │ │ │ │ ├── outline-config-js.feature │ │ │ │ ├── outline-csv.csv │ │ │ │ ├── outline-csv.feature │ │ │ │ ├── outline-dynamic-fail.feature │ │ │ │ ├── outline-dynamic.feature │ │ │ │ ├── outline-generator.feature │ │ │ │ ├── outline-setup-once.feature │ │ │ │ ├── outline.feature │ │ │ │ ├── parajava/ │ │ │ │ │ ├── ParallelJavaTest.java │ │ │ │ │ ├── karate-config.js │ │ │ │ │ ├── parallel-java1.feature │ │ │ │ │ ├── parallel-java2.feature │ │ │ │ │ └── setup.feature │ │ │ │ ├── parallel/ │ │ │ │ │ ├── Hello.java │ │ │ │ │ ├── HelloTest.java │ │ │ │ │ ├── ParallelCsvTest.java │ │ │ │ │ ├── ParallelOutlineTest.java │ │ │ │ │ ├── ParallelTest.java │ │ │ │ │ ├── call-once-from-feature.feature │ │ │ │ │ ├── call-single-from-config.feature │ │ │ │ │ ├── call-single-from-config2.feature │ │ │ │ │ ├── call-single-from-config3.js │ │ │ │ │ ├── call-single-from-feature.feature │ │ │ │ │ ├── called.feature │ │ │ │ │ ├── cookies.js │ │ │ │ │ ├── data.csv │ │ │ │ │ ├── headers.js │ │ │ │ │ ├── hello.feature │ │ │ │ │ ├── karate-base.js │ │ │ │ │ ├── karate-config.js │ │ │ │ │ ├── mock.feature │ │ │ │ │ ├── parallel-csv.feature │ │ │ │ │ ├── parallel-outline-1.feature │ │ │ │ │ ├── parallel-outline-2.feature │ │ │ │ │ ├── parallel-outline-call-api.feature │ │ │ │ │ └── parallel.feature │ │ │ │ ├── parasimple/ │ │ │ │ │ ├── ParallelOutlineSimpleTest.java │ │ │ │ │ ├── headers.feature │ │ │ │ │ ├── headers.js │ │ │ │ │ ├── karate-config.js │ │ │ │ │ └── parallel-outline-simple.feature │ │ │ │ ├── parser/ │ │ │ │ │ ├── FeatureParserTest.java │ │ │ │ │ ├── test-comments.feature │ │ │ │ │ ├── test-def-docstring.feature │ │ │ │ │ ├── test-edge-cases.feature │ │ │ │ │ ├── test-empty-background.feature │ │ │ │ │ ├── test-empty-first-line1.feature │ │ │ │ │ ├── test-empty-first-line2.feature │ │ │ │ │ ├── test-empty-first-line3.feature │ │ │ │ │ ├── test-empty.feature.txt │ │ │ │ │ ├── test-error.feature │ │ │ │ │ ├── test-feature-header-only.feature │ │ │ │ │ ├── test-hide.feature │ │ │ │ │ ├── test-ignore-feature.feature │ │ │ │ │ ├── test-ignore-scenario.feature │ │ │ │ │ ├── test-outline-dynamic.feature │ │ │ │ │ ├── test-outline-examples-tags.feature │ │ │ │ │ ├── test-outline-name-js.feature │ │ │ │ │ ├── test-outline-name.feature │ │ │ │ │ ├── test-scenario-description.feature │ │ │ │ │ ├── test-set-table.feature │ │ │ │ │ ├── test-simple-background.feature │ │ │ │ │ ├── test-simple.feature │ │ │ │ │ ├── test-table-pipe.feature │ │ │ │ │ └── test-tags-multiline.feature │ │ │ │ ├── payments-mock.feature │ │ │ │ ├── payments.feature │ │ │ │ ├── perf-mock.feature │ │ │ │ ├── perf.feature │ │ │ │ ├── print.feature │ │ │ │ ├── read-expressions.json │ │ │ │ ├── read-expressions.yml │ │ │ │ ├── read-properties.feature │ │ │ │ ├── read-properties.properties │ │ │ │ ├── replace.feature │ │ │ │ ├── retry/ │ │ │ │ │ ├── RetryTest.java │ │ │ │ │ ├── retry-with-setup.feature │ │ │ │ │ └── test.feature │ │ │ │ ├── runner/ │ │ │ │ │ ├── FeatureResultTest.java │ │ │ │ │ ├── FeatureReuseTest.java │ │ │ │ │ ├── NoopDriver.java │ │ │ │ │ ├── RunnerTest.java │ │ │ │ │ ├── TagTest.java │ │ │ │ │ ├── aborted.feature │ │ │ │ │ ├── called-arg-loop.feature │ │ │ │ │ ├── called-arg-null.feature │ │ │ │ │ ├── called-arg-single.feature │ │ │ │ │ ├── called-shared.feature │ │ │ │ │ ├── called-shared2.feature │ │ │ │ │ ├── called.feature │ │ │ │ │ ├── called_2.feature │ │ │ │ │ ├── caller-arg.feature │ │ │ │ │ ├── caller-shared.feature │ │ │ │ │ ├── caller-with-lambda-arg.feature │ │ │ │ │ ├── caller.feature │ │ │ │ │ ├── caller_2.feature │ │ │ │ │ ├── failed.feature │ │ │ │ │ ├── hooks/ │ │ │ │ │ │ ├── HooksTest.java │ │ │ │ │ │ ├── MandatoryTagHook.java │ │ │ │ │ │ ├── NoFeatureTestRuntimeHook.java │ │ │ │ │ │ ├── NoScenarioTestRuntimeHook.java │ │ │ │ │ │ ├── NoStepTestRuntimeHook.java │ │ │ │ │ │ ├── ScenarioHookSkipTest.java │ │ │ │ │ │ ├── ScenarioHookTest.java │ │ │ │ │ │ ├── SkipHook.java │ │ │ │ │ │ ├── TestRuntimeHook.java │ │ │ │ │ │ ├── hook-dynamic-outline.feature │ │ │ │ │ │ ├── hook-multiple-dynamic-outline.feature │ │ │ │ │ │ ├── hook-multiple-outlines.feature │ │ │ │ │ │ ├── hook-outline.feature │ │ │ │ │ │ ├── hook-scenario.feature │ │ │ │ │ │ ├── karate-base.js │ │ │ │ │ │ ├── karate-config.js │ │ │ │ │ │ ├── test-hook-multiexample.feature │ │ │ │ │ │ ├── test-hook-notags.feature │ │ │ │ │ │ └── test-hook-skip.feature │ │ │ │ │ ├── multi-scenario-fail.feature │ │ │ │ │ ├── multi-scenario.feature │ │ │ │ │ ├── no-scenario-name.feature │ │ │ │ │ ├── notEqualMatch.feature │ │ │ │ │ ├── outline.feature │ │ │ │ │ ├── run-arg.feature │ │ │ │ │ ├── run-ignore.feature │ │ │ │ │ ├── scenario.feature │ │ │ │ │ ├── signin.feature │ │ │ │ │ ├── stackoverflow-error.feature │ │ │ │ │ ├── table.feature │ │ │ │ │ ├── test-called-embedded-file.feature │ │ │ │ │ ├── test-called-embedded.feature │ │ │ │ │ ├── test-called.feature │ │ │ │ │ ├── test.json │ │ │ │ │ └── test.xml │ │ │ │ ├── scenario-outline-result.feature │ │ │ │ ├── scenario-variable-scope.feature │ │ │ │ ├── schema-like-odds.json │ │ │ │ ├── schema-like-time-validator.js │ │ │ │ ├── schema-like.feature │ │ │ │ ├── schema-read-inner.json │ │ │ │ ├── schema-read.feature │ │ │ │ ├── schema-read.json │ │ │ │ ├── set-xml.feature │ │ │ │ ├── set.feature │ │ │ │ ├── single-scenario.feature │ │ │ │ ├── sort-array.feature │ │ │ │ ├── sort-array.js │ │ │ │ ├── table.feature │ │ │ │ ├── tags/ │ │ │ │ │ ├── TagsTest.java │ │ │ │ │ ├── env-tags.feature │ │ │ │ │ └── outline-tags.feature │ │ │ │ ├── tags.feature │ │ │ │ ├── to-bean-called.feature │ │ │ │ ├── to-bean.feature │ │ │ │ ├── type-conv-query.txt │ │ │ │ ├── type-conv-query2.txt │ │ │ │ ├── type-conv.feature │ │ │ │ ├── type-conversion.feature │ │ │ │ ├── ui-google.feature │ │ │ │ ├── users-doc.feature │ │ │ │ ├── users-single.html │ │ │ │ ├── users.html │ │ │ │ ├── utils-reuse-common.feature │ │ │ │ ├── utils-reuse.feature │ │ │ │ ├── uuid.js │ │ │ │ ├── websocket.feature │ │ │ │ ├── xml/ │ │ │ │ │ ├── XmlTest.java │ │ │ │ │ ├── envelope1.xml │ │ │ │ │ ├── envelope2.xml │ │ │ │ │ ├── soap1.xml │ │ │ │ │ ├── soap2.xml │ │ │ │ │ ├── xml-and-xpath.feature │ │ │ │ │ ├── xml-call.feature │ │ │ │ │ ├── xml-called.feature │ │ │ │ │ └── xml.feature │ │ │ │ └── xml-pretty.feature │ │ │ ├── driver/ │ │ │ │ ├── DriverElementTest.java │ │ │ │ ├── DriverOptionsTest.java │ │ │ │ ├── ElementFinderTest.java │ │ │ │ ├── KeyTest.java │ │ │ │ ├── appium/ │ │ │ │ │ └── MobileDriverOptionsTest.java │ │ │ │ └── playwright/ │ │ │ │ └── PlaywrightDriverRunner.java │ │ │ ├── fatjar/ │ │ │ │ ├── ClientRunner.java │ │ │ │ ├── FeatureProxyRunner.java │ │ │ │ ├── FeatureServerRunner.java │ │ │ │ ├── FeatureServerTest.java │ │ │ │ ├── MainRunner.java │ │ │ │ ├── MainSslRunner.java │ │ │ │ ├── ProxyServerRunner.java │ │ │ │ ├── ProxyServerSslMain.java │ │ │ │ ├── ProxyServerSslTest.java │ │ │ │ ├── ProxyServerTest.java │ │ │ │ ├── client.feature │ │ │ │ ├── karate-config.js │ │ │ │ ├── proxy.feature │ │ │ │ ├── server.feature │ │ │ │ └── temp.html │ │ │ ├── graal/ │ │ │ │ ├── JsEngineTest.java │ │ │ │ ├── JsValueTest.java │ │ │ │ ├── SimplePojo.java │ │ │ │ └── StaticPojo.java │ │ │ ├── http/ │ │ │ │ ├── AwsLambdaHandlerTest.java │ │ │ │ ├── GenericHttpHeaderTrackingTest.java │ │ │ │ ├── HttpClientTester.java │ │ │ │ ├── HttpHookTest.java │ │ │ │ ├── HttpLoggerTest.java │ │ │ │ ├── HttpRequestBuilderTest.java │ │ │ │ ├── HttpUtilsTest.java │ │ │ │ ├── MultiPartBuilderTest.java │ │ │ │ ├── ProxyContextTest.java │ │ │ │ ├── RequestHandlerTest.java │ │ │ │ ├── WebSocketClientRunner.java │ │ │ │ ├── WebSocketProxyRunner.java │ │ │ │ ├── WebSocketTempRunner.java │ │ │ │ ├── api.json │ │ │ │ ├── cats.json │ │ │ │ ├── form.json │ │ │ │ ├── index.json │ │ │ │ ├── mock.feature │ │ │ │ └── root.json │ │ │ ├── malformed.txt │ │ │ ├── report/ │ │ │ │ ├── ReportUtilsTest.java │ │ │ │ ├── called-loop.feature │ │ │ │ ├── called.feature │ │ │ │ ├── called2.feature │ │ │ │ ├── called3.feature │ │ │ │ ├── customTags.feature │ │ │ │ ├── data.csv │ │ │ │ └── test.feature │ │ │ ├── resource/ │ │ │ │ ├── ResourceUtilsTest.java │ │ │ │ ├── dir1/ │ │ │ │ │ └── dir1.log │ │ │ │ ├── dir2/ │ │ │ │ │ └── dir2.log │ │ │ │ ├── test.feature │ │ │ │ ├── test1.txt │ │ │ │ └── test2.log │ │ │ ├── shell/ │ │ │ │ ├── CommandTest.java │ │ │ │ └── CommandTester.java │ │ │ ├── template/ │ │ │ │ ├── TemplateTest.java │ │ │ │ ├── attr.html │ │ │ │ ├── called1.html │ │ │ │ ├── global.js │ │ │ │ ├── ka-set.html │ │ │ │ ├── local.js │ │ │ │ ├── main.html │ │ │ │ ├── nocache.html │ │ │ │ ├── temp.html │ │ │ │ ├── temp.js │ │ │ │ ├── with-called.html │ │ │ │ └── with.html │ │ │ └── test/ │ │ │ └── file-utils-test.feature │ │ ├── demo/ │ │ │ ├── ServerRunner.java │ │ │ ├── api/ │ │ │ │ ├── cats.js │ │ │ │ ├── demo.js │ │ │ │ ├── payments.js │ │ │ │ ├── render.js │ │ │ │ └── test.html │ │ │ ├── apidocs.html │ │ │ ├── app.css │ │ │ ├── cats.html │ │ │ ├── details.html │ │ │ ├── index.html │ │ │ ├── person.html │ │ │ ├── user.html │ │ │ └── users.html │ │ ├── karate-base.js │ │ ├── karate-config.js │ │ ├── logback-test.xml │ │ └── payments.jmx │ └── resources/ │ ├── analytics.md │ └── readme.txt ├── karate-demo/ │ ├── README.md │ ├── build.gradle │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── intuit/ │ │ │ └── karate/ │ │ │ └── demo/ │ │ │ ├── Application.java │ │ │ ├── config/ │ │ │ │ ├── ServerStartedInitializingBean.java │ │ │ │ ├── TomcatConfig.java │ │ │ │ ├── WebSecurityConfig.java │ │ │ │ └── WebSocketConfig.java │ │ │ ├── controller/ │ │ │ │ ├── CatsController.java │ │ │ │ ├── DogsController.java │ │ │ │ ├── EchoController.java │ │ │ │ ├── EncodingController.java │ │ │ │ ├── GraphqlController.java │ │ │ │ ├── GreetingController.java │ │ │ │ ├── HeadersController.java │ │ │ │ ├── RedirectController.java │ │ │ │ ├── SearchController.java │ │ │ │ ├── SignInController.java │ │ │ │ ├── SoapController.java │ │ │ │ ├── UploadController.java │ │ │ │ ├── WebSocketController.java │ │ │ │ └── WebSocketHandler.java │ │ │ ├── domain/ │ │ │ │ ├── Binary.java │ │ │ │ ├── Cat.java │ │ │ │ ├── Dog.java │ │ │ │ ├── FileInfo.java │ │ │ │ ├── Greeting.java │ │ │ │ ├── Message.java │ │ │ │ └── SignIn.java │ │ │ ├── exception/ │ │ │ │ ├── ErrorResponse.java │ │ │ │ └── GlobalExceptionHandler.java │ │ │ └── util/ │ │ │ ├── DbUtils.java │ │ │ ├── FileChecker.java │ │ │ ├── FizzBuzz.java │ │ │ ├── JavaDemo.java │ │ │ └── SchemaUtils.java │ │ └── resources/ │ │ ├── application.properties │ │ ├── graphql-1.json │ │ ├── graphql-2.json │ │ ├── schema.sql │ │ ├── soap-1.xml │ │ └── soap-2.xml │ └── test/ │ └── java/ │ ├── demo/ │ │ ├── DemoRunner.java │ │ ├── DemoTestParallel.java │ │ ├── DemoTestSelected.java │ │ ├── TestBase.java │ │ ├── abort/ │ │ │ └── abort.feature │ │ ├── binary/ │ │ │ └── binary.feature │ │ ├── callarray/ │ │ │ ├── call-json-array.feature │ │ │ ├── kitten-create.feature │ │ │ └── kittens.json │ │ ├── calldynamic/ │ │ │ ├── call-dynamic-json.feature │ │ │ └── get-cat.feature │ │ ├── callfeature/ │ │ │ ├── call-feature.feature │ │ │ ├── called-arg.feature │ │ │ ├── called-normal.feature │ │ │ ├── create-cats-outline.feature │ │ │ └── create-two-cats.feature │ │ ├── callnested/ │ │ │ ├── call-nested.feature │ │ │ ├── called1.feature │ │ │ ├── called2.feature │ │ │ └── called3.feature │ │ ├── callonce/ │ │ │ └── call-once.feature │ │ ├── calltable/ │ │ │ ├── call-table.feature │ │ │ └── kitten-create.feature │ │ ├── cats/ │ │ │ ├── CatsJava.java │ │ │ ├── billie-expected.json │ │ │ ├── cats-put.feature │ │ │ ├── cats.feature │ │ │ ├── kittens.feature │ │ │ └── syntax-demo.feature │ │ ├── cookies/ │ │ │ └── cookies.feature │ │ ├── delete/ │ │ │ └── delete.feature │ │ ├── dogs/ │ │ │ └── dogs.feature │ │ ├── dsl/ │ │ │ ├── common.feature │ │ │ ├── dsl.feature │ │ │ ├── greet.js │ │ │ └── login.feature │ │ ├── embed/ │ │ │ ├── embed-pdf.js │ │ │ └── embed.feature │ │ ├── encoding/ │ │ │ └── encoding.feature │ │ ├── error/ │ │ │ ├── error.feature │ │ │ └── no-url.feature │ │ ├── form/ │ │ │ └── form.feature │ │ ├── graphql/ │ │ │ ├── graphql.feature │ │ │ └── user-by-id.graphql │ │ ├── greeting/ │ │ │ └── greeting.feature │ │ ├── headers/ │ │ │ ├── DemoLogModifier.java │ │ │ ├── call-isolated-config.feature │ │ │ ├── call-isolated-headers.feature │ │ │ ├── call-updates-config.feature │ │ │ ├── callonce-background-multiscenario.feature │ │ │ ├── common-multiple.feature │ │ │ ├── common-noheaders.feature │ │ │ ├── common.feature │ │ │ ├── content-type.feature │ │ │ ├── headers-alt.js │ │ │ ├── headers-background-configure.feature │ │ │ ├── headers-background.feature │ │ │ ├── headers-form-get.feature │ │ │ ├── headers-masking.feature │ │ │ ├── headers-override.feature │ │ │ ├── headers-single.feature │ │ │ ├── headers-uuid.feature │ │ │ ├── headers.feature │ │ │ └── null-header.feature │ │ ├── hooks/ │ │ │ ├── after-feature.feature │ │ │ ├── after-scenario.feature │ │ │ ├── called.feature │ │ │ └── hooks.feature │ │ ├── info/ │ │ │ └── info.feature │ │ ├── java/ │ │ │ ├── JavaApiTest.java │ │ │ ├── cats-java.feature │ │ │ └── from-java.feature │ │ ├── jwt/ │ │ │ └── jwt.feature │ │ ├── oauth/ │ │ │ ├── Signer.java │ │ │ ├── oauth1.feature │ │ │ └── oauth2.feature │ │ ├── outline/ │ │ │ ├── dynamic-csv.feature │ │ │ ├── dynamic-generator.feature │ │ │ ├── dynamic.feature │ │ │ ├── examples.feature │ │ │ ├── kittens.csv │ │ │ └── setup-outline.feature │ │ ├── params/ │ │ │ └── params.feature │ │ ├── polling/ │ │ │ ├── get.feature │ │ │ └── polling.feature │ │ ├── read/ │ │ │ ├── read-files.feature │ │ │ ├── sample.json │ │ │ └── sample.xml │ │ ├── redirect/ │ │ │ └── redirect.feature │ │ ├── request/ │ │ │ └── request.feature │ │ ├── schema/ │ │ │ ├── products-schema.json │ │ │ ├── products.json │ │ │ └── schema.feature │ │ ├── search/ │ │ │ ├── dynamic-params.feature │ │ │ ├── get-response-param.js │ │ │ ├── search-complex.feature │ │ │ └── search-simple.feature │ │ ├── signin/ │ │ │ └── sign-in.feature │ │ ├── soap/ │ │ │ ├── expected.xml │ │ │ ├── request.xml │ │ │ └── soap.feature │ │ ├── tags/ │ │ │ ├── TagsRunner.java │ │ │ ├── first.feature │ │ │ └── second.feature │ │ ├── unit/ │ │ │ ├── cat.feature │ │ │ ├── common.feature │ │ │ └── fizz-buzz.feature │ │ ├── upload/ │ │ │ ├── upload-image.feature │ │ │ ├── upload-multiple-fields.feature │ │ │ ├── upload-multiple-files.feature │ │ │ ├── upload-retry.feature │ │ │ └── upload.feature │ │ ├── websocket/ │ │ │ ├── WebSocketClientRunner.java │ │ │ ├── echo.feature │ │ │ └── websocket.feature │ │ └── xml/ │ │ └── preserve-whitespace.feature │ ├── driver/ │ │ ├── demo/ │ │ │ ├── Demo01JavaRunner.java │ │ │ ├── demo-01.feature │ │ │ ├── demo-02.feature │ │ │ ├── demo-03.feature │ │ │ ├── demo-04.feature │ │ │ ├── demo-05.feature │ │ │ └── demo-06.feature │ │ ├── mock/ │ │ │ ├── demo-01.feature │ │ │ ├── demo-02.feature │ │ │ ├── mock-01.feature │ │ │ ├── mock-02.feature │ │ │ └── response.json │ │ ├── screenshot/ │ │ │ ├── ChromeFullPageRunner.java │ │ │ ├── ChromePdfRunner.java │ │ │ ├── EdgeChromiumFullPageRunner.java │ │ │ └── EdgeChromiumPdfRunner.java │ │ └── windows/ │ │ └── calc.feature │ ├── headers.js │ ├── karate-config.js │ ├── log4j2.properties │ ├── logback-test.xml │ ├── mock/ │ │ ├── async/ │ │ │ ├── AsyncTest.java │ │ │ ├── QueueConsumer.java │ │ │ ├── QueueUtils.java │ │ │ ├── karate-config.js │ │ │ ├── main.feature │ │ │ └── mock.feature │ │ ├── contract/ │ │ │ ├── Consumer.java │ │ │ ├── ConsumerIntegrationTest.java │ │ │ ├── ConsumerUsingMockTest.java │ │ │ ├── ConsumerUsingProxyHttpTest.java │ │ │ ├── ConsumerUsingProxyRewriteSslTest.java │ │ │ ├── ConsumerUsingProxyRewriteTest.java │ │ │ ├── Payment.java │ │ │ ├── PaymentService.java │ │ │ ├── PaymentServiceContractSslTest.java │ │ │ ├── PaymentServiceContractTest.java │ │ │ ├── PaymentServiceContractUsingMockSslTest.java │ │ │ ├── PaymentServiceContractUsingMockTest.java │ │ │ ├── PaymentServiceMockMain.java │ │ │ ├── PaymentServiceMockSslMain.java │ │ │ ├── QueueConsumer.java │ │ │ ├── QueueUtils.java │ │ │ ├── QueueUtilsTest.java │ │ │ ├── Shipment.java │ │ │ ├── increment.js │ │ │ ├── karate-config.js │ │ │ ├── payment-service-mock.feature │ │ │ ├── payment-service-proxy.feature │ │ │ ├── payment-service.feature │ │ │ └── payments.html │ │ ├── micro/ │ │ │ ├── CatsMockRunner.java │ │ │ ├── cats-mock.feature │ │ │ └── cats.feature │ │ ├── proxy/ │ │ │ ├── DemoMockProceedRunner.java │ │ │ ├── DemoMockProxyRunner.java │ │ │ ├── DemoMockProxySslRunner.java │ │ │ ├── DemoMockRunner.java │ │ │ ├── DemoMockSslRunner.java │ │ │ ├── demo-mock-proceed.feature │ │ │ ├── demo-mock.feature │ │ │ └── karate-config.js │ │ └── web/ │ │ ├── CatsMockRunner.java │ │ ├── CatsMockStarter.java │ │ ├── CatsTestRunner.java │ │ ├── cats-mock.feature │ │ ├── cats-test.feature │ │ └── cats.html │ ├── mock-cert.pem │ ├── mock-contract.jmx │ ├── mock-key.pem │ ├── server-keystore-cert.pem │ ├── server-keystore-key.pem │ ├── server-keystore.p12 │ ├── server-keystore.pem │ ├── ssl/ │ │ ├── SslTest.java │ │ ├── TestService.java │ │ ├── ssl-keystore.feature │ │ └── ssl-truststore.feature │ └── test/ │ ├── MonitorThread.java │ ├── ServerStart.java │ ├── ServerStop.java │ └── Stoppable.java ├── karate-docker/ │ ├── karate-chrome/ │ │ ├── Dockerfile │ │ ├── entrypoint.sh │ │ └── supervisord.conf │ ├── karate-chromium/ │ │ ├── Dockerfile │ │ ├── entrypoint.sh │ │ └── supervisord.conf │ └── karate-firefox/ │ ├── Dockerfile │ ├── build.sh │ ├── entrypoint.sh │ ├── install.sh │ └── supervisord.conf ├── karate-e2e-tests/ │ ├── README.md │ ├── pom.xml │ └── src/ │ └── test/ │ └── java/ │ ├── axe/ │ │ ├── AxeRunner.java │ │ ├── axe-report.html │ │ └── axe.feature │ ├── driver/ │ │ ├── 00.feature │ │ ├── 00_outline.feature │ │ ├── 01.feature │ │ ├── 02.feature │ │ ├── 03.feature │ │ ├── 04.feature │ │ ├── 05.feature │ │ ├── 05_mock.feature │ │ ├── 06.feature │ │ ├── 07.feature │ │ ├── 08.feature │ │ ├── 09.feature │ │ ├── 10.feature │ │ ├── 11.feature │ │ ├── 12.feature │ │ ├── 13.feature │ │ ├── 14.feature │ │ ├── 15.feature │ │ ├── 16.feature │ │ ├── 17.feature │ │ ├── 18.feature │ │ ├── 19.feature │ │ ├── 99_bootstrap.feature │ │ ├── DockerRunner.java │ │ ├── JavaApiPlaywrightRunner.java │ │ ├── JavaApiRunner.java │ │ ├── LocalParallelRunner.java │ │ ├── LocalPlaywrightRunner.java │ │ ├── LocalSingleRunner.java │ │ ├── ServerStarter.java │ │ ├── html/ │ │ │ ├── 00.css │ │ │ ├── 00.html │ │ │ ├── 00.js │ │ │ ├── 01.html │ │ │ ├── 02.html │ │ │ ├── 03.html │ │ │ ├── 04.html │ │ │ ├── 05.html │ │ │ ├── 06.html │ │ │ ├── 07.html │ │ │ ├── 08.html │ │ │ ├── 08_upload.html │ │ │ ├── 09.html │ │ │ ├── 10.html │ │ │ ├── 11.html │ │ │ ├── 11_tab.html │ │ │ ├── 13.html │ │ │ ├── 14.html │ │ │ ├── 14_embedded.html │ │ │ ├── 14_processing.html │ │ │ ├── 15.html │ │ │ ├── 16.html │ │ │ ├── 17_a.html │ │ │ ├── 17_b.html │ │ │ ├── 18.html │ │ │ ├── 19.html │ │ │ ├── 99_bootstrap.html │ │ │ ├── api/ │ │ │ │ └── 05.js │ │ │ └── scratch.html │ │ ├── karate-config-docker.js │ │ ├── karate-config-playwright.js │ │ ├── karate-config-single.js │ │ ├── karate-config-xbrowser.js │ │ ├── karate-config.js │ │ └── scratch.feature │ ├── logback-test.xml │ └── regex/ │ ├── RegexRunner.java │ └── regex.feature ├── karate-gatling/ │ ├── README.md │ ├── dummy.txt │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── intuit/ │ │ │ └── karate/ │ │ │ └── gatling/ │ │ │ └── javaapi/ │ │ │ ├── KarateDsl.java │ │ │ ├── KarateFeatureBuilder.java │ │ │ ├── KarateProtocolBuilder.java │ │ │ └── KarateUriPattern.java │ │ └── scala/ │ │ └── com/ │ │ └── intuit/ │ │ └── karate/ │ │ └── gatling/ │ │ ├── KarateActions.scala │ │ ├── KarateProtocol.scala │ │ └── PreDef.scala │ └── test/ │ ├── java/ │ │ ├── com/ │ │ │ └── intuit/ │ │ │ └── karate/ │ │ │ └── gatling/ │ │ │ └── javaapi/ │ │ │ └── KarateProtocolBuilderTest.java │ │ └── mock/ │ │ ├── CatsChainedSimulation.java │ │ ├── CatsSimulation.java │ │ ├── MockUtils.java │ │ ├── cats-chained.feature │ │ ├── cats-create.feature │ │ ├── cats-delete-one.feature │ │ ├── cats-delete.feature │ │ ├── custom-rpc.feature │ │ └── mock.feature │ ├── resources/ │ │ ├── gatling-akka.conf │ │ ├── karate-config-perf.js │ │ ├── karate-config.js │ │ ├── logback-test.xml │ │ └── test.feature │ └── scala/ │ └── mock/ │ ├── CatsChainedScalaSimulation.scala │ ├── CatsScalaSimulation.scala │ └── CatsSimulationWithSilentWarmUp.scala ├── karate-junit5/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── com/ │ │ └── intuit/ │ │ └── karate/ │ │ └── junit5/ │ │ ├── FeatureNode.java │ │ └── Karate.java │ └── test/ │ └── java/ │ ├── karate/ │ │ ├── NoFeatureNoScenarioTest.java │ │ ├── SampleCustomTagsTest.java │ │ ├── SampleTest.java │ │ ├── SetupDryRunTest.java │ │ ├── customTags.feature │ │ ├── embed.feature │ │ ├── noFeatureNoScenario.feature │ │ ├── sample.feature │ │ ├── setup-with-dryrun.feature │ │ └── tags.feature │ ├── karate-config.js │ └── logback-test.xml ├── karate-netty/ │ └── README.md ├── karate-playwright/ │ ├── .gitignore │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── com/ │ │ └── intuit/ │ │ └── karate/ │ │ ├── driver/ │ │ │ └── playwright/ │ │ │ └── PlaywrightDriver.java │ │ └── playwright/ │ │ └── driver/ │ │ ├── InvocationHandlers.java │ │ ├── PlaywrightDriver.java │ │ ├── PlaywrightDriverOptions.java │ │ ├── PlaywrightElement.java │ │ ├── PlaywrightFinder.java │ │ ├── PlaywrightMouse.java │ │ ├── PlaywrightToken.java │ │ └── util/ │ │ └── KarateTokenParser.java │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── intuit/ │ │ └── karate/ │ │ └── playwright/ │ │ └── driver/ │ │ ├── PlaywrightDriverTest.java │ │ └── PlaywrightElementTest.java │ └── resources/ │ └── html/ │ └── 02.html ├── karate-robot/ │ ├── README.md │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── com/ │ │ └── intuit/ │ │ └── karate/ │ │ └── robot/ │ │ ├── Element.java │ │ ├── ImageElement.java │ │ ├── Location.java │ │ ├── MissingElement.java │ │ ├── OpenCvUtils.java │ │ ├── Region.java │ │ ├── Robot.java │ │ ├── RobotBase.java │ │ ├── RobotFactory.java │ │ ├── RobotUtils.java │ │ ├── StringMatcher.java │ │ ├── Tesseract.java │ │ ├── Window.java │ │ ├── linux/ │ │ │ └── LinuxRobot.java │ │ ├── mac/ │ │ │ └── MacRobot.java │ │ └── win/ │ │ ├── ComAllocated.java │ │ ├── ComAllocatedStr.java │ │ ├── ComAllocatedVarInt.java │ │ ├── ComAllocatedVarStr.java │ │ ├── ComFunction.java │ │ ├── ComInterface.java │ │ ├── ComLibrary.java │ │ ├── ComRef.java │ │ ├── ComUtils.java │ │ ├── ControlType.java │ │ ├── IUIAutomation.java │ │ ├── IUIAutomationBase.java │ │ ├── IUIAutomationCondition.java │ │ ├── IUIAutomationElement.java │ │ ├── IUIAutomationElementArray.java │ │ ├── IUIAutomationInvokePattern.java │ │ ├── IUIAutomationScrollPattern.java │ │ ├── IUIAutomationSelectionItemPattern.java │ │ ├── IUIAutomationTreeWalker.java │ │ ├── IUIAutomationValuePattern.java │ │ ├── IUIAutomationWindowPattern.java │ │ ├── PathSearch.java │ │ ├── Pattern.java │ │ ├── Property.java │ │ ├── ScrollAmount.java │ │ ├── TreeScope.java │ │ ├── WinElement.java │ │ ├── WinRobot.java │ │ └── WinWindow.java │ └── test/ │ └── java/ │ ├── com/ │ │ └── intuit/ │ │ └── karate/ │ │ └── robot/ │ │ ├── OpenCvUtilsTest.java │ │ ├── TesseractRunner.java │ │ └── win/ │ │ ├── IUIAutomationRunner.java │ │ ├── PathSearchTest.java │ │ └── WinRobotRunner.java │ ├── logback-test.xml │ └── robot/ │ └── core/ │ ├── ChromeJavaRunner.java │ ├── CoreRunner.java │ ├── calc.feature │ ├── called.feature │ ├── caller.feature │ ├── chrome.feature │ ├── dummy.feature │ ├── iphone.feature │ ├── upload.feature │ └── wordpad.feature └── pom.xml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [karatelabs.io](https://www.karatelabs.io/karate-labs-contact). All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at https://www.contributor-covenant.org/version/2/1/code_of_conduct.html - v2.1 ================================================ FILE: .github/CONTRIBUTING.md ================================================ # Contribution Guidelines First of all, thank you for your interest in contributing to this project ! * Before submitting a [Pull Request](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests) (PR), please make sure that you have had a discussion with the project-leads * If a [relevant issue](https://github.com/karatelabs/karate/issues) already exists, have a discussion within that issue (by commenting) - and make sure that the project-leads are okay with your approach * If no relevant issue exists, please [open a new issue](https://github.com/karatelabs/karate/issues) to start a discussion * Please proceed with a PR only *after* the project admins or owners are okay with your approach. We don't want you to spend time and effort working on something - only to find out later that it was not aligned with how the project developers were thinking about it ! * You can refer to the [Developer Guide](https://github.com/karatelabs/karate/wiki/Developer-Guide) for information on how to build and test the project on your local / developer machine * **IMPORTANT**: Submit your PR(s) against the [`develop`](https://github.com/karatelabs/karate/tree/develop) branch of this repository If you are interested in project road-map items that you can potentially contribute to, please refer to the [Project Board](https://github.com/karatelabs/karate/projects/3). ================================================ FILE: .github/FUNDING.yml ================================================ github: [karatelabs] ================================================ FILE: .github/ISSUE_TEMPLATE.md ================================================ IMPORTANT: If you have a general question please use Stack Overflow instead where Karate has a dedicated "tag": https://stackoverflow.com/questions/tagged/karate If you are sure you have found a bug, please make sure you follow the instructions here: https://github.com/karatelabs/karate/wiki/How-to-Submit-an-Issue ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ ### Description Thanks for contributing this Pull Request. Make sure that you submit this Pull Request against the `develop` branch of this repository, add a brief description, and tag the relevant issue(s) and PR(s) below. - Relevant Issues : (compulsory) - Relevant PRs : (optional) - Type of change : - [ ] New feature - [ ] Bug fix for existing feature - [ ] Code quality improvement - [ ] Addition or Improvement of tests - [ ] Addition or Improvement of documentation ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: # Dependencies listed in .github/workflows/*.yml - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" ================================================ FILE: .github/workflows/codeql.yml ================================================ # For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # name: "CodeQL" on: push: branches: [ "develop", "master" ] pull_request: # The branches below must be a subset of the branches above branches: [ "develop" ] # schedule: # - cron: '31 14 * * 4' jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: [ 'java' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] # Use only 'java' to analyze code written in Java, Kotlin or both # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support steps: - name: Checkout repository uses: actions/checkout@v6 - name: set up jdk 17 uses: actions/setup-java@v5 with: distribution: adopt java-version: 17 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # queries: security-extended,security-and-quality # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun # If the Autobuild fails above, remove it and uncomment the following three lines. # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. # - run: | # echo "Run, Build Application using script" # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 with: category: "/language:${{matrix.language}}" ================================================ FILE: .github/workflows/delete-workflow-runs.yml ================================================ name: delete-workflow-runs on: workflow_dispatch: inputs: days: description: 'no. of days' required: true default: 30 minimum_runs: description: 'minimum runs to keep (per workflow)' required: true default: 6 # push: # branches: [ develop ] jobs: del_runs: runs-on: ubuntu-latest steps: - name: Delete workflow runs uses: Mattraks/delete-workflow-runs@v2 with: token: ${{ github.token }} repository: ${{ github.repository }} retain_days: ${{ github.event.inputs.days }} # retain_days: 30 keep_minimum_runs: ${{ github.event.inputs.minimum_runs }} # keep_minimum_runs: 6 ================================================ FILE: .github/workflows/jdk-compat.yml ================================================ name: jdk-compat on: push: branches: [ develop ] jobs: build: timeout-minutes: 10 runs-on: ubuntu-latest steps: - name: git checkout uses: actions/checkout@v6 - name: get latest jdk ga uses: oracle-actions/setup-java@v1 with: website: jdk.java.net release: 25 - name: cache maven packages uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} restore-keys: ${{ runner.os }}-maven- - name: build with maven run: mvn -B clean install -P pre-release -Djavacpp.platform=linux-x86_64 ================================================ FILE: .github/workflows/maven-build.yml ================================================ name: maven-build on: push: branches: [ master, develop ] pull_request: branches: [ master, develop ] jobs: build: timeout-minutes: 20 runs-on: ubuntu-latest steps: - name: git checkout uses: actions/checkout@v6 - name: set up jdk 17 uses: actions/setup-java@v5 with: distribution: adopt java-version: 17 - name: cache maven packages uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} restore-keys: ${{ runner.os }}-maven- - name: build with maven run: mvn -B clean install -P pre-release -Djavacpp.platform=linux-x86_64 - name: build and test with docker run: ./build-docker.sh - name: upload workspace if build fails # if: ${{ failure() }} if: ${{ false }} uses: actions/upload-artifact@v5 with: name: build-results path: . retention-days: 5 ================================================ FILE: .github/workflows/maven-release.yml ================================================ name: maven-release on: workflow_dispatch: inputs: version: description: maven and docker release version required: true default: 'X.X.X.RCX' docker: description: push to docker required: true type: choice options: - 'enabled' - 'disabled' maven: description: push to maven central required: true type: choice options: - 'enabled' - 'disabled' jobs: build: timeout-minutes: 20 runs-on: ubuntu-latest steps: - name: git checkout uses: actions/checkout@v6 - name: set up jdk 17 uses: actions/setup-java@v5 with: distribution: adopt java-version: 17 server-id: central server-username: CENTRAL_TOKEN_USERNAME server-password: CENTRAL_TOKEN_PASSWORD gpg-private-key: ${{ secrets.OSSRH_GPG_SECRET_KEY }} gpg-passphrase: MAVEN_GPG_PASSPHRASE - name: set maven version run: | mvn versions:set versions:commit -B -ntp -DnewVersion=${{ github.event.inputs.version }} - name: docker build if: ( github.event.inputs.docker == 'enabled' ) run: | mvn clean install -B -ntp -DskipTests -P pre-release ./build-docker.sh - name: docker login if: ( github.event.inputs.docker == 'enabled' ) uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: docker push if: ( github.event.inputs.docker == 'enabled' ) run: | docker tag karate-chrome karatelabs/karate-chrome:${{ github.event.inputs.version }} docker tag karate-chrome karatelabs/karate-chrome:latest docker push karatelabs/karate-chrome:${{ github.event.inputs.version }} docker push karatelabs/karate-chrome:latest - name: maven deploy to central if: ( github.event.inputs.maven == 'enabled' ) env: CENTRAL_TOKEN_USERNAME: ${{ secrets.OSSRH_TOKEN_USER }} CENTRAL_TOKEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }} MAVEN_GPG_PASSPHRASE: ${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }} run: | mvn -B -ntp deploy -DskipTests -P pre-release,release -pl "!karate-archetype,!karate-demo,!karate-e2e-tests" - name: maven build binaries run: | mvn package -DskipTests -P fatjar -f karate-core/pom.xml mvn package -DskipTests -P fatjar -f karate-robot/pom.xml - name: upload binaries uses: actions/upload-artifact@v5 with: name: karate-release-${{ github.event.inputs.version }} retention-days: 5 path: | karate-core/target/*.jar karate-robot/target/*.jar ================================================ FILE: .gitignore ================================================ .DS_Store target/ .idea .project .settings .classpath .vscode .java-version *.iml *.tokens build/ bin/ .gradle gradle gradlew gradlew.* dependency-reduced-pom.xml examples/zip-release/*.jar karate-demo/activemq-data/ karate-demo/*.pem karate-demo/*.jks karate-demo/*.der karate-core/gen karate-core/*.pem karate-core/*.jks karate-core/*.der karate-robot/tessdata karate-junit4/src/test/java/com/intuit/karate/junit4/dev karate-robot/src/test/java/robot/dev ================================================ FILE: LICENSE ================================================ Copyright 2022 Karate Labs Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Karate **The open-source tool that combines API testing, mocks, performance testing, and UI automation into a single, unified framework.** [![Maven Central](https://img.shields.io/maven-central/v/io.karatelabs/karate-core.svg)](https://central.sonatype.com/namespace/io.karatelabs) [![Build Status](https://github.com/karatelabs/karate/actions/workflows/maven-build.yml/badge.svg?branch=develop)](https://github.com/karatelabs/karate/actions?query=workflow%3Amaven-build) [![GitHub release](https://img.shields.io/github/release/karatelabs/karate.svg)](https://github.com/karatelabs/karate/releases) [![Twitter Follow](https://img.shields.io/twitter/follow/getkarate?style=social)](https://twitter.com/getkarate) [![GitHub Stars](https://img.shields.io/github/stars/karatelabs/karate?style=social)](https://github.com/karatelabs/karate/stargazers)

📖 Documentation: docs.karatelabs.io

Looking for the old README? The previous README monolith is preserved at: **[github.com/karatelabs/karate/blob/v1.5.2](https://github.com/karatelabs/karate/blob/v1.5.2)** Anchor links (e.g. `#syntax-guide`, `#configuration`) can be appended to navigate directly to specific sections.
================================================ FILE: SECURITY.md ================================================ # Security Policy ## Supported Versions | Version | Supported | | ------- | ------------------ | | [latest (non-RC)](https://github.com/karatelabs/karate/releases) | :white_check_mark: | | older versions | :x: | ## Reporting a Vulnerability Private vulnerability reporting is enabled, please [follow these instructions](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability#privately-reporting-a-security-vulnerability). ================================================ FILE: _config.yml ================================================ # Site settings title: Karate description: Test Automation Made Simple. twitter_username: getkarate github_username: ptrthomas # Theme remote_theme: pmarsceill/just-the-docs # Theme settings search_enabled: true aux_links: "Karate on Github": - "//github.com/karatelabs/karate" "Karate Labs": - "//karatelabs.io" ================================================ FILE: _includes/nav.html ================================================ ================================================ FILE: _includes/toc.html ================================================ {% capture tocWorkspace %} {% comment %} Version 1.0.6 https://github.com/allejo/jekyll-toc "...like all things liquid - where there's a will, and ~36 hours to spare, there's usually a/some way" ~jaybe Usage: {% include toc.html html=content sanitize=true class="inline_toc" id="my_toc" h_min=2 h_max=3 %} Parameters: * html (string) - the HTML of compiled markdown generated by kramdown in Jekyll Optional Parameters: * sanitize (bool) : false - when set to true, the headers will be stripped of any HTML in the TOC * class (string) : '' - a CSS class assigned to the TOC * id (string) : '' - an ID to assigned to the TOC * h_min (int) : 1 - the minimum TOC header level to use; any header lower than this value will be ignored * h_max (int) : 6 - the maximum TOC header level to use; any header greater than this value will be ignored * ordered (bool) : false - when set to true, an ordered list will be outputted instead of an unordered list * item_class (string) : '' - add custom class(es) for each list item; has support for '%level%' placeholder, which is the current heading level * baseurl (string) : '' - add a base url to the TOC links for when your TOC is on another page than the actual content * anchor_class (string) : '' - add custom class(es) for each anchor element Output: An ordered or unordered list representing the table of contents of a markdown block. This snippet will only generate the table of contents and will NOT output the markdown given to it {% endcomment %} {% capture my_toc %}{% endcapture %} {% assign orderedList = include.ordered | default: false %} {% assign minHeader = include.h_min | default: 1 %} {% assign maxHeader = include.h_max | default: 6 %} {% assign nodes = include.html | split: ' maxHeader %} {% continue %} {% endif %} {% if firstHeader %} {% assign firstHeader = false %} {% assign minHeader = headerLevel %} {% endif %} {% assign indentAmount = headerLevel | minus: minHeader | add: 1 %} {% assign _workspace = node | split: '' | first }}>{% endcapture %} {% assign header = _workspace[0] | replace: _hAttrToStrip, '' %} {% assign space = '' %} {% for i in (1..indentAmount) %} {% assign space = space | prepend: ' ' %} {% endfor %} {% unless include.item_class == blank %} {% capture listItemClass %}{:.{{ include.item_class | replace: '%level%', headerLevel }}}{% endcapture %} {% endunless %} {% capture my_toc %}{{ my_toc }} {{ space }}{{ listModifier }} {{ listItemClass }} [{% if include.sanitize %}{{ header | strip_html }}{% else %}{{ header }}{% endif %}]({% if include.baseurl %}{{ include.baseurl }}{% endif %}#{{ html_id }}){% if include.anchor_class %}{:.{{ include.anchor_class }}}{% endif %}{% endcapture %} {% endfor %} {% if include.class %} {% capture my_toc %}{:.{{ include.class }}} {{ my_toc | lstrip }}{% endcapture %} {% endif %} {% if include.id %} {% capture my_toc %}{: #{{ include.id }}} {{ my_toc | lstrip }}{% endcapture %} {% endif %} {% endcapture %}{% assign tocWorkspace = '' %}{{ my_toc | markdownify | strip }} ================================================ FILE: build-docker.sh ================================================ #!/bin/bash #set -x -e # assume that karate jars are installed in maven local repo # mvn clean install -DskipTests -P pre-release KARATE_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout) # run e2e test that depends on karate-gatling mvn versions:set versions:commit -B -ntp -DnewVersion=${KARATE_VERSION} -f examples/gatling/pom.xml mvn clean test -B -ntp -f examples/gatling/pom.xml # copy only karate jars to a place where the docker image build can add from KARATE_REPO=karate-docker/karate-chrome/target/repository/io/karatelabs mkdir -p "${KARATE_REPO}" cp -r ~/.m2/repository/io/karatelabs "${KARATE_REPO}" # create / copy the karate fatjar so that the docker image build can add it mvn package -B -ntp -P fatjar -DskipTests -f karate-core/pom.xml cp "karate-core/target/karate-${KARATE_VERSION}.jar" karate-docker/karate-chrome/target/karate.jar # setup multiplatform build (ignore error if builder doesn't exist) docker buildx rm multiplatform-builder || true docker buildx create --name multiplatform-builder docker buildx use multiplatform-builder # build karate-chrome docker image that includes karate fatjar + maven jars for convenience # Only linux/amd64 is supported because google-chrome-stable is amd64-only docker buildx build \ --platform linux/amd64 \ --cache-from=type=local,src=./target/docker \ --cache-to=type=local,dest=./target/docker \ -t karatelabs/karate-chrome:${{ github.event.inputs.version }} \ -t karatelabs/karate-chrome:latest \ karate-docker/karate-chrome # Decide platform for local "load" build PLATFORM_FLAG="" if [[ "$OSTYPE" == "darwin"* ]]; then # On Mac (including M1), force linux/amd64 so Chrome is available PLATFORM_FLAG="--platform=linux/amd64" fi # Build local image (for running tests) and load into Docker docker buildx build \ --load \ ${PLATFORM_FLAG} \ --cache-from=type=local,src=./target/docker \ -t karate-chrome \ karate-docker/karate-chrome # just in case a previous run had hung (likely only in local dev) docker stop karate || true # note that this command is run as a background process docker run --name karate --rm --cap-add=SYS_ADMIN \ -v "$PWD":/karate \ -v "$HOME/.m2":/root/.m2 \ karate-chrome & # just ensure that the docker container named "karate" exists after the above command # it does not have to have completed startup, the command / karate test below will wait sleep 5 # run a test to check a particular jar packaging issue docker exec -w /karate karate mvn test -B -ntp -f karate-e2e-tests/pom.xml -Dtest=regex.RegexRunner # run tests against chrome docker exec -w /karate karate mvn test -B -ntp -f karate-e2e-tests/pom.xml -Dtest=driver.DockerRunner docker stop karate wait ================================================ FILE: examples/README.md ================================================ # Karate Examples Karate Examples are being moved to this url: [Examples](https://github.com/karatelabs/karate-examples/blob/main/README.md). <-- please start here. > The code in this folder are designed to be stand-alone Maven projects that you can use as a reference or as a starting point. ================================================ FILE: examples/consumer-driven-contracts/.gitignore ================================================ .DS_Store target/ ================================================ FILE: examples/consumer-driven-contracts/README.md ================================================ # Karate Consumer Driven Contracts Demo ## References This is a simplified version of the [example in the Karate test-doubles documentation](https://github.com/karatelabs/karate/tree/master/karate-netty#consumer-provider-example) - with JMS / queues removed and simplified to be a stand-alone maven project. These articles are recommended reading: * [API Contract Testing - Visual Guide](https://www.linkedin.com/pulse/api-contract-testing-visual-guide-peter-thomas/) * [Karate vs Pact](https://stackoverflow.com/a/64218355/143475) ## Instructions * clone the project * `mvn clean test` ## Main Artifacts You can click on the links to view the source-code. | File | Description | Comment | | ---- | ----------- | ------- | | [PaymentService.java](payment-producer/src/main/java/payment/producer/PaymentService.java) | Producer | A very simple [Spring Boot](https://spring.io/projects/spring-boot) app / REST service | | [payment-contract.feature](payment-producer/src/test/java/payment/producer/contract/payment-contract.feature) | Contract + Functional Test | [Karate](https://github.com/karatelabs/karate) API test | | [PaymentContractTest.java](payment-producer/src/test/java/payment/producer/contract/PaymentContractTest.java) | Producer Integration Test | JUnit runner for the above | | [payment-mock.feature](payment-producer/src/test/java/payment/producer/mock/payment-mock.feature) | Mock / Stub | [Karate mock](https://github.com/karatelabs/karate/tree/master/karate-netty) that *perfectly* simulates the Producer ! | | [PaymentContractAgainstMockTest.java](payment-producer/src/test/java/payment/producer/mock/PaymentContractAgainstMockTest.java) | Verify that the Mock is as per Contract | JUnit runner that points `payment-contract.feature` --> `payment-mock.feature` | | [Consumer.java](payment-consumer/src/main/java/payment/consumer/Consumer.java) | Consumer | A simple Java app that calls the Producer to do some work | | [ConsumerIntegrationTest.java](payment-consumer/src/test/java/payment/consumer/ConsumerIntegrationTest.java) | Consumer Integration Test | A JUnit *full* integration test, using the *real* Consumer and Producer | | [ConsumerIntegrationAgainstMockTest.java](payment-consumer/src/test/java/payment/consumer/ConsumerIntegrationAgainstMockTest.java) | Consumer Integration Test but using the Mock | Like the above but using the mock Producer | ================================================ FILE: examples/consumer-driven-contracts/payment-consumer/pom.xml ================================================ 4.0.0 io.karatelabs.examples examples-cdc 1.0-SNAPSHOT examples-cdc-consumer jar io.karatelabs.examples examples-cdc-producer ${project.version} commons-io commons-io 2.7 ================================================ FILE: examples/consumer-driven-contracts/payment-consumer/src/main/java/payment/consumer/Consumer.java ================================================ package payment.consumer; import com.fasterxml.jackson.databind.ObjectMapper; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import payment.producer.Payment; /** * * @author pthomas3 */ public class Consumer { private static final Logger logger = LoggerFactory.getLogger(Consumer.class); private final String paymentServiceUrl; private final ObjectMapper mapper = new ObjectMapper(); public Consumer(String paymentServiceUrl) { this.paymentServiceUrl = paymentServiceUrl; } private HttpURLConnection getConnection(String path) throws Exception { URL url = new URL(paymentServiceUrl + path); return (HttpURLConnection) url.openConnection(); } public Payment create(Payment payment) { try { HttpURLConnection con = getConnection("/payments"); con.setRequestMethod("POST"); con.setDoOutput(true); con.setRequestProperty("Content-Type", "application/json"); String json = mapper.writeValueAsString(payment); IOUtils.write(json, con.getOutputStream(), "utf-8"); int status = con.getResponseCode(); if (status != 200) { throw new RuntimeException("status code was " + status); } String content = IOUtils.toString(con.getInputStream(), StandardCharsets.UTF_8); return mapper.readValue(content, Payment.class); } catch (Exception e) { throw new RuntimeException(e); } } } ================================================ FILE: examples/consumer-driven-contracts/payment-consumer/src/test/java/logback-test.xml ================================================ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n target/karate.log %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n ================================================ FILE: examples/consumer-driven-contracts/payment-consumer/src/test/java/payment/consumer/ConsumerIntegrationAgainstMockTest.java ================================================ package payment.consumer; import com.intuit.karate.core.MockServer; import java.io.File; import org.junit.jupiter.api.AfterAll; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import payment.producer.Payment; /** * * @author pthomas3 */ class ConsumerIntegrationAgainstMockTest { static MockServer server; static Consumer consumer; @BeforeAll static void beforeAll() { File file = new File("../payment-producer/src/test/java/payment/producer/mock/payment-mock.feature"); server = MockServer.feature(file).http(0).build(); String paymentServiceUrl = "http://localhost:" + server.getPort(); consumer = new Consumer(paymentServiceUrl); } @Test void testPaymentCreate() throws Exception { Payment payment = new Payment(); payment.setAmount(5.67); payment.setDescription("test one"); payment = consumer.create(payment); assertTrue(payment.getId() > 0); assertEquals(payment.getAmount(), 5.67, 0); assertEquals(payment.getDescription(), "test one"); } @AfterAll static void afterAll() { server.stop(); } } ================================================ FILE: examples/consumer-driven-contracts/payment-consumer/src/test/java/payment/consumer/ConsumerIntegrationTest.java ================================================ package payment.consumer; import org.junit.jupiter.api.AfterAll; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.context.ConfigurableApplicationContext; import payment.producer.Payment; import payment.producer.PaymentService; /** * * @author pthomas3 */ class ConsumerIntegrationTest { static ConfigurableApplicationContext context; static Consumer consumer; @BeforeAll static void beforeAll() { context = PaymentService.start(0); String paymentServiceUrl = "http://localhost:" + PaymentService.getPort(context); consumer = new Consumer(paymentServiceUrl); } @Test void testPaymentCreate() throws Exception { Payment payment = new Payment(); payment.setAmount(5.67); payment.setDescription("test one"); payment = consumer.create(payment); assertTrue(payment.getId() > 0); assertEquals(payment.getAmount(), 5.67, 0); assertEquals(payment.getDescription(), "test one"); } @AfterAll static void afterAll() { PaymentService.stop(context); } } ================================================ FILE: examples/consumer-driven-contracts/payment-producer/pom.xml ================================================ 4.0.0 io.karatelabs.examples examples-cdc 1.0-SNAPSHOT examples-cdc-producer jar org.springframework.boot spring-boot-dependencies ${spring.boot.version} pom import org.springframework.boot spring-boot-starter-web ${spring.boot.version} ================================================ FILE: examples/consumer-driven-contracts/payment-producer/src/main/java/payment/producer/Payment.java ================================================ package payment.producer; /** * * @author pthomas3 */ public class Payment { private int id; private double amount; private String description; public int getId() { return id; } public void setId(int id) { this.id = id; } public double getAmount() { return amount; } public void setAmount(double amount) { this.amount = amount; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } } ================================================ FILE: examples/consumer-driven-contracts/payment-producer/src/main/java/payment/producer/PaymentService.java ================================================ package payment.producer; import java.util.Collection; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.server.ResponseStatusException; /** * * @author pthomas3 */ @Configuration @EnableAutoConfiguration public class PaymentService { @RestController @RequestMapping("/payments") class PaymentController { private final AtomicInteger counter = new AtomicInteger(); private final Map payments = new ConcurrentHashMap(); @PostMapping public Payment create(@RequestBody Payment payment) { int id = counter.incrementAndGet(); payment.setId(id); payments.put(id, payment); return payment; } @PutMapping("/{id:.+}") public Payment update(@PathVariable("id") int id, @RequestBody Payment payment) { payments.put(id, payment); return payment; } @GetMapping public Collection list() { return payments.values(); } @GetMapping("/{id:.+}") public Payment get(@PathVariable("id") int id) { Payment payment = payments.get(id); if (payment == null) { throw new ResponseStatusException(HttpStatus.NOT_FOUND); } return payment; } @DeleteMapping("/{id:.+}") public void delete(@PathVariable("id") int id) { Payment payment = payments.remove(id); if (payment == null) { throw new RuntimeException("payment not found, id: " + id); } } } public static ConfigurableApplicationContext start(int port) { return SpringApplication.run(PaymentService.class, new String[]{"--server.port=" + port}); } public static void stop(ConfigurableApplicationContext context) { SpringApplication.exit(context, () -> 0); } public static int getPort(ConfigurableApplicationContext context) { ServerStartedInitializingBean ss = context.getBean(ServerStartedInitializingBean.class); return ss.getLocalPort(); } @Bean public ServerStartedInitializingBean getInitializingBean() { return new ServerStartedInitializingBean(); } public static void main(String[] args) { start(8090); } } ================================================ FILE: examples/consumer-driven-contracts/payment-producer/src/main/java/payment/producer/ServerStartedInitializingBean.java ================================================ package payment.producer; import java.util.Arrays; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.boot.web.context.WebServerInitializedEvent; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; /** * * @author pthomas3 */ @Component public class ServerStartedInitializingBean implements ApplicationRunner, ApplicationListener { private static final Logger logger = LoggerFactory.getLogger(ServerStartedInitializingBean.class); private int localPort; public int getLocalPort() { return localPort; } @Override public void run(ApplicationArguments aa) throws Exception { logger.info("server started with args: {}", Arrays.toString(aa.getSourceArgs())); } @Override public void onApplicationEvent(WebServerInitializedEvent e) { localPort = e.getWebServer().getPort(); logger.info("after runtime init, local server port: {}", localPort); } } ================================================ FILE: examples/consumer-driven-contracts/payment-producer/src/test/java/karate-config.js ================================================ function() { return { paymentServiceUrl: karate.properties['payment.service.url'] } } ================================================ FILE: examples/consumer-driven-contracts/payment-producer/src/test/java/logback-test.xml ================================================ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n target/karate.log %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n ================================================ FILE: examples/consumer-driven-contracts/payment-producer/src/test/java/payment/producer/contract/PaymentContractTest.java ================================================ package payment.producer.contract; import com.intuit.karate.Results; import com.intuit.karate.Runner; import org.junit.jupiter.api.AfterAll; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.context.ConfigurableApplicationContext; import payment.producer.PaymentService; /** * * @author pthomas3 */ class PaymentContractTest { static ConfigurableApplicationContext context; @BeforeAll static void beforeAll() { context = PaymentService.start(0); } @Test void testReal() { String paymentServiceUrl = "http://localhost:" + PaymentService.getPort(context); Results results = Runner.path("classpath:payment/producer/contract/payment-contract.feature") .systemProperty("payment.service.url", paymentServiceUrl) .parallel(1); assertTrue(results.getFailCount() == 0, results.getErrorMessages()); } @AfterAll static void afterAll() { PaymentService.stop(context); } } ================================================ FILE: examples/consumer-driven-contracts/payment-producer/src/test/java/payment/producer/contract/payment-contract.feature ================================================ Feature: payment service contract test Background: * url paymentServiceUrl + '/payments' Scenario: create, get, update, list and delete payments Given request { amount: 5.67, description: 'test one' } When method post Then status 200 And match response == { id: '#number', amount: 5.67, description: 'test one' } And def id = response.id Given path id When method get Then status 200 And match response == { id: '#(id)', amount: 5.67, description: 'test one' } Given path id And request { id: '#(id)', amount: 5.67, description: 'test two' } When method put Then status 200 And match response == { id: '#(id)', amount: 5.67, description: 'test two' } When method get Then status 200 And match response contains { id: '#(id)', amount: 5.67, description: 'test two' } Given path id When method delete Then status 200 When method get Then status 200 And match response !contains { id: '#(id)', amount: '#number', description: '#string' } ================================================ FILE: examples/consumer-driven-contracts/payment-producer/src/test/java/payment/producer/mock/PaymentContractAgainstMockTest.java ================================================ package payment.producer.mock; import com.intuit.karate.Results; import com.intuit.karate.Runner; import com.intuit.karate.core.MockServer; import org.junit.jupiter.api.AfterAll; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; /** * * @author pthomas3 */ class PaymentContractAgainstMockTest { static MockServer server; @BeforeAll static void beforeAll() { server = MockServer.feature("classpath:payment/producer/mock/payment-mock.feature").http(0).build(); } @Test void testMock() { String paymentServiceUrl = "http://localhost:" + server.getPort(); Results results = Runner.path("classpath:payment/producer/contract/payment-contract.feature") .systemProperty("payment.service.url", paymentServiceUrl) .parallel(1); assertTrue(results.getFailCount() == 0, results.getErrorMessages()); } @AfterAll static void afterAll() { server.stop(); } } ================================================ FILE: examples/consumer-driven-contracts/payment-producer/src/test/java/payment/producer/mock/payment-mock.feature ================================================ Feature: payment service mock Background: * def id = 0 * def payments = {} Scenario: pathMatches('/payments') && methodIs('post') * def payment = request * def id = ~~(id + 1) * payment.id = id * payments[id + ''] = payment * def response = payment Scenario: pathMatches('/payments') * def response = $payments.* Scenario: pathMatches('/payments/{id}') && methodIs('put') * payments[pathParams.id] = request * def response = request Scenario: pathMatches('/payments/{id}') && methodIs('delete') * karate.remove('payments', pathParams.id) Scenario: pathMatches('/payments/{id}') * def response = payments[pathParams.id] ================================================ FILE: examples/consumer-driven-contracts/payment-producer/src/test/java/payment/producer/mock/test.feature ================================================ Feature: Scenario: * print 'hello' ================================================ FILE: examples/consumer-driven-contracts/pom.xml ================================================ 4.0.0 io.karatelabs.examples examples-cdc 1.0-SNAPSHOT pom payment-producer payment-consumer UTF-8 3.8.1 2.22.2 3.2.2 5.7.0 17 1.5.2 io.karatelabs karate-core ${karate.version} test org.junit.jupiter junit-jupiter-api ${junit5.version} test org.junit.jupiter junit-jupiter-engine ${junit5.version} test src/test/java **/*.java org.apache.maven.plugins maven-compiler-plugin ${maven.compiler.version} UTF-8 ${java.version} ${java.version} -Werror org.apache.maven.plugins maven-surefire-plugin ${maven.surefire.version} -Dfile.encoding=UTF-8 ================================================ FILE: examples/gatling/.gitignore ================================================ .DS_Store target/ .idea/ ================================================ FILE: examples/gatling/README.md ================================================ # karate-gatling-demo demo sample project for karate [test-doubles](https://github.com/karatelabs/karate/tree/master/karate-netty) and [gatling integration](https://github.com/karatelabs/karate/tree/master/karate-gatling) > Another example which demos the use of the Java DSL instead of Scala can be found here: [karate-todo](https://github.com/karatelabs/karate-todo). ## Instructions ``` mvn clean test ``` The above works because the `gatling-maven-plugin` has been configured to run as part of the Maven `test` phase automatically in the [`pom.xml`](pom.xml). The file location of the Gatling HTML report should appear towards the end of the console log. Copy and paste it into your browser address-bar. Here's a video of what to expect: https://twitter.com/ptrthomas/status/986463717465391104 ================================================ FILE: examples/gatling/build.gradle ================================================ plugins { id 'scala' } ext { karateVersion = '1.5.2' } dependencies { testImplementation "io.karatelabs:karate-gatling:${karateVersion}" } repositories { mavenCentral() // mavenLocal() } test { systemProperty "karate.options", System.properties.getProperty("karate.options") systemProperty "karate.env", System.properties.getProperty("karate.env") outputs.upToDateWhen { false } } sourceSets { test { resources { srcDir file('src/test/java') exclude '**/*.java' exclude '**/*.scala' } scala { srcDirs = ['src/test/java'] } } } // to run, type: "gradle gatling" task gatlingRun(type: JavaExec) { group = 'Web Tests' description = 'Run Gatling Tests' new File("${buildDir}/reports/gatling").mkdirs() classpath = sourceSets.test.runtimeClasspath main = "io.gatling.app.Gatling" args = [ // change this to suit your simulation entry-point '-s', 'mock.CatsKarateSimulation', '-rf', "${buildDir}/reports/gatling" ] systemProperties System.properties } ================================================ FILE: examples/gatling/pom.xml ================================================ 4.0.0 io.karatelabs.examples examples-gatling 1.0-SNAPSHOT jar UTF-8 17 3.6.0 ${project.version} 4.3.4 io.karatelabs karate-core ${karate.version} test all io.karatelabs karate-gatling ${karate.version} test src/test/java **/*.java org.apache.maven.plugins maven-compiler-plugin ${maven.compiler.version} UTF-8 ${java.version} ${java.version} -Werror net.alchim31.maven scala-maven-plugin 4.5.6 testCompile -Jbackend:GenBCode -Jdelambdafy:method -release:11 -deprecation -feature -unchecked -language:implicitConversions -language:postfixOps io.gatling gatling-maven-plugin ${gatling.plugin.version} src/test/java mock.CatsKarateSimulation test test ================================================ FILE: examples/gatling/src/test/java/karate-config.js ================================================ function(){ return {}; } ================================================ FILE: examples/gatling/src/test/java/logback-test.xml ================================================  false %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n false target/karate.log %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n ================================================ FILE: examples/gatling/src/test/java/mock/CatsGatlingSimulation.scala ================================================ package mock import scala.language.postfixOps import io.gatling.core.Predef._ import io.gatling.http.Predef._ import scala.concurrent.duration._ class CatsGatlingSimulation extends Simulation { MockUtils.startServer() val httpConf = http.baseUrl(System.getProperty("mock.cats.url")) val create = scenario("create") .pause(25 milliseconds) .exec(http("POST /cats") .post("/") .body(StringBody("""{ "name": "Billie" }""")) .check(status.is(200)) .check(jsonPath("$.name").is("Billie")) .check(jsonPath("$.id") .saveAs("id"))) .pause(10 milliseconds).exec( http("GET /cats/{id}") .get("/${id}") .check(status.is(200)) .check(jsonPath("$.id").is("${id}")) // intentional assertion failure .check(jsonPath("$.name").is("Billi"))) .exitHereIfFailed .exec( http("PUT /cats/{id}") .put("/${id}") .body(StringBody("""{ "id":"${id}", "name": "Bob" }""")) .check(status.is(200)) .check(jsonPath("$.id").is("${id}")) .check(jsonPath("$.name").is("Bob"))) .pause(10 milliseconds).exec( http("GET /cats/{id}") .get("/${id}") .check(status.is(200))) val delete = scenario("delete") .pause(15 milliseconds).exec( http("GET /cats") .get("/") .check(status.is(200)) .check(jsonPath("$[*].id").findAll.optional .saveAs("ids"))) .doIf(_.contains("ids")) { foreach("${ids}", "id") { pause(20 milliseconds).exec( http("DELETE /cats/{id}") .delete("/${id}") .check(status.is(200)) .check(bodyString.is(""))) .pause(10 milliseconds).exec( http("GET /cats/{id}") .get("/${id}") .check(status.is(404))) } } setUp( create.inject(rampUsers(10) during (5 seconds)).protocols(httpConf), delete.inject(rampUsers(5) during (5 seconds)).protocols(httpConf) ) } ================================================ FILE: examples/gatling/src/test/java/mock/CatsKarateSimulation.scala ================================================ package mock import scala.language.postfixOps import com.intuit.karate.gatling.PreDef._ import io.gatling.core.Predef._ import scala.concurrent.duration._ class CatsKarateSimulation extends Simulation { MockUtils.startServer() val feeder = Iterator.continually(Map("catName" -> MockUtils.getNextCatName)) val protocol = karateProtocol( "/cats/{id}" -> Nil, "/cats" -> pauseFor("get" -> 15, "post" -> 25) ) protocol.nameResolver = (req, ctx) => req.getHeader("karate-name") val create = scenario("create").feed(feeder).exec(karateFeature("classpath:mock/cats-create.feature")) val delete = scenario("delete").group("delete cats") { exec(karateFeature("classpath:mock/cats-delete.feature@name=delete")) } val custom = scenario("custom").exec(karateFeature("classpath:mock/custom-rpc.feature")) setUp( create.inject(rampUsers(10) during (5 seconds)).protocols(protocol), delete.inject(rampUsers(5) during (5 seconds)).protocols(protocol), custom.inject(rampUsers(10) during (5 seconds)).protocols(protocol) ) } ================================================ FILE: examples/gatling/src/test/java/mock/MockUtils.java ================================================ package mock; import com.intuit.karate.core.MockServer; import com.intuit.karate.PerfContext; import com.intuit.karate.Runner; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; /** * * @author pthomas3 */ public class MockUtils { public static void startServer() { MockServer server = MockServer.feature("classpath:mock/mock.feature").build(); System.setProperty("mock.cats.url", "http://localhost:" + server.getPort() + "/cats"); } private static final List catNames = (List) Runner.runFeature("classpath:mock/feeder.feature", null, false).get("names"); private static final AtomicInteger counter = new AtomicInteger(); public static String getNextCatName() { return catNames.get(counter.getAndIncrement() % catNames.size()); } public static Map myRpc(Map map, PerfContext context) { long startTime = System.currentTimeMillis(); // this is just an example, you can put any kind of code here int sleepTime = (Integer) map.get("sleep"); try { Thread.sleep(sleepTime); } catch (Exception e) { throw new RuntimeException(e); } long endTime = System.currentTimeMillis(); // and here is where you send the performance data to the reporting engine context.capturePerfEvent("myRpc-" + sleepTime, startTime, endTime); return Collections.singletonMap("success", true); } } ================================================ FILE: examples/gatling/src/test/java/mock/cats-create.feature ================================================ Feature: cats crud Background: * url karate.properties['mock.cats.url'] Scenario: create, get and update cat # example of using the gatling session / feeder data # note how this can still work as a normal test, without gatling * def name = karate.get('__gatling.catName', 'Billie') Given request { name: '#(name)' } When method post Then status 200 And match response == { id: '#uuid', name: '#(name)' } * def id = response.id Given path id When method get # this step may randomly fail because another thread is doing deletes Then status 200 # intentional assertion failure And match response == { id: '#(id)', name: 'Billi' } # since we failed above, these lines will not be executed Given path id When request { id: '#(id)', name: 'Bob' } When method put Then status 200 And match response == { id: '#(id)', name: 'Bob' } When method get Then status 200 And match response contains { id: '#(id)', name: 'Bob' } ================================================ FILE: examples/gatling/src/test/java/mock/cats-delete-one.feature ================================================ @ignore Feature: delete cat by id and verify Scenario: Given url karate.properties['mock.cats.url'] And path id When method delete Then status 200 And match response == '' Given path id And header karate-name = 'cats-get-404' When method get Then status 404 ================================================ FILE: examples/gatling/src/test/java/mock/cats-delete.feature ================================================ Feature: delete all cats found Background: * url karate.properties['mock.cats.url'] Scenario: this scenario will be ignored because the gatling script looks for the tag @name=delete * print 'this should not appear in the logs !' When method get Then status 400 @name=delete Scenario: get all cats and then delete each by id When method get Then status 200 * def delete = read('cats-delete-one.feature') * def result = call delete response ================================================ FILE: examples/gatling/src/test/java/mock/custom-rpc.feature ================================================ @ignore Feature: even java interop performance test reports are possible Background: * def Utils = Java.type('mock.MockUtils') Scenario: fifty * def payload = { sleep: 50 } * def response = Utils.myRpc(payload, karate) * match response == { success: true } Scenario: seventy five * def payload = { sleep: 75 } * def response = Utils.myRpc(payload, karate) # this is deliberately set up to fail * match response == { success: false } Scenario: hundred * def payload = { sleep: 100 } * def response = Utils.myRpc(payload, karate) * match response == { success: true } ================================================ FILE: examples/gatling/src/test/java/mock/feeder.feature ================================================ Feature: to generate a list of cat names Scenario: any variables defined can be retrieved when called via the java api * def names = ['Bob', 'Wild', 'Nyan', 'Ceiling'] ================================================ FILE: examples/gatling/src/test/java/mock/mock.feature ================================================ Feature: cats stateful crud Background: * def uuid = function(){ return java.util.UUID.randomUUID() + '' } * def cats = {} Scenario: pathMatches('/cats') && methodIs('post') * def cat = request * def id = uuid() * cat.id = id * cats[id] = cat * def response = cat Scenario: pathMatches('/cats') * def response = $cats.* Scenario: pathMatches('/cats/{id}') && methodIs('put') * cats[pathParams.id] = request * def response = request Scenario: pathMatches('/cats/{id}') && methodIs('delete') * karate.remove('cats', pathParams.id) * def responseDelay = 850 Scenario: pathMatches('/cats/{id}') * def response = cats[pathParams.id] * def responseStatus = response ? 200 : 404 ================================================ FILE: examples/image-comparison/README.md ================================================ # Karate Image Comparison This project is designed to demonstrate basic usage of the [Image Comparison](https://github.com/karatelabs/karate/#compare-image) feature. You can also watch a video explanation [here](https://youtu.be/wlvmNBraP60). A more detailed video deep-dive can be viewed by skipping to 33:30 of this video: [Karate Version 1.3.0 Release Webinar ](https://youtu.be/oMsKNE_ctaM?t=2009) ## Overview The [Image Comparison](https://github.com/karatelabs/karate/#compare-image) feature was introduced in [Karate 1.3.0](https://github.com/karatelabs/karate/wiki/1.3.0-Upgrade-Guide). As a new feature with a number of options and a new UI component we wanted to provide a simple introduction to help users get started. The included features are numbered 1 through 7 and build on each other. They are intended to demonstrate how you might start from scratch without any baseline images on a new project: * `1_establish_baseline.feature` establishes baseline images to use in future test runs * `2_compare_baseline.feature` compares dynamic screenshots against our baseline images * `3_custom_rebase.feature` demonstrates the use of the `onShowRebase` handler to customize the filename when rebasing * `4_generic_rebase.feature` shows a slightly more advanced use of the `onShowRebase` handler that incorporates image comparison configuration options * `5_custom_config.feature` shows a complete scenario that is similar to what you might use in real tests * `6_outline.feature` explores a more complex use case with multiple browsers * `7_api.feature` solves some issues identified in the outline feature above using the JS API There is also a [screencast](https://youtu.be/wlvmNBraP60) that demonstrates basic usage of the diff UI in the Karate HTML report. ## Running The `5_custom_config.feature` is a complete [Karate UI test](https://github.com/karatelabs/karate/tree/master/karate-core) that can be executed by running `ImageComparisonRunner` as a JUnit test. You will be able to open the HTML report (the file-name will appear at the end of the console log) and refresh it when re-running the test. To manually run the test execute the following commands: * Install maven artifacts from the latest [develop](https://github.com/karatelabs/karate/tree/develop) branch locally ``` mvn clean install -P pre-release ``` * Run the test from the `examples/image-comparison` directory ``` mvn clean test -Dtest=ImageComparisonRunner ``` ## Debugging You should be able to use the [Karate extension for Visual Studio Code](https://github.com/karatelabs/karate/wiki/IDE-Support#vs-code-karate-plugin) for stepping-through a test for troubleshooting. ================================================ FILE: examples/image-comparison/pom.xml ================================================ 4.0.0 io.karatelabs.examples image-comparison-test 1.0-SNAPSHOT jar UTF-8 17 3.8.1 1.5.2 io.karatelabs karate-junit5 ${karate.version} test src/test/java **/*.java org.apache.maven.plugins maven-compiler-plugin ${maven.compiler.version} UTF-8 ${java.version} ${java.version} -Werror org.apache.maven.plugins maven-surefire-plugin 2.22.2 ================================================ FILE: examples/image-comparison/src/test/java/karate-config.js ================================================ function fn() { karate.configure('retry', { count: 20, interval: 200 }) return { baseUrl: karate.properties['web.url.base'] || 'http://localhost:8080/', browsers: [ { deviceType: 'phone', width: 375, height: 667, userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1' }, { deviceType: 'tablet', width: 820, height: 1180, useragent: 'Mozilla/5.0 (iPad; CPU OS 13_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/87.0.4280.77 Mobile/15E148 Safari/604.1' } ], emulateBrowser(deviceType) { const browser = karate.get('browsers').find(browser => browser.deviceType === deviceType) return driver.emulateDevice(browser.width, browser.height, browser.userAgent) } } } ================================================ FILE: examples/image-comparison/src/test/java/logback-test.xml ================================================ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n target/karate.log %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n ================================================ FILE: examples/image-comparison/src/test/java/ui/1_establish_baseline.feature ================================================ Feature: Image comparison demo Background: * configure driver = { type: 'chrome', timeout: 5000, screenshotOnFailure: false } * driver baseUrl + '?r=0.1' * emulateBrowser('phone') Scenario: Landing page * configure imageComparison = { mismatchShouldPass: true } * def loadingScreenshot = screenshot() * compareImage { latest: #(loadingScreenshot) } * waitFor('.welcome') * def loadedScreenshot = screenshot() * compareImage { latest: #(loadedScreenshot) } ================================================ FILE: examples/image-comparison/src/test/java/ui/2_compare_baseline.feature ================================================ Feature: Image comparison demo Background: * configure driver = { type: 'chrome', timeout: 5000, screenshotOnFailure: false } * driver baseUrl + '?r=0.75' * emulateBrowser('phone') Scenario: Landing page * configure imageComparison = { mismatchShouldPass: true } * def loadingScreenshot = screenshot() * compareImage { baseline: 'this:screenshots/latest.png', latest: #(loadingScreenshot) } * waitFor('.welcome') * def loadedScreenshot = screenshot() * compareImage { baseline: 'this:screenshots/latest.png', latest: #(loadedScreenshot) } ================================================ FILE: examples/image-comparison/src/test/java/ui/3_custom_rebase.feature ================================================ Feature: Image comparison demo Background: * configure driver = { type: 'chrome', timeout: 5000, screenshotOnFailure: false } * driver baseUrl + '?r=0.75' * emulateBrowser('phone') Scenario: Landing page * def loadingScreenshot = screenshot() * configure imageComparison = { onShowRebase: "(cfg, saveAs) => saveAs('loading.png')", mismatchShouldPass: true } * compareImage { baseline: 'this:screenshots/latest.png', latest: #(loadingScreenshot) } * waitFor('.welcome') * def loadedScreenshot = screenshot() * configure imageComparison = { onShowRebase: "(cfg, saveAs) => saveAs('loaded.png')", mismatchShouldPass: true } * compareImage { baseline: 'this:screenshots/latest.png', latest: #(loadedScreenshot) } ================================================ FILE: examples/image-comparison/src/test/java/ui/4_generic_rebase.feature ================================================ Feature: Image comparison demo Background: * configure imageComparison = { onShowRebase: '(cfg, saveAs) => saveAs(cfg.name)', mismatchShouldPass: true } * configure driver = { type: 'chrome', timeout: 5000, screenshotOnFailure: false } * driver baseUrl + '?r=1.2' * emulateBrowser('phone') Scenario: Landing page * def loadingScreenshot = screenshot() * def loadingOpts = """ { name: 'loading.png' } """ * compareImage { baseline: 'this:screenshots/loading.png', latest: #(loadingScreenshot), options: #(loadingOpts) } * waitFor('.welcome') * def loadedScreenshot = screenshot() * def loadedOpts = """ { name: 'loaded.png' } """ * compareImage { baseline: 'this:screenshots/loaded.png', latest: #(loadedScreenshot), options: #(loadedOpts) } ================================================ FILE: examples/image-comparison/src/test/java/ui/5_custom_config.feature ================================================ Feature: Image comparison demo Background: * configure imageComparison = { onShowRebase: '(cfg, saveAs) => saveAs(cfg.name)' } * configure driver = { type: 'chrome', timeout: 5000, screenshotOnFailure: false } * driver baseUrl + '?r=1.2' * emulateBrowser('phone') Scenario: Landing page * def loadingScreenshot = screenshot() * def loadingOpts = """ { name: 'loading.png', "ignoredBoxes": [{ "top": 278, "left": 131, "bottom": 391, "right": 246 }] } """ * compareImage { baseline: 'this:screenshots/loading.png', latest: #(loadingScreenshot), options: #(loadingOpts) } * waitFor('.welcome') * def loadedScreenshot = screenshot() * def loadedOpts = """ { name: 'loaded.png', "ignoredBoxes": [{ "top": 73, "left": 17, "bottom": 125, "right": 188 }] } """ * compareImage { baseline: 'this:screenshots/loaded.png', latest: #(loadedScreenshot), options: #(loadedOpts) } ================================================ FILE: examples/image-comparison/src/test/java/ui/6_outline.feature ================================================ Feature: Image comparison demo Background: * configure driver = { type: 'chrome', timeout: 5000, screenshotOnFailure: false } @setup Scenario: * copy data = browsers Scenario Outline: Landing page - # change to `false` after establishing baseline images... it would be nice if the baseline was created automatically * def firstRun = true * configure imageComparison = { onShowRebase: '(cfg, saveAs) => saveAs(cfg.name)', mismatchShouldPass: #(firstRun) } * driver baseUrl + (firstRun ? '?r=0.75' : '?r=1.5') * emulateBrowser('') # we have a problem below: the `tablet` device has a different screen size and our `ignoredBoxes` will be off * def loadingScreenshot = screenshot() * def loadingOpts = """ { name: 'loading_.png', "ignoredBoxes": [{ "top": 278, "left": 131, "bottom": 391, "right": 246 }] } """ * def loadingBaselinePath = firstRun ? null : `this:screenshots/${loadingOpts.name}` * compareImage { baseline: #(loadingBaselinePath), latest: #(loadingScreenshot), options: #(loadingOpts) } * waitFor('.welcome') * def loadedScreenshot = screenshot() * def loadedOpts = """ { name: 'loaded_.png', "ignoredBoxes": [{ "top": 73, "left": 17, "bottom": 125, "right": 188 }] } """ * def loadedBaselinePath = firstRun ? null : `this:screenshots/${loadedOpts.name}` * compareImage { baseline: #(loadedBaselinePath), latest: #(loadedScreenshot), options: #(loadedOpts) } Examples: | karate.setup().data | ================================================ FILE: examples/image-comparison/src/test/java/ui/7_api.feature ================================================ Feature: Image comparison demo Background: # `screenGrab` processes a named image comparison by: # - capturing a screenshot to use as the latest image # - locating the correct baseline image from the filesystem (if one exists) # - fetching comparison options from the filesystem (if they exist) # - automatically copying the latest image to be the new baseline when no baseline exists # - executing `compareImage` using the named baseline image, the latest screenshot, and dynamically-loaded options * def screenGrab = """ function (name) { const latestBytes = screenshot(false) const File = Java.type('java.io.File') const latestFile = karate.write(latestBytes, `screenshots/${name}.png`) const baselineFile = new File(karate.toAbsolutePath('this:screenshots'), `${name}.png`) const optionsFile = new File(karate.toAbsolutePath('this:screenshots/config'), `${name}.json`) // read options from browser-specific config JSON (if exists) const options = optionsFile.exists() ? karate.read('file:' + optionsFile.getPath()) : {} options.baselinePath = baselineFile.getPath() options.latestPath = new File(karate.toAbsolutePath('this:' + latestFile.getPath())).getPath() options.optionsPath = optionsFile.getPath() let baselinePath = 'file:' + options.baselinePath if (!baselineFile.exists()) { // automatically copy latest image to baseline when no baseline exists java.nio.file.Files.copy(latestFile.toPath(), baselineFile.toPath()) baselinePath = null } const result = karate.compareImage(baselinePath, latestBytes, options) if (result.error && !result.isBaselineMissing) throw new Error(result.error) return result } """ # instead of manually downloading / managing rebased images we'll provide a shell command to copy/paste * def rebaseFn = 'cfg => `cp \\\\\n ${cfg.latestPath} \\\\\n ${cfg.baselinePath}`' # since we now want the configuration to be stored on the filesystem we'll provide a shell command to copy/paste * def configFn = '(imgConfigStr, cfg) => `cat << EOF > ${cfg.optionsPath}\n${imgConfigStr}\nEOF`' * configure imageComparison = { mismatchShouldPass: true, onShowRebase: #(rebaseFn), onShowConfig: #(configFn) } * configure driver = { type: 'chrome', timeout: 5000, screenshotOnFailure: false } * driver baseUrl @setup Scenario: * copy data = browsers Scenario Outline: Landing page - * emulateBrowser('') * screenGrab('loading_') * waitFor('.welcome') * screenGrab('loaded_') Examples: | karate.setup().data | ================================================ FILE: examples/image-comparison/src/test/java/ui/ImageComparisonRunner.java ================================================ package ui; import com.intuit.karate.http.HttpServer; import com.intuit.karate.junit5.Karate; import org.junit.jupiter.api.BeforeAll; class ImageComparisonRunner { @BeforeAll public static void beforeAll() { HttpServer server = MockRunner.start(0); System.setProperty("web.url.base", "http://localhost:" + server.getPort()); } @Karate.Test Karate testUi() { return Karate.run("classpath:ui/7_api.feature"); } } ================================================ FILE: examples/image-comparison/src/test/java/ui/MockRunner.java ================================================ package ui; import com.intuit.karate.http.HttpServer; import com.intuit.karate.http.ServerConfig; import org.junit.jupiter.api.Test; /** * run this as a junit test to start an http server at port 8080 the html page * can be viewed at http://localhost:8080/ kill / stop this process when done */ class MockRunner { @Test public void testStart() { start(8080).waitSync(); } public static HttpServer start(int port) { ServerConfig config = new ServerConfig("src/test/java/ui/html") .autoCreateSession(true); return HttpServer.config(config).http(port).build(); } } ================================================ FILE: examples/image-comparison/src/test/java/ui/html/index.html ================================================ Karate Image Comparison ================================================ FILE: examples/image-comparison/src/test/java/ui/screenshots/config/loaded_phone.json ================================================ { "ignoredBoxes": [ { "top": 75, "left": 13, "bottom": 128, "right": 191 } ] } ================================================ FILE: examples/image-comparison/src/test/java/ui/screenshots/config/loaded_tablet.json ================================================ { "ignoredBoxes": [ { "top": 59, "left": 10, "bottom": 139, "right": 195 } ] } ================================================ FILE: examples/image-comparison/src/test/java/ui/screenshots/config/loading_phone.json ================================================ { "ignoredBoxes": [ { "top": 262, "left": 108, "bottom": 396, "right": 261 } ] } ================================================ FILE: examples/image-comparison/src/test/java/ui/screenshots/config/loading_tablet.json ================================================ { "ignoredBoxes": [ { "top": 531, "left": 344, "bottom": 652, "right": 471 } ] } ================================================ FILE: examples/mobile-test/README.md ================================================ # Examples - Karate Mobile (Appium) > Please consider Mobile support as experimental. But we are very close and there are some teams that use Karate for simple use-cases. Please contribute code if you can. ## Overview This project is to replicate issues with the [Karate UI framework](https://github.com/karatelabs/karate/tree/develop/karate-core) for mobile app testing using appium. ## Running > Before running: > * change `karate.version` in [`pom.xml`](pom.xml) > * change `platformVersion`,`avd` in [`karate-config.js`](src/test/java/karate-config.js) according to your local android `avd` setup and add/remove the desiredCapabilities according to your requirement. > * start appium server manually when `android.feature` has `configure driver` with `start: false` > * change `configure driver` to `start: true` if you want karate to start appium driver automatically (this is experimental and needs appium pre-installed via `npm`) The [`android.feature`](src/test/java/android/android.feature) is a simple [Karate UI test](https://github.com/karatelabs/karate/tree/develop/karate-core), and executing `AndroidRunner` as a JUnit test will run it. Refer [`Documentation`](https://github.com/karatelabs/karate/tree/develop/karate-core#appium) for currently supported methods ================================================ FILE: examples/mobile-test/pom.xml ================================================ 4.0.0 io.karatelabs.examples mobile-test 1.0-SNAPSHOT jar UTF-8 17 3.8.1 2.22.2 1.5.2 io.karatelabs karate-junit5 ${karate.version} test src/test/java **/*.java org.apache.maven.plugins maven-compiler-plugin ${maven.compiler.version} UTF-8 ${java.version} ${java.version} -Werror org.apache.maven.plugins maven-surefire-plugin ${maven.surefire.version} -Dfile.encoding=UTF-8 ================================================ FILE: examples/mobile-test/src/test/java/android/AndroidTest.java ================================================ package android; import com.intuit.karate.junit5.Karate; /** * @author babusekaran */ class AndroidTest { @Karate.Test public Karate test() { return Karate.run("classpath:android/android.feature"); } } ================================================ FILE: examples/mobile-test/src/test/java/android/android.feature ================================================ Feature: android test Background: App Preset * configure driver = { type: 'android', webDriverPath : "/wd/hub", start: false, httpConfig : { readTimeout: 120000 }} Scenario: android mobile app UI tests Given driver { webDriverSession: { desiredCapabilities : "#(android.desiredConfig)"} } And driver.click('#com.bs.droidaction:id/showTextCheckBox') And driver.clear('#com.bs.droidaction:id/showTextOnDelay').input("10000") And driver.input('#com.bs.droidaction:id/editTextBox', "KarateDSL") And driver.click('#com.bs.droidaction:id/showTextCheckBox') And retry(10, 1000).waitForAny("#com.bs.droidaction:id/nameTextView", "//android.widget.TextView[@text='KarateDSL']") Then match driver.text('#com.bs.droidaction:id/nameTextView') == 'KarateDSL' And driver.click('#com.bs.droidaction:id/showTextCheckBox') And assert (optional('#com.bs.droidaction:id/nameTextView').present != true) ================================================ FILE: examples/mobile-test/src/test/java/karate-config.js ================================================ function fn() { var config = {} var android = {} android["desiredConfig"] = { "app" : "https://github.com/babusekaran/droidAction/raw/main/build/UiDemo.apk", "newCommandTimeout" : 300, "platformVersion" : "9.0", "platformName" : "Android", "connectHardwareKeyboard" : true, "deviceName" : "emulator-5554", "avd" : "Pixel2_PIE", "automationName" : "UiAutomator2" } config["android"] = android return config; } ================================================ FILE: examples/mobile-test/src/test/java/logback-test.xml ================================================ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n target/karate.log %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n ================================================ FILE: examples/profiling-test/pom.xml ================================================ 4.0.0 io.karatelabs.examples profiling-test 1.0-SNAPSHOT jar UTF-8 17 3.6.0 1.5.2 4.1.1 io.karatelabs karate-gatling ${karate.version} test src/test/java **/*.java org.apache.maven.plugins maven-compiler-plugin ${maven.compiler.version} UTF-8 ${java.version} ${java.version} -Werror net.alchim31.maven scala-maven-plugin 4.5.6 testCompile -Jbackend:GenBCode -Jdelambdafy:method -target:jvm-1.8 -deprecation -feature -unchecked -language:implicitConversions -language:postfixOps io.gatling gatling-maven-plugin ${gatling.plugin.version} src/test/java perf.TestSimulation test test profiling-test ================================================ FILE: examples/profiling-test/src/test/java/karate-config.js ================================================ function fn() { var config = karate.call('classpath:perf/called.feature'); karate.log('config:', config); return config; } ================================================ FILE: examples/profiling-test/src/test/java/logback-test.xml ================================================  false %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n false target/karate.log %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n ================================================ FILE: examples/profiling-test/src/test/java/perf/Main.java ================================================ package perf; import com.intuit.karate.PerfHook; import com.intuit.karate.Runner; import com.intuit.karate.core.FeatureResult; import com.intuit.karate.core.PerfEvent; import com.intuit.karate.core.ScenarioRuntime; import com.intuit.karate.http.HttpRequest; /** * * @author pthomas3 */ public class Main { public static void main(String[] args) { TestUtils.startServer(); int count = 0; PerfHook hook = new PerfHook() { @Override public String getPerfEventName(HttpRequest request, ScenarioRuntime sr) { return request.getMethod() + " " + request.getUrl(); } @Override public void reportPerfEvent(PerfEvent event) { } @Override public void submit(Runnable runnable) { runnable.run(); } @Override public void afterFeature(FeatureResult fr) { } @Override public void pause(Number millis) { } }; Runner.Builder builder = Runner.builder(); while (true) { Runner.callAsync(builder, "classpath:perf/test.feature", null, hook); count++; System.out.print(count + " "); if (count % 100 == 0) { System.out.println(""); } } } } ================================================ FILE: examples/profiling-test/src/test/java/perf/TestSimulation.scala ================================================ package perf import com.intuit.karate.gatling.PreDef._ import io.gatling.core.Predef._ import scala.concurrent.duration._ class TestSimulation extends Simulation { val protocol = karateProtocol() TestUtils.startServer() val main = scenario("main").exec(karateFeature("classpath:perf/main.feature")) val called = scenario("called").exec(karateFeature("classpath:perf/called.feature")) val chained = scenario("chained") .exec(main) .exec(karateSet("extraKey", s => "extraValue")) .exec(called) setUp( chained.inject( rampUsers(20).during(5.seconds), constantUsersPerSec(20).during(10.minutes) ).protocols(protocol) ) } ================================================ FILE: examples/profiling-test/src/test/java/perf/TestUtils.java ================================================ package perf; import com.intuit.karate.core.MockServer; /** * * @author pthomas3 */ public class TestUtils { public static void startServer() { MockServer server = MockServer.feature("classpath:perf/mock.feature").build(); System.setProperty("mock.server.url", "http://localhost:" + server.getPort()); } } ================================================ FILE: examples/profiling-test/src/test/java/perf/called.feature ================================================ Feature: Scenario: * def var1 = { foo: 'bar' } * def var2 = { baz: { hello: 'world' } } * def var3 = function(name){ return 'hello ' + name } * def extraKey = karate.get('extraKey') # to see the next line in the log, change level to warn * if (extraKey) karate.logger.info('called from gatling:', extraKey) ================================================ FILE: examples/profiling-test/src/test/java/perf/main.feature ================================================ Feature: Background: * def backgroundData = callonce read('called.feature') * url karate.properties['mock.server.url'] Scenario: * path 'test' * method get * match response == { success: true } * match backgroundData contains { var1: { foo: 'bar' } } * def scenarioData = call read('called.feature') { callArgData: '#backgroundData' } * match scenarioData contains { var2: { baz: { hello: 'world' } } } ================================================ FILE: examples/profiling-test/src/test/java/perf/mock.feature ================================================ Feature: Scenario: pathMatches('/test') * def response = { success: true } ================================================ FILE: examples/robot-test/README.md ================================================ # Examples - Karate Robot This is designed to be a simple Maven project to get started with [`karate-robot`](https://github.com/karatelabs/karate/tree/master/karate-robot). * [`chrome.feature`](src/test/java/chrome/chrome.feature) - this example will switch to Google Chrome (assumed to be open) and perform a search using only keystrokes and navigation using image-detection ================================================ FILE: examples/robot-test/pom.xml ================================================ 4.0.0 io.karatelabs.examples examples-robot-test 1.0-SNAPSHOT jar UTF-8 17 3.8.1 2.22.2 1.5.2 io.karatelabs karate-robot ${karate.version} test io.karatelabs karate-junit5 ${karate.version} test src/test/java **/*.java org.apache.maven.plugins maven-compiler-plugin ${maven.compiler.version} UTF-8 ${java.version} ${java.version} -Werror org.apache.maven.plugins maven-surefire-plugin ${maven.surefire.version} -Dfile.encoding=UTF-8 ================================================ FILE: examples/robot-test/src/test/java/karate-config.js ================================================ function fn() { return {}; } ================================================ FILE: examples/robot-test/src/test/java/logback-test.xml ================================================ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n target/karate.log %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n ================================================ FILE: examples/robot-test/src/test/java/mac/ChromeRunner.java ================================================ package mac; import com.intuit.karate.junit5.Karate; /** * * @author pthomas3 */ class ChromeRunner { @Karate.Test Karate testChrome() { return Karate.run("classpath:mac/chrome.feature"); } } ================================================ FILE: examples/robot-test/src/test/java/mac/chrome.feature ================================================ Feature: mac - robot and chrome Scenario: # * karate.exec('Chrome') # or make sure Chrome is open * robot { window: '^Chrome', highlight: true, highlightDuration: 500 } * input(Key.META + 't') * input('karate dsl' + Key.ENTER) * waitFor('tams.png').click() * delay(2000) * screenshot() ================================================ FILE: examples/robot-test/src/test/java/win/CalcRunner.java ================================================ package win; import com.intuit.karate.junit5.Karate; /** * * @author pthomas3 */ class CalcRunner { @Karate.Test Karate testCalc() { return Karate.run("classpath:win/calc.feature"); } } ================================================ FILE: examples/robot-test/src/test/java/win/calc.feature ================================================ Feature: windows calculator Scenario: * robot { window: 'Calculator', fork: 'calc', highlight: true, highlightDuration: 500 } * click('Clear') * click('One') * click('Plus') * click('Two') * click('Equals') * match locate('#CalculatorResults').name == 'Display is 3' * screenshot() * click('Close Calculator') ================================================ FILE: examples/ui-test/README.md ================================================ # Karate UI Test This project is designed to be the simplest way to replicate issues with the [Karate UI framework](https://github.com/karatelabs/karate/tree/master/karate-core) for web-browser testing. It includes an HTTP mock that serves HTML and JavaScript, which you can easily modify to simulate complex situations such as a slow-loading element. To submit an issue after you have a way to replicate the scenario, follow these instructions: [How to Submit an Issue](https://github.com/karatelabs/karate/wiki/How-to-Submit-an-Issue). ## Overview To point to a specifc version of Karate, edit the `pom.xml`. If you are working with the source-code of Karate, follow the [developer guide](https://github.com/karatelabs/karate/wiki/Developer-Guide). You can view the HTML source of `page-01.html` to see how it works. It depends on `karate.js` which is very simple, so you can see how to add any JS (if required) along the same lines. The code in [`MockRunner.java`](src/test/java/ui/MockRunner.java) starts a Karate HTTP server. Note how it is very simple - but able to serve both HTML and JS. If you need to include navigation to a second page, you can easily add a second HTML file. To manually verify the HTML that will be served, you can start the mock-server by running `MockRunner` as a JUnit test, and then opening [`http://localhost:8080/page-01`](http://localhost:8080/page-01) in a browser. And yes, hot-reloading is possible ! ## Running The `test.feature` is a simple [Karate UI test](https://github.com/karatelabs/karate/tree/master/karate-core) that can be executed by running `UiRunner` as a JUnit test. You will be able to open the HTML report (the file-name will appear at the end of the console log) and refresh it when re-running the test. For convenience, this test is a `Scenario Outline` - set up so that you can add multiple browser targets or driver implementations. This makes it easy to validate cross-browser compatibility. ## Debugging You should be able to use the [Karate extension for Visual Studio Code](https://github.com/karatelabs/karate/wiki/IDE-Support#vs-code-karate-plugin) for stepping-through a test for troubleshooting. ## WebDriver Tips If you are targeting a WebDriver implementation, you may need to experiment with HTTP calls. Don't forget that that is Karate's core competency ! So you can use a "scratchpad" Karate test on the side, like this, after you have manually started a "driver executable", [`chromedriver`](https://chromedriver.chromium.org) in this case: ```cucumber Feature: Scenario: * url 'http://localhost:9515' * path 'session' * request {"capabilities":{"browserName":"msedge"}} * method post ``` Within a test script, as a convenience, the `driver` object exposes an `http` property, which makes it easy to make custom-crafted WebDriver requests using the [`Http` helper / class](../../karate-core/src/main/java/com/intuit/karate/Http.java). Note that this will be available only after the [`driver` keyword](https://github.com/karatelabs/karate/tree/master/karate-core#driver) has been used, and thus a WebDriver session has been initialized. Here is an example of [getting the page title](https://w3c.github.io/webdriver/#get-title): ```cucumber * def temp = driver.http.path('title').get().body().asMap() * print 'temp:', temp ``` Which results in a `GET` request to: `http://localhost:9515/session/{sessionId}/title` - and the response body will be printed. Now you can easily extract data out of the response JSON. And here is how you can make a `POST` request, to [navigate to a given URL](https://w3c.github.io/webdriver/#navigate-to): ```cucumber * driver.http.path('url').post({ url: 'https://github.com' }) ``` And note that the [official Karate plugins](https://github.com/karatelabs/karate/wiki/IDE-Support) are really convenient for re-running tests - or you can pause a test using a break-point and [type in interactive commands](https://twitter.com/getkarate/status/1546468347826827264). ## DevTools Protocol Tips When using the driver type `chrome`, you can call the `send()` method and pass a raw JSON message that will be sent to the Chrome browser using a WebSocket connection. For example here is how to get the [metadata about frames](https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-getFrameTree): ```cucumber * def temp = driver.send({ method: 'Page.getFrameTree' }) * print 'temp:', temp ``` This will result in the following raw message sent (Karate will supply the `id` automatically): ``` {"method":"Page.getFrameTree","id":7} ``` Chrome will respond with something like this, which should be viewable in the log / console: ``` {"id":7,"result":{"frameTree":{"frame":{"id":"11B3A5ABDEE5802201D84389EE0215B8","loaderId":"D2241AD7B86ED533F095F907A78A1208","url":"http://localhost:52664/page-01","securityOrigin":"http://localhost:52664","mimeType":"text/html"}}}} ``` You can do more, but this should be sufficient for exploring the possible commands and troubleshooting via trial and error. And then you can suggest / contribute changes to be made to the code, e.g. the [DevToolsDriver](../../karate-core/src/main/java/com/intuit/karate/driver/DevToolsDriver.java). ================================================ FILE: examples/ui-test/pom.xml ================================================ 4.0.0 io.karatelabs.examples examples-ui-test 1.0-SNAPSHOT jar UTF-8 17 3.6.0 1.5.2 io.karatelabs karate-junit5 ${karate.version} test src/test/java **/*.java org.apache.maven.plugins maven-compiler-plugin ${maven.compiler.version} UTF-8 ${java.version} ${java.version} -Werror org.apache.maven.plugins maven-surefire-plugin 2.22.2 ================================================ FILE: examples/ui-test/src/test/java/logback-test.xml ================================================ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n target/karate.log %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n ================================================ FILE: examples/ui-test/src/test/java/ui/MockRunner.java ================================================ package ui; import com.intuit.karate.http.HttpServer; import com.intuit.karate.http.ServerConfig; import org.junit.jupiter.api.Test; /** * run this as a junit test to start an http server at port 8080 the html page * can be viewed at http://localhost:8080/page-01 kill / stop this process when * done */ class MockRunner { @Test public void testStart() { start(8080).waitSync(); } public static HttpServer start(int port) { ServerConfig config = new ServerConfig("src/test/java/ui/html") .autoCreateSession(true); return HttpServer.config(config).port(port).build(); } } ================================================ FILE: examples/ui-test/src/test/java/ui/UiRunner.java ================================================ package ui; import com.intuit.karate.http.HttpServer; import com.intuit.karate.junit5.Karate; import org.junit.jupiter.api.BeforeAll; class UiRunner { @BeforeAll public static void beforeAll() { HttpServer server = MockRunner.start(0); System.setProperty("web.url.base", "http://localhost:" + server.getPort()); } @Karate.Test Karate testUi() { return Karate.run("classpath:ui/test.feature"); } } ================================================ FILE: examples/ui-test/src/test/java/ui/html/index.html ================================================ Index

Page 01

================================================ FILE: examples/ui-test/src/test/java/ui/html/karate.js ================================================ var karate = {}; karate.get = function(id) { return document.getElementById(id) }; karate.setHtml = function(id, value) { this.get(id).innerHTML = value }; ================================================ FILE: examples/ui-test/src/test/java/ui/html/page-01.html ================================================ Page One
Before
Waiting
================================================ FILE: examples/ui-test/src/test/java/ui/test.feature ================================================ Feature: ui test Scenario Outline: * def webUrlBase = karate.properties['web.url.base'] * configure driver = { type: '#(type)', showDriverLog: true } * driver webUrlBase + '/page-01' * match text('#placeholder') == 'Before' * click('{}Click Me') * match text('#placeholder') == 'After' Examples: | type | | chrome | #| chromedriver | #| geckodriver | #| safaridriver | ================================================ FILE: examples/zip-release/src/demo/api/httpbin.feature ================================================ Feature: simple requests Scenario: simple sequence Given url 'https://httpbin.org/anything' And request { myKey: 'myValue' } When method post Then status 200 And match response contains { json: { myKey: 'myValue' } } * path response.json.myKey * method get * status 200 ================================================ FILE: examples/zip-release/src/demo/api/users.feature ================================================ Feature: sample karate api test script Background: * url 'https://jsonplaceholder.typicode.com' Scenario: get all users and then get the first user by id Given path 'users' When method get Then status 200 * def first = response[0] Given path 'users', first.id When method get Then status 200 Scenario: create a user and then get it by id * def user = """ { "name": "Test User", "username": "testuser", "email": "test@user.com", "address": { "street": "Has No Name", "suite": "Apt. 123", "city": "Electri", "zipcode": "54321-6789" } } """ Given url 'https://jsonplaceholder.typicode.com/users' And request user When method post Then status 201 * def id = response.id * print 'created id is: ', id Given path id # When method get # Then status 200 # And match response contains user ================================================ FILE: examples/zip-release/src/demo/mock/cats-mock.feature ================================================ @ignore Feature: stateful mock server Background: * configure cors = true * def uuid = function(){ return java.util.UUID.randomUUID() + '' } * def cats = {} Scenario: pathMatches('/cats') && methodIs('post') * def cat = request * def id = uuid() * cat.id = id * cats[id] = cat * def response = cat Scenario: pathMatches('/cats') * def response = $cats.* Scenario: pathMatches('/cats/{id}') * def response = cats[pathParams.id] Scenario: pathMatches('/hardcoded') * def response = { hello: 'world' } Scenario: # catch-all * def responseStatus = 404 * def responseHeaders = { 'Content-Type': 'text/html; charset=utf-8' } * def response = Not Found ================================================ FILE: examples/zip-release/src/demo/mock/cats-test.feature ================================================ Feature: integration test for the mock Background: * def port = karate.env == 'mock' ? karate.start('cats-mock.feature').port : 8080 * url 'http://localhost:' + port + '/cats' Scenario: create cat Given request { name: 'Billie' } When method post Then status 200 And match response == { id: '#uuid', name: 'Billie' } And def id = response.id Given path id When method get Then status 200 And match response == { id: '#(id)', name: 'Billie' } When method get Then status 200 And match response contains [{ id: '#(id)', name: 'Billie' }] Given request { name: 'Bob' } When method post Then status 200 And match response == { id: '#uuid', name: 'Bob' } And def id = response.id Given path id When method get Then status 200 And match response == { id: '#(id)', name: 'Bob' } When method get Then status 200 And match response contains [{ id: '#uuid', name: 'Billie' },{ id: '#(id)', name: 'Bob' }] ================================================ FILE: examples/zip-release/src/demo/mock/cats.html ================================================ Cats

================================================ FILE: examples/zip-release/src/demo/robot/calc-old.feature ================================================ Feature: windows calculator depending on your version of windows, if "calc.feature" doesn't work, try this one instead also refer docs: https://github.com/karatelabs/karate/wiki/ZIP-Release#karate-robot Scenario: * robot { window: 'Calculator', fork: 'calc', highlight: true, highlightDuration: 500 } * click('Clear') * click('1') * click('Add') * click('2') * click('Equals') * match locate('//text{3}').name == '3' * screenshot() ================================================ FILE: examples/zip-release/src/demo/robot/calc.feature ================================================ Feature: windows calculator did you know that Karate can do Windows desktop / app automation ? you are almost set - just download one more JAR file instructions here: https://github.com/karatelabs/karate/wiki/ZIP-Release#karate-robot Scenario: * robot { window: 'Calculator', fork: 'calc', highlight: true, highlightDuration: 500 } * click('Clear') * click('One') * click('Plus') * click('Two') * click('Equals') * match locate('#CalculatorResults').name == 'Display is 3' * screenshot() * click('Close Calculator') ================================================ FILE: examples/zip-release/src/demo/web/google.feature ================================================ Feature: web-browser automation Background: * configure driver = { type: 'chrome' } Scenario: try to login to github and then do a google search Given driver 'https://github.com/login' And input('#login_field', 'dummy') And input('#password', 'world') When submit().click("input[name=commit]") Then match html('.flash-error') contains 'Incorrect username or password.' Given driver 'https://google.com' And input("textarea[name=q]", 'karate dsl') When submit().click("input[name=btnI]") # this may fail depending on which part of the world you are in ! Then waitForUrl('https://github.com/karatelabs/karate') ================================================ FILE: jbang-catalog.json ================================================ { "catalogs": {}, "aliases": { "karate": { "script-ref": "io.karatelabs:karate-core:1.5.0:all" } } } ================================================ FILE: karate-archetype/pom.xml ================================================ 4.0.0 io.karatelabs karate-parent 1.5.2 karate-archetype jar org.sonatype.central central-publishing-maven-plugin ${central.publishing.maven.plugin.version} true ================================================ FILE: karate-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml ================================================ src **/** ================================================ FILE: karate-archetype/src/main/resources/archetype-resources/.gitignore ================================================ .DS_Store .vscode .idea .project .settings .classpath *.iml target/ ================================================ FILE: karate-archetype/src/main/resources/archetype-resources/pom.xml ================================================ 4.0.0 ${groupId} ${artifactId} ${version} jar UTF-8 17 3.11.0 3.0.0 1.5.2 io.karatelabs karate-junit5 ${karate.version} test src/test/java **/*.java org.apache.maven.plugins maven-compiler-plugin ${maven.compiler.version} UTF-8 ${java.version} ${java.version} org.apache.maven.plugins maven-surefire-plugin ${maven.surefire.version} -Dfile.encoding=UTF-8 ================================================ FILE: karate-archetype/src/main/resources/archetype-resources/src/test/java/examples/ExamplesTest.java ================================================ package examples; import com.intuit.karate.Results; import com.intuit.karate.Runner; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; class ExamplesTest { @Test void testParallel() { Results results = Runner.path("classpath:examples") //.outputCucumberJson(true) .parallel(5); assertEquals(0, results.getFailCount(), results.getErrorMessages()); } } ================================================ FILE: karate-archetype/src/main/resources/archetype-resources/src/test/java/examples/users/UsersRunner.java ================================================ package examples.users; import com.intuit.karate.junit5.Karate; class UsersRunner { @Karate.Test Karate testUsers() { return Karate.run("users").relativeTo(getClass()); } } ================================================ FILE: karate-archetype/src/main/resources/archetype-resources/src/test/java/examples/users/users.feature ================================================ Feature: sample karate test script for help, see: https://github.com/karatelabs/karate/wiki/IDE-Support Background: * url 'https://jsonplaceholder.typicode.com' Scenario: get all users and then get the first user by id Given path 'users' When method get Then status 200 * def first = response[0] Given path 'users', first.id When method get Then status 200 Scenario: create a user and then get it by id * def user = """ { "name": "Test User", "username": "testuser", "email": "test@user.com", "address": { "street": "Has No Name", "suite": "Apt. 123", "city": "Electri", "zipcode": "54321-6789" } } """ Given url 'https://jsonplaceholder.typicode.com/users' And request user When method post Then status 201 * def id = response.id * print 'created id is: ', id Given path id # When method get # Then status 200 # And match response contains user ================================================ FILE: karate-archetype/src/main/resources/archetype-resources/src/test/java/karate-config.js ================================================ function fn() { var env = karate.env; // get system property 'karate.env' karate.log('karate.env system property was:', env); if (!env) { env = 'dev'; } var config = { env: env, myVarName: 'someValue' } if (env == 'dev') { // customize // e.g. config.foo = 'bar'; } else if (env == 'e2e') { // customize } return config; } ================================================ FILE: karate-archetype/src/main/resources/archetype-resources/src/test/java/logback-test.xml ================================================ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n target/karate.log %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n ================================================ FILE: karate-core/README.md ================================================ # Karate UI ## UI Test Automation Made `Simple.` ### Hello World # Index
Start IDE Support | Maven Quickstart | Karate - Main Index
Config driver | configure driver | configure driverTarget | Docker / karate-chrome | Driver Types | Playwright | Playwright Legacy | timeout() | driver.sessionId
Concepts Syntax | Special Keys | Short Cuts | Chaining | Function Composition | Browser JavaScript | Looping | Drop Downs | Debugging | Retries | Waits | Proxy | Intercepting HTTP Requests | File Upload | Code Reuse | Hybrid Tests | Java API | Visual Testing
Locators Locator Types | Wildcards | Friendly Locators | Tree Walking | rightOf() | leftOf() | above() | below() | near() | Locator Lookup
Browser driver.url | driver.dimensions | refresh() | reload() | back() | forward() | maximize() | minimize() | fullscreen() | quit()
Page dialog() | switchPage() | switchFrame() | close() | driver.title | screenshot() | pdf()
Actions click() | input() | submit() | focus() | clear() | value(set) | select() | scroll() | mouse() | highlight() | highlightAll()
State html() | text() | value() | attribute() | enabled() | exists() | optional() | locate() | locateAll() | position()
Wait / JS retry() | waitFor() | waitForAny() | waitForUrl() | waitForText() | waitForEnabled() | waitForResultCount() | waitUntil() | delay() | script() | scriptAll() | Karate vs the Browser
Cookies cookie() cookie(set) | driver.cookies | deleteCookie() | clearCookies()
Chrome Java API | driver.pdf() | driver.screenshotFull() | driver.intercept() | driver.inputFile() | driver.emulateDevice() | driver.scriptAwait()
Appium Screen Recording | driver.startRecordingScreen() | driver.saveRecordingScreen() | driver.hideKeyboard()
## Capabilities * Simple, clean syntax that is well suited for people new to programming or test-automation * All-in-one framework that includes [parallel-execution](https://github.com/karatelabs/karate#parallel-execution), [HTML reports](https://github.com/karatelabs/karate#junit-html-report), [environment-switching](https://github.com/karatelabs/karate#switching-the-environment), [Visual Testing](#visual-testing), and [CI integration](https://github.com/karatelabs/karate#test-reports) * Cross-platform - with even the option to run as a programming-language *neutral* [stand-alone executable](https://github.com/karatelabs/karate/wiki/ZIP-Release) * Support for [`iframe`-s](#switchframe), [switching tabs](#switchpage), multiple URL domains, and [uploading files](#driverinputfile) * No need to learn complicated programming concepts such as "callbacks", "`async` / `await`" and "promises" * Option to use [wildcard](#wildcard-locators) and ["friendly" locators](#friendly-locators) without needing to inspect the HTML-page source, CSS, or internal XPath structure * Chrome-native automation using the [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/) (equivalent to [Puppeteer](https://pptr.dev)) * [W3C WebDriver](https://w3c.github.io/webdriver/) support built-in, which can also use [remote / grid providers](https://twitter.com/ptrthomas/status/1222790566598991873) * [Cross-Browser support](https://twitter.com/ptrthomas/status/1048260573513666560) including [Microsoft Edge on Windows](https://twitter.com/ptrthomas/status/1046459965668388866) and [Safari on Mac](https://twitter.com/ptrthomas/status/1047152170468954112) * [Playwright](https://playwright.dev) support (experimental) for even more cross-browser and headless options, that can connect to a server or Docker container using the Playwright wire-protocol * [Parallel execution on a single node](https://twitter.com/ptrthomas/status/1159295560794308609), cloud-CI environment or [Docker](#configure-drivertarget) - without needing a "master node" or "grid" * Embed [video-recordings of tests](#karate-chrome) into the HTML report from a Docker container * [experimental] [Android and iOS mobile support](https://github.com/karatelabs/karate/issues/743) via [Appium](http://appium.io) * Seamlessly mix API and UI tests within the same script, for example [sign-in using an API](https://github.com/karatelabs/karate#http-basic-authentication-example) and speed-up your tests * [Intercept HTTP requests](#intercepting-http-requests) made by the browser and re-use [Karate mocks](https://github.com/karatelabs/karate/tree/master/karate-netty) to stub / modify server responses and even replace HTML content * Use the power of Karate's [`match`](https://github.com/karatelabs/karate#prepare-mutate-assert) assertions and [core capabilities](https://github.com/karatelabs/karate#features) for UI assertions * Simple [retry](#retry) and [wait](#wait-api) strategy, no need to graduate from any test-automation university to understand the difference between "implicit waits", "explicit waits" and "fluent waits" :) * Simpler, [elegant, and *DRY* alternative](#locator-lookup) to the so-called "Page Object Model" pattern * Carefully designed [fluent-API](#chaining) to handle common combinations such as a [`submit()` + `click()`](#submit) action * Elegant syntax for typical web-automation challenges such as waiting for a [page-load](#waitforurl-instead-of-submit) or [element to appear](#waitfor) * Execute JavaScript in the browser with [one-liners](#script) - for example to [get data out of an HTML table](#scriptall) * [Compose re-usable functions](#function-composition) based on your specific environment or application needs * Comprehensive support for user-input types including [key-combinations](#special-keys) and [`mouse()`](#mouse) actions * Step-debug and even *"go back in time"* to edit and re-play steps - using the unique, innovative [Karate Extension for Visual Studio Code](https://twitter.com/KarateDSL/status/1167533484560142336) * Traceability: detailed [wire-protocol logs](https://twitter.com/ptrthomas/status/1155958170335891467) can be enabled *in-line* with test-steps in the HTML report * Convert HTML to PDF and capture the *entire* (scrollable) web-page as an image using the [Chrome Java API](#chrome-java-api) ## Comparison To understand how Karate compares to other UI automation frameworks, this article can be a good starting point: [The world needs an alternative to Selenium - *so we built one*](https://hackernoon.com/the-world-needs-an-alternative-to-selenium-so-we-built-one-zrk3j3nyr). # Examples ## Web Browser * [Example 1](../karate-demo/src/test/java/driver/demo/demo-01.feature) - simple example that navigates to GitHub and Google Search * [Example 2](../karate-demo/src/test/java/driver/demo/demo-02.feature) - simple but *very* relevant and meaty example ([see video](https://twitter.com/ptrthomas/status/1160680240781262851)) that shows how to * wait for [page-navigation](#waitforurl-instead-of-submit) * use a friendly [wildcard locator](#wildcard-locators) * wait for an element to [be ready](#waitfor) * [compose functions](#function-composition) for elegant *custom* "wait" logic * assert on tabular [results in the HTML](#scriptall) * [Example 3](../karate-e2e-tests/src/test/java/driver/00.feature) - which is a single modular script that exercises *all* capabilities of Karate Driver * a handy reference that can give you ideas on how to structure your tests * run as part of Karate's [regression suite](https://stackoverflow.com/a/66005331/143475) via GitHub Actions ## Mobile / Appium > Please consider Mobile support as experimental. But we are very close and there are some teams that use Karate for simple use-cases. Please contribute code if you can. * Refer to this [example project](../examples/mobile-test) ## Windows * [Example](../karate-demo/src/test/java/driver/windows/calc.feature) - but also see the [`karate-robot`](https://github.com/karatelabs/karate/tree/master/karate-robot) for an alternative approach. # Driver Configuration ## `configure driver` This below declares that the native (direct) Chrome integration should be used, on both Mac OS and Windows - from the default installed location. ```cucumber * configure driver = { type: 'chrome' } ``` If you want to customize the start-up, you can use a batch-file: ```cucumber * configure driver = { type: 'chrome', executable: 'chrome' } ``` Here a batch-file called `chrome` can be placed in the system [`PATH`](https://www.java.com/en/download/help/path.xml) (and made executable) with the following contents: ```bash "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" $* ``` For Windows it would be `chrome.bat` in the system [`PATH`](https://www.java.com/en/download/help/path.xml) as follows: ```bat "C:\Program Files (x86)\Google\Chrome\Application\chrome" %* ``` Another example for WebDriver, again assuming that `chromedriver` is in the [`PATH`](https://www.java.com/en/download/help/path.xml): ```cucumber { type: 'chromedriver', port: 9515, executable: 'chromedriver' } ``` key | description --- | ----------- `type` | see [driver types](#driver-types) `executable` | if present, Karate will attempt to invoke this, if not in the system [`PATH`](https://www.java.com/en/download/help/path.xml), you can use a full-path instead of just the name of the executable. batch files should also work `start` | default `true`, Karate will attempt to start the `executable` - and if the `executable` is not defined, Karate will even try to assume the default for the OS in use `stop` | optional, defaults to `true` *very* rarely needed, only in cases where you want the browser to remain open after your tests have completed, typically when you write a custom [`Target`](#custom-target) `port` | optional, and Karate would choose the "traditional" port for the given `type` `host` | optional, will default to `localhost` and you normally never need to change this `pollAttempts` | optional, will default to `20`, you normally never need to change this (and changing `pollInterval` is preferred), and this is the number of attempts Karate will make to wait for the `port` to be ready and accepting connections before proceeding `pollInterval` | optional, will default to `250` (milliseconds) and you normally never need to change this (see `pollAttempts`) unless the driver `executable` takes a *very* long time to start `headless` | [headless mode](https://developers.google.com/web/updates/2017/04/headless-chrome) only applies to `{ type: 'chrome' }` for now, also see [`DockerTarget`](#dockertarget) and [`webDriverSession`](#webdriversession) `showDriverLog` | default `false`, will include webdriver HTTP traffic in Karate report, useful for troubleshooting or bug reports `showProcessLog` | default `false`, will include even executable (webdriver or browser) logs in the Karate report `showBrowserLog` | default `true`, only applies to `{ type: 'chrome' }` which shows the browser console log in the HTML report for convenience `addOptions` | default `null`, has to be a list / JSON array that will be appended as additional CLI arguments to the `executable`, e.g. `['--no-sandbox', '--windows-size=1920,1080']` `beforeStart` | default `null`, an OS command that will be executed before commencing a `Scenario` (and before the `executable` is invoked if applicable) typically used to start video-recording `afterStop` | default `null`, an OS command that will be executed after a `Scenario` completes, typically used to stop video-recording and save the video file to an output folder `videoFile` | default `null`, the path to the video file that will be added to the end of the test report, if it does not exist, it will be ignored `httpConfig` | optional, and typically only used for remote WebDriver usage where the HTTP client [configuration](https://github.com/karatelabs/karate#configure) needs to be tweaked, e.g. `{ readTimeout: 120000 }` (also see `timeout` below) `timeout` | default `30000`, amount of time (in milliseconds) that type `chrome` will wait for an operation that takes time (typically navigating to a new page) - and as a convenience for WebDriver, this will be equivalent to setting the `readTimeout` for the `httpConfig` (see above) - also see [`timeout()`](#timeout) `playwrightUrl` | only applies for `{ type: 'playwright', start: false }`, the [Playwright](https://playwright.dev) wire-protocol (websockets) server URL, also see [`playwrightOptions`](#playwrightoptions) `playwrightOptions` | optional, see [`playwrightOptions`](#playwrightoptions) `webDriverUrl` | see [`webDriverUrl`](#webdriverurl) `webDriverSession` | see [`webDriverSession`](#webdriversession) `webDriverPath` | optional, and rarely used only in case you need to append a path such as `/wd/hub` - typically needed for Appium (or a Selenium Grid) on `localhost`, where `host`, `port` / `executable` etc. are involved. `highlight` | default `false`, useful for demos or for running a test in "slow motion" where before each navigation action, the HTML element for the current [locator](#locators) is highlighted for a duration of `highlightDuration` `highlightDuration` | default 3000 (milliseconds), duration to apply the `highlight` `attach` | optional, only for `type: 'chrome'` and `start: false` when you want to attach to an existing page in a Chrome DevTools session, uses a "contains" match against the URL `userDataDir` | optional, by default Karate will auto-create a [user dir](https://chromium.googlesource.com/chromium/src.git/+/master/docs/user_data_dir.md) for Chrome and other browsers, but if you want to provide the path to an existing folder (which can reduce disk space usage in some situations), note that for Chrome, this will pass the command line option `--user-data-dir`. if `null`, Chrome will use the system defaults (the `--user-data-dir` command-line option will *not* be passed) For more advanced options such as for Docker, CI, headless, cloud-environments or custom needs, see [`configure driverTarget`](#configure-drivertarget). ## webDriverUrl Karate implements the [W3C WebDriver spec](https://w3c.github.io/webdriver), which means that you can point Karate to a remote "grid" such as [Zalenium](https://opensource.zalando.com/zalenium/) or a SaaS provider such as [the AWS Device Farm](https://docs.aws.amazon.com/devicefarm/latest/testgrid/what-is-testgrid.html). The `webDriverUrl` driver configuration key is optional, but if specified, will be used as the W3C WebDriver remote server. Note that you typically would set `start: false` as well, or use a [Custom `Target`](#custom-target). For example, once you run the [couple of Docker commands](https://opensource.zalando.com/zalenium/#try-it) to get Zalenium running, you can do this: ```cucumber * configure driver = { type: 'chromedriver', start: false, webDriverUrl: 'http://localhost:4444/wd/hub' } ``` Note that you can add `showDriverLog: true` to the above for troubleshooting if needed. You should be able to [run tests in parallel](https://github.com/karatelabs/karate#parallel-execution) with ease ! ## `webDriverSession` When targeting a W3C WebDriver implementation, either as a local executable or [Remote WebDriver](https://selenium.dev/documentation/en/remote_webdriver/remote_webdriver_client/), you can specify the JSON that will be passed as the payload to the [Create Session](https://w3c.github.io/webdriver/#new-session) API. The most important part of this payload is the [`capabilities`](https://w3c.github.io/webdriver/#capabilities). It will default to `{ browserName: '' }` for convenience where `` will be `chrome`, `firefox` etc. So most of the time this would be sufficient: ```cucumber * configure driver = { type: 'chromedriver' } ``` Since it will result in the following request to the WebDriver `/session`: ```js {"capabilities":{"alwaysMatch":{"browserName":"chrome"}}} ``` But in some cases, especially when you need to talk to remote driver instances, you need to pass specific "shapes" of JSON expected by the particular implementation - or you may need to pass custom data or "extension" properties. Use the `webDriverSession` property in those cases. > Note: `desiredCapabilities` has been deprecated and not recommended for use. For example: ```cucumber * def session = { capabilities: { alwaysMatch: { browserName: 'chrome', 'goog:chromeOptions': { args: [ '--headless', 'window-size=1280,720' ] } } } } * configure driver = { type: 'chromedriver', webDriverSession: '#(session)', start: false, webDriverUrl: 'http://localhost:9515/wd/hub' } ``` Another example is that for the new Microsoft Edge browser (based on Chromium), the Karate default `alwaysMatch` is not supported, so this is what works: ```cucumber * configure driver = { type: 'msedgedriver', webDriverSession: { capabilities: { browserName: 'edge' } } } ``` Here are some of the things that you can customize, but note that these depend on the driver implementation. * [`proxy`](#proxy) * [`acceptInsecureCerts`](https://w3c.github.io/webdriver/#dfn-insecure-tls-certificates) * [`moz:firefoxOptions`](https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities/firefoxOptions#firefoxOptions) - e.g. for headless FireFox Note that some capabilities such as "headless" may be possible via the command-line to the local executable, so using [`addOptions`](#configure-driver) may work instead. Also see [`driver.sessionId`](#driversessionid). ### Playwright Driver that leverages [Playwright](https://playwright.dev) java native APIs. To use it, add the following dependency: ```xml io.karate karate-playwright test ``` And make sure it is declared before `io.karate:karate-core`. A server will be automatically started and made available to Karate without any extra-script. If you have one pre-started, you can still use the [`playwrightUrl`](#configure-driver) driver config. ### `playwrightOptions` When using [Playwright](#playwright) you can omit this in which case Karate will default to Chrome (within Playwright) and the default browser window size. This can take the following keys: * `browserType` - defaults to `chromium`, can be set to the other [types that Playwright supports](https://playwright.dev/docs/core-concepts#browser), e.g. `firefox` and `webkit` * `context` - JSON which will be passed as the argument of the Playwright [`browser.newContext()`](https://playwright.dev/docs/api/class-browser#browsernewcontextoptions) call, needed typically to set the page dimensions * `channel` - defaults to chrome, for the `chromium` browserType, allows to specify which [flavor](https://playwright.dev/docs/browsers#google-chrome--microsoft-edge) to use * `installBrowsers` - defaults to `true`, whether or not all the supported browsers will be downloaded and installed by Playwright (once). Note that there is a top-level config flag for `headless` mode. The default is: `* configure driver = { headless: false }` ### Playwright Legacy To use [Playwright](https://playwright.dev), you need to start a Playwright server. If you have one pre-started, you need to use the [`playwrightUrl`](#configure-driver) driver config. Or you can set up an executable that can do it and log the URL to the console when the server is ready. The websocket URL will look like this: `ws://127.0.0.1:4444/0e0bd1c0bb2d4eb550d02c91046dd6e0`. Here's a simple recipe to set up this mechanism on your local machine. NodeJS is a pre-requisite and you can choose a folder (e.g. `playwright`) for the "start scripts" to live. Within that folder, [you can run](https://playwright.dev/docs/intro#installation): ``` npm i -D playwright ``` Now create a file called `playwright/server.js` with the following code: ```js const playwright = require('playwright'); const port = process.argv[2] || 4444; const browserType = process.argv[3] || 'chromium'; const headless = process.argv[4] == 'true'; console.log('using port:', port, 'browser:', browserType, 'headless:', headless); const serverPromise = playwright[browserType].launchServer({ headless: headless, port: port }); serverPromise.then(bs => console.log(bs.wsEndpoint())); ``` The main thing here is that the server URL should be logged to the console when it starts. Karate will scan the log for any string that starts with `ws://` and kick things off from there. Also Karate will call the executable with three arguments in this order: * `port` * `browserType` * `headless` So this is how you can communicate your cross-browser config from your Karate test to the executable. The final piece of the puzzle is to set up a batch file to start the server: ```bash #!/bin/bash exec node /some/path/playwright/server.js $* ``` The [`exec`](http://veithen.io/2014/11/16/sigterm-propagation.html) is important here so that Karate can stop the `node` process cleanly. Now you can use the path of the batch file in the driver `executable` config. ```cucumber * configure driver = { type: 'playwright', executable: 'path/to/start-server' } ``` For convenience, Karate assumes by default that the executable name is `playwright` and that it exists in the System [`PATH`](https://www.java.com/en/download/help/path.xml). Make sure that the batch file is made executable depending on your OS. Based on the above details, you should be able to come up with a custom strategy to connect Karate to Playwright. And you can consider a [`driverTarget`](#custom-target) approach for complex needs such as using a Docker container for CI. ## `configure driverTarget` The [`configure driver`](#configure-driver) options are fine for testing on "`localhost`" and when not in `headless` mode. But when the time comes for running your web-UI automation tests on a continuous integration server, things get interesting. To support all the various options such as Docker, headless Chrome, cloud-providers etc., Karate introduces the concept of a pluggable [`Target`](src/main/java/com/intuit/karate/driver/Target.java) where you just have to implement two methods: ```java public interface Target { Map start(com.intuit.karate.core.ScenarioRuntime sr); Map stop(com.intuit.karate.core.ScenarioRuntime sr); } ``` * `start()`: The `Map` returned will be used as the generated [driver configuration](#driver-configuration). And the `start()` method will be invoked as soon as any `Scenario` requests for a web-browser instance (for the first time) via the [`driver`](#driver) keyword. * `stop()`: Karate will call this method at the end of every top-level `Scenario` (that has not been `call`-ed by another `Scenario`). If you use the provided `ScenarioRuntime.logger` instance in your `Target` code, any logging you perform will nicely appear in-line with test-steps in the HTML report, which is great for troubleshooting or debugging tests. Combined with Docker, headless Chrome and Karate's [parallel-execution capabilities](https://github.com/karatelabs/karate#parallel-execution) - this simple `start()` and `stop()` lifecycle can effectively run web UI automation tests in parallel on a single node. ### `DockerTarget` Karate has a built-in implementation for Docker ([`DockerTarget`](src/main/java/com/intuit/karate/driver/DockerTarget.java)) that supports 2 existing Docker images out of the box: * [`justinribeiro/chrome-headless`](https://hub.docker.com/r/justinribeiro/chrome-headless/) - for Chrome "native" in [headless mode](https://developers.google.com/web/updates/2017/04/headless-chrome) * [`karatelabs/karate-chrome`](#karate-chrome) - for Chrome "native" but with an option to connect to the container and view via VNC, and with video-recording To use either of the above, you do this in a Karate test: ```cucumber * configure driverTarget = { docker: 'justinribeiro/chrome-headless', showDriverLog: true } ``` Or for more flexibility, you could do this in [`karate-config.js`](https://github.com/karatelabs/karate#configuration) and perform conditional logic based on [`karate.env`](https://github.com/karatelabs/karate#switching-the-environment). One very convenient aspect of `configure driverTarget` is that *if* in-scope, it will over-ride any `configure driver` directives that exist. This means that you can have the below snippet activate *only* for your CI build, and you can leave your feature files set to point to what you would use in "dev-local" mode. ```javascript function fn() { var config = { baseUrl: 'https://qa.mycompany.com' }; if (karate.env == 'ci') { karate.configure('driverTarget', { docker: 'karatelabs/karate-chrome' }); } return config; } ``` To use the [recommended `--security-opt seccomp=chrome.json` Docker option](https://hub.docker.com/r/justinribeiro/chrome-headless/), add a `secComp` property to the `driverTarget` configuration. And if you need to view the container display via VNC, set the `vncPort` to map the port exposed by Docker. ```javascript karate.configure('driverTarget', { docker: 'karatelabs/karate-chrome', secComp: 'src/test/java/chrome.json', vncPort: 5900 }); ``` ### Custom `Target` If you have a custom implementation of a `Target`, you can easily [construct any custom Java class](https://github.com/karatelabs/karate#calling-java) and pass it to `configure driverTarget`. Here below is the equivalent of the above, done the "hard way": ```javascript var DockerTarget = Java.type('com.intuit.karate.driver.DockerTarget'); var options = { showDriverLog: true }; var target = new DockerTarget(options); target.command = function(port){ return 'docker run -d -p ' + port + ':9222 --security-opt seccomp=./chrome.json justinribeiro/chrome-headless' }; karate.configure('driverTarget', target); ``` The built-in [`DockerTarget`](src/main/java/com/intuit/karate/driver/DockerTarget.java) is a good example of how to: * perform any pre-test set-up actions * provision a free port and use it to shape the `start()` command dynamically * execute the command to start the target process * perform an HTTP health check to wait until we are ready to receive connections * and when `stop()` is called, indicate if a video recording is present (after retrieving it from the stopped container) Controlling this flow from Java can take a lot of complexity out your build pipeline and keep things cross-platform. And you don't need to line-up an assortment of shell-scripts to do all these things. You can potentially include the steps of deploying (and un-deploying) the application-under-test using this approach - but probably the top-level [JUnit test-suite](https://github.com/karatelabs/karate#parallel-execution) would be the right place for those. If the machine where you are running Karate is not the same as your target host (e.g. a sibling Docker container or a Chrome browser in a different machine) you might need to configure `DockerTarget` with the `remoteHost` and/or `useDockerHost` properties. The `DockerTarget` implementation has an example [and you can find more details here](https://github.com/karatelabs/karate/pull/1603#issuecomment-846420716). Another (simple) example of a custom `Target` you can use as a reference is this one: [`karate-devicefarm-demo`](https://github.com/ptrthomas/karate-devicefarm-demo) - which demonstrates how Karate can be used to drive tests on [AWS DeviceFarm](https://docs.aws.amazon.com/devicefarm/latest/testgrid/what-is-testgrid.html). The same approach should apply to any Selenium "grid" provider such as [Zalenium](https://opensource.zalando.com/zalenium/). ### `karate-chrome` The [`karate-chrome`](https://hub.docker.com/r/karatelabs/karate-chrome) Docker is an image created from scratch, using a Java / Maven image as a base and with the following features: * Chrome in "full" mode (non-headless) * [Chrome DevTools protocol](https://chromedevtools.github.io/devtools-protocol/) exposed on port 9222 * VNC server exposed on port 5900 so that you can watch the browser in real-time * a video of the entire test is saved to `/tmp/karate.mp4` * after the test, when `stop()` is called, the [`DockerTarget`](src/main/java/com/intuit/karate/driver/DockerTarget.java) will embed the video into the HTML report (expand the last step in the `Scenario` to view) To try this or especially when you need to investigate why a test is not behaving properly when running within Docker, these are the steps: * start the container: * `docker run --name karate --rm -p 9222:9222 -p 5900:5900 -e KARATE_SOCAT_START=true --cap-add=SYS_ADMIN karatelabs/karate-chrome` * it is recommended to use [`--security-opt seccomp=chrome.json`](https://hub.docker.com/r/justinribeiro/chrome-headless/) instead of `--cap-add=SYS_ADMIN` * point your VNC client to `localhost:5900` (password: `karate`) * for example on a Mac you can use this command: `open vnc://localhost:5900` * run a test using the following [`driver` configuration](#configure-driver), and this is one of the few times you would ever need to set the [`start` flag](#configure-driver) to `false` * `* configure driver = { type: 'chrome', start: false, showDriverLog: true }` * you can even use the [Karate VS Code extension](https://github.com/karatelabs/karate/wiki/IDE-Support#vs-code-karate-plugin) to debug and step-through a test * if you omit the `--rm` part in the start command, after stopping the container, you can dump the logs and video recording using this command (here `.` stands for the current working folder, change it if needed): * `docker cp karate:/tmp .` * this would include the `stderr` and `stdout` logs from Chrome, which can be helpful for troubleshooting For more information on the Docker containers for Karate and how to use them, refer to the wiki: [Docker](https://github.com/karatelabs/karate/wiki/Docker). ## Driver Types The recommendation is that you prefer `chrome` for development, and once you have the tests running smoothly - you can switch to a different WebDriver implementation. type | default port | default executable | description ---- | ------------ | ------------------ | ----------- [`chrome`](https://chromedevtools.github.io/devtools-protocol/) | 9222 | mac: `/Applications/Google Chrome.app/Contents/MacOS/Google Chrome`
win: `C:/Program Files (x86)/Google/Chrome/Application/chrome.exe` | "native" Chrome automation via the [DevTools protocol](https://chromedevtools.github.io/devtools-protocol/) [`playwright`](https://playwright.dev) | 4444 | `playwright` | see [`playwrightOptions`](#playwrightoptions) and [Playwright](#playwright) [`msedge`](https://docs.microsoft.com/en-us/microsoft-edge/) | 9222 | mac: `/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge`
win: `C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe` | the new Chromium based Microsoft Edge, using the [DevTools protocol](https://docs.microsoft.com/en-us/microsoft-edge/devtools-protocol-chromium) [`chromedriver`](https://sites.google.com/a/chromium.org/chromedriver/home) | 9515 | `chromedriver` | W3C Chrome Driver [`geckodriver`](https://github.com/mozilla/geckodriver) | 4444 | `geckodriver` | W3C Gecko Driver (Firefox) [`safaridriver`](https://webkit.org/blog/6900/webdriver-support-in-safari-10/) | 5555 | `safaridriver` | W3C Safari Driver [`msedgedriver`](https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/) | 9515 | `msedgedriver` | W3C Microsoft Edge WebDriver (the new one based on Chromium), also see [`webDriverSession`](#webdriversession) [`mswebdriver`](https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/) | 17556 | `MicrosoftWebDriver` | Microsoft Edge "Legacy" WebDriver [`iedriver`](https://github.com/SeleniumHQ/selenium/wiki/InternetExplorerDriver) | 5555 | `IEDriverServer` | IE (11 only) Driver [`winappdriver`](https://github.com/Microsoft/WinAppDriver) | 4727 | `C:/Program Files (x86)/Windows Application Driver/WinAppDriver` | Windows Desktop automation, similar to Appium [`android`](https://github.com/appium/appium/) | 4723 | `appium` | android automation via [Appium](https://github.com/appium/appium/) [`ios`](https://github.com/appium/appium/) | 4723 |`appium` | iOS automation via [Appium](https://github.com/appium/appium/) # Distributed Testing Karate can split a test-suite across multiple machines or Docker containers for execution and aggregate the results. Please refer to the wiki: [Distributed Testing](https://github.com/karatelabs/karate/wiki/Distributed-Testing). # Locators The standard locator syntax is supported. For example for web-automation, a `/` prefix means XPath and else it would be evaluated as a "CSS selector". ```cucumber And input('input[name=someName]', 'test input') When submit().click("//input[@name='commit']") ``` platform | prefix | means | example ----- | ------ | ----- | ------- web | (none) | css selector | `input[name=someName]` web
android
ios | `/` | xpath | `//input[@name='commit']` web | `{}` | [exact text content](#wildcard-locators) | `{a}Click Me` web | `{^}` | [partial text content](#wildcard-locators) | `{^a}Click Me` win
android
ios| (none) | name | `Submit` win
android
ios | `@` | accessibility id | `@CalculatorResults` win
android
ios | `#` | id | `#MyButton` ios| `:` | -ios predicate string | `:name == 'OK' type == XCUIElementTypeButton` ios| `^` | -ios class chain | `^**/XCUIElementTypeTable[name == 'dataTable']` android| `-` | -android uiautomator | `-input[name=someName]` ## Wildcard Locators The "`{}`" and "`{^}`" locator-prefixes are designed to make finding an HTML element by *text content* super-easy. You will typically also match against a specific HTML tag (which is preferred, and faster at run-time). But even if you use "`{*}`" (or "`{}`" which is the equivalent short-cut) to match *any* tag, you are selecting based on what the user *sees on the page*. When you use CSS and XPath, you need to understand the internal CSS class-names and XPath structure of the page. But when you use the visible text-content, for example the text within a `