Exception in tearDownDuskClass - laravel-dusk

Environment:
Dusk 5.0.2, Laravel 5.7.28, PHP 7.2.14, PHPUnit 7.5.8, MySQL 5.7, Laravel Homestead, ChromeDriver 2.45.615279
Problems:
№1
Exception in Tests\Browser\::tearDownDuskClass
Curl error thrown for http DELETE to /session/e76b9bfe8c9f9af519c2901601531c6a
Operation timed out after 30000 milliseconds with 0 bytes received
№2
Facebook\WebDriver\Exception\WebDriverCurlException: Curl error thrown
for http POST to /session/ce9d54d51ec982ff77aa5ca202159ca4/log with
params: {"type":"browser"}
Operation timed out after 30001 milliseconds with 0 bytes received
Caused by Facebook\WebDriver\Exception\WebDriverCurlException: Curl
error thrown for http GET to
/session/ce9d54d51ec982ff77aa5ca202159ca4/screenshot
Operation timed out after 30001 milliseconds with 0 bytes received
Test class:
class MyTest extends DuskTestCase
{
use DatabaseMigrations, WithFaker;
public function testExample()
{
$user = \factory(User::class)->create();
$this->browse(function (Browser $browser) use ($user) {
$browser
->loginAs($user)
->visitRoute(/**/)
->waitForText(/**/)
->type(/**/)
->type(/**/)
->type(/**/)
->attach(/**/, /**/)
->press(/**/)
->waitForText(/**/)
->assertSeeIn(/**/)
->click(/**/)
->assertSeeIn(/**/)
->click(/**/); //new tab is opened
$windows = collect($browser->driver->getWindowHandles());
//navigate to new tab
$browser->driver->switchTo()->window($windows->last());
$browser->assertPathIs(/**/);
});
}
}
Exceptions are thrown randomly. Usually the very first test after I up my VM is succeed.
Update: temporarily solved using
$window = $browser->driver->getWindowHandles()[1];
$browser->driver->switchTo()->window($window);
Update 2: even this code at the end of the test cause the error
$browser->driver->switchTo()->window($browser->driver->getWindowHandles()[1]);
$browser->screenshot('scr');
$browser->driver->switchTo()->window($browser->driver->getWindowHandles()[0]);

Related

[Karate Test][Saucelab]: Session disconnecting after launching mobile app installed in Saucelab

I am launching android test on Saucelab, however I can see the script is launching the mobile app on saucelab but not able to perform any action on it and throwing this exception:
12:54:20.931 [main] ERROR com.intuit.karate - java.net.SocketTimeoutException: Read timed out, http call failed after 32303 milliseconds for url: https://oauth-abdulkadir786-684f1:1bd00f8e-392f-4b4c-8f0e-2597cc9912a3#ondemand.eu-central-1.saucelabs.com:443/wd/hub/session
I am using the following steps for execute my test:
Configure driver in karate-config.js
var android = {}
android["desiredConfig"] = {
"accessKey":"1bd00f8e-392f-4b4c-8f0e-2597cc9912a3",
"deviceName":"Android GoogleAPI Emulator",
"app" : "storage:b82d6099-60ad-49dd-b15f-925166e03dcd",
"platformVersion" : "11.0",
"platformName" : "Android",
"newCommandTimeout":300,
"automationName" : "UiAutomator2",
"username": "oauth-abdulkadir786-684f1"
}
config["android"] = android
Then Writing the feature file to call the webDriverSession:
Feature: Calling test from Sauce labs
Background:
* configure driver = { type: 'android', start: false, webDriverUrl: 'https://oauth-abdulkadir786-684f1:1bd00f8e-392f-4b4c-8f0e-2597cc9912a3#ondemand.eu-central-1.saucelabs.com:443/wd/hub' }
Scenario: android mobile app UI tests
Given driver { webDriverSession: { desiredCapabilities : "#(android.desiredConfig)"} }
* delay(2000)
Then click('/hierarchy/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.ScrollView/android.widget.LinearLayout/android.widget.LinearLayout/android.widget.LinearLayout[2]/android.widget.Button[3]')
Any help would be highly appreciated.....
It looks like Karate is timing out the session before the emulator and app gets a chance to load fully:
Read timed out, http call failed after 32303 milliseconds for url: https://oauth-abdulkadir786-684f1:1bd00f8e-392f-4b4c-8f0e-2597cc9912a3#ondemand.eu-central-1.saucelabs.com:443/wd/hub/session
The suspicious parts are Read timed out, 32303 milliseconds (which is pretty close to 30 seconds and so is probably a config thing; 30 or 60 seconds is a common default timeout) and the path /wd/hub/session looks like the initial POST request which starts a sesson.
You'll probably have more luck if you increase the connectTimeout and readTimeout, either in your Background or in karate-config.js:
karate.configure('connectTimeout', 60000);
karate.configure('readTimeout', 60000);

Cypress failing randomly during CI testing but passing locally

Hey I've created a test that is passing locally wihtout a problem but failing 50% of the time during CI tests.
it("Check if scroll is working on list", { retries: 3 }, () => {
cy.server()
cy.intercept('GET','**/api/**', {
statusCode: 200
}).as('loadMoreAbstracts')
cy.get('[data-cy=VirtualScroll]', { timeout: 15000 }).scrollTo("bottom").then(() => {
cy.wait('#loadMoreAbstracts', { timeout: 15000 })
cy.get('[data-cy=VirtualScroll]').invoke('scrollTop').should('be.gt', 0)
})
cy.get('[data-cy=VirtualScroll]', { timeout: 15000 }).scrollTo("bottom")
})
Could anybody tell me what can be wrong with this test and why its working locally but failing most of the time on CI? Other tests that require listening to request are 100% passing on CI, only this one has problem somehow but I cant figure it out. Locally it never fails.
Error I am getting:
Check if scroll is working on list:
CypressError: Timed out retrying: `cy.wait()` timed out waiting `15000ms` for the 1st request to the route: `loadMoreAbstracts`. No request ever occurred.

Junit assertions don't work when called inside a Javalin handler

I have this test that launches a service at port 7000 and inside the only endpoint I do a failing assertion:
#Test
fun `javalin assertion should fail`() {
Javalin.create()
.get("/") { assertTrue(false) }
.start()
newHttpClient().send(
HttpRequest.newBuilder()
.uri(URI.create("http://localhost:7000/"))
.GET().build(),
discarding()
)
}
The problem is that the test always passes (but it should fail):
(same behavior happens by running ./gradlew test)
... even though there's a console output claiming that a test failed:
[Test worker] INFO io.javalin.Javalin - Listening on http://localhost:7000/
[Test worker] INFO io.javalin.Javalin - Javalin started in 356ms \o/
[qtp2100106358-22] ERROR io.javalin.Javalin - Exception occurred while servicing http-request
org.opentest4j.AssertionFailedError: expected: <true> but was: <false>
at org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:55)
..
Probably, it's being run in another thread, but I wonder if there's a way to attach it to the same context.
(Weirdly, in another scenario in my app - that I couldn't isolate - it properly fails.)
TLDR
To make the test fail as you'd expect, add an assertion on the response you get from your Javalin instance.
#Test
fun `javalin assertion should fail`() {
Javalin.create()
.get("/") { assertTrue(false) } // or any expression that throws an Exception, like Kotlin's TODO()
.start()
val javalinResponse: HttpResponse<Void> = newHttpClient().send(
HttpRequest.newBuilder()
.uri(URI.create("http://localhost:7000/"))
.GET().build(),
discarding()
)
assertThat(javalinResponse.statusCode()).isEqualTo(200) // will fail with Expected: 200, Actual: 500
}
Details
There're two distinct steps in the test: new Javalin instance configuration + build, and calling that instance with a HttpClient.
In Javalin configuration + build step it gets instructed to do assertTrue(false) when called on the / endpoint. assertTrue(false) will throw an AssertionFailedError, but the behavior will be the same if you throw something else there. Now, as many (all?) other webservers, Javalin / Jetty will try to catch any uncaught exceptions that happen within it and return a HTTP response with code 500 (Internal Server Error).
Indeed, this all happens in another thread, as internally a Jetty webserver instance is being launched, which does the port listening, HTTP request / response handling and other important stuff.
So when later in the test an HTTP call is performed to the new Javalin instance, it gets the 500 (Internal Server Error) response successfully, and as originally there are no assertions on the response and there were no uncaught exceptions, the test is deemed successful.
Never do assertions inside the Javalin handler, because if the test fails, the JUnit exception is swallowed by Javalin and the test fails silently (better explained in the other answer). The solution is to make an assertion outside, in the end, as in the Arrange, Act, Assert pattern.
How? You store what you want to assert inside the handler and assert it later. For example, if it's a POST.
var postedBody: String? = null
fakeProfileApi = Javalin.create().post("profile") {
postedBody = it.body()
}.start(1234)
val profileGateway = ProfileGateway(apiUrl = "http://localhost:1234")
profileGateway.saveProfile( // contains the HTTP POST
Profile(id = "abc", email = "john.doe#gmail.com".toEmail())
)
JSONAssert.assertEquals(
""" { "id": "abc", "email": "johndoe#gmail.com" } """,
postedBody, true
)
If it's a GET, it's easier:
fakeProfileApi = Javalin.create().get("profile/abc") {
it.result(""" {"id": "abc", "email": "johndoe#gmail.com"} """)
}.start(1234)
val profileGateway = ProfileGateway(apiUrl = "http://localhost:1234")
val result = profileGateway.fetchProfile("abc") // contains the HTTP GET
assertEquals(
Profile(id = "abc", email = "john.doe#gmail.com".toEmail()),
result
)
More info: Unit testing a gateway with Javalin

WebSocket is not in the OPEN state

I'm trying SignalR on ASP.NET Core. It works fine running from VisaulStudio debugger.
However it does not work in deployed code, showing the error message "WebSocket is not in the OPEN state" and "Handshake was canceled". What is the possible cause of the problem?
Microsoft.AspNetCore.Mvc 2.2.0
Microsoft.AspNetCore.SignalR 1.1.0
#aspnet/signalr 1.1.2
Bootstrap4
jQuery v3.1.0
Kestrel
No HTTPS SSL
Tried with Windows 7 and Ubuntu 18.4
Network Console on Google Chrome
WebSocket is not in the OPEN state (kms-event-exit.js:12)
Uncaught Error: Seerver returned handshake error: Handshake was canceled. (signalr.min.js:16)
at HubConnection.processHandshakeResponse (signalr.min.js:16)
at HubConnection.processIncomingData (signalr.min.js:16)
at WebSocketTransport.HubConnection.connection.onreceive (signalr.min.js:16)
at WebSocket.webSocket.onmessag (signalr.min.js:16)
[2019-04-06T01:06:41.965Z] Error: Connection disconnected with error 'Error: Server returned handshake error: Handshake was canceled.'. signalr.min.js:16
Uncaught (in promise) Server returned handshake error: Handshake was canceled. (signalr.min.js:16)
Startup functions.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.AddRazorOptions(options => options.AllowRecompilingViewsOnFileChange = true);
services.AddSignalR(options => options.EnableDetailedErrors = true);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseDeveloperExceptionPage();
if (!env.IsDevelopment())
{
//app.UseExceptionHandler("/Home/Main");
app.UseHsts();
}
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseSignalR(routes =>
{
routes.MapHub<Hubs.KmsHub>("/KmsHub");
routes.MapHub<Hubs.AllResetHub>("/AllResetHub");
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "areaDefault",
template: "{area:exists}/{controller=Home}/{action=Main}/{id?}");
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Main}/{id?}/{exit?}");
});
}
Logs
dbug: Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionDispatcher[4]
Establishing new connection.
dbug: Microsoft.AspNetCore.SignalR.HubConnectionHandler[5]
OnConnectedAsync started.
dbug: Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport[1]
Socket opened using Sub-Protocol: '(null)'.
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 GET http://localhost:5000/favicon.ico
info: Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware[2]
Sending file. Request path: '/favicon.ico'. Physical path: 'D:\K4\KMS\KMS\bin\Release\netcoreapp2.2\publish\wwwroot\favicon.ico'
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 6.664ms 200 image/x-icon
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 GET http://localhost:5000/lib/Popper/popper.min.js.map
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 3.4573ms 404
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 GET http://localhost:5000/lib/bootstrap/dist/js/bootstrap.min.js.map
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 GET http://localhost:5000/lib/signalr/dist/browser/signalr.min.js.map
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 2.3443ms 404
info: Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware[2]
Sending file. Request path: '/lib/bootstrap/dist/js/bootstrap.min.js.map'. Physical path: 'D:\K4\KMS\KMS\bin\Release\netcoreapp2.2\publish\wwwroot\lib\boo
tstrap\dist\js\bootstrap.min.js.map'
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 114.858ms 200 text/plain
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 GET http://localhost:5000/lib/bootstrap/dist/css/bootstrap.min.css.map
info: Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware[2]
Sending file. Request path: '/lib/bootstrap/dist/css/bootstrap.min.css.map'. Physical path: 'D:\K4\KMS\KMS\bin\Release\netcoreapp2.2\publish\wwwroot\lib\b
ootstrap\dist\css\bootstrap.min.css.map'
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 18.0356ms 200 text/plain
dbug: Microsoft.AspNetCore.SignalR.HubConnectionContext[2]
Handshake was canceled.
dbug: Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport[7]
Waiting for the client to close the socket.
dbug: Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport[2]
Socket closed.
dbug: Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionManager[2]
Removing connection 8K2CDgDs6jWXM7DPMWk_Dg from the list of connections.
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 15047.5227ms 101
JavaScript code which caught the exception.
function kmsEventExit(url) {
var exitButton = document.getElementById("exitButton");
var connection = new signalR.HubConnectionBuilder().withUrl(url + "/KmsHub").build();
//Disable send button until connection is established
exitButton.disabled = true;
//Wait until connection finishes.
connection.start().then(function () {
exitButton.disabled = false;
}).catch(function (err) {
return console.error(err.toString()); //WebSocket is not in the OPEN state
});
//Call ExitKms on clicking the button.
exitButton.addEventListener("click", function (event) {
connection.invoke("ExitKms").catch(function (err) {
return console.error(err.toString());
});
event.preventDefault();
});
//Catch the result.
connection.on("ExitKmsResult", function (isAlert, options) {
if (isAlert) {
swal(JSON.parse(options));
}
});
}
There has been an workaround posted in a GitHub Issue that has worked for me.
Before connecting, add:
Object.defineProperty(WebSocket, 'OPEN', { value: 1, });
Using this method, there's no need to remove pace.js.
Important note: if you are using Blazor Server, you will face the same issue. Remove pace.js then, as it's quite useless in this case.
I have identified the problem!
It did not work because pace.js was not compatible with signalr.js. WebSocket variable was duplicated in these two plugins. SignalR works fine after removing pace.js.
SignalR on VisualStudio worked with pace.js because it uses SSE and IIS Express, instead of WebSocket and Kestrel, thus pace.js and signalr.js can be compatible with this particular configuration.
Reference:
https://github.com/aspnet/SignalR/issues/2389
As Identified above, pace and signalr are not directly compatible, however putting the script below before loading pace.js will resolve the problem instead of outrightly remove pace.js
<script>
window.paceOptions = { ajax: { ignoreURLs: ['mainHub', '__browserLink', 'browserLinkSignalR'], trackWebSockets: false } }
</script>
Reference to above answer: https://stackoverflow.com/questions/54770354/how-to-use-asp-net-core-signalr-with-pace-js
The value of 'mainHub' is the signalR hub in your C# application you are connecting to.

webpack dev-server: Avoid proxy errors on HTTP errors returned from proxy target

I have a Vue.js project where I have configured a webpack dev-server to proxy all requests to the UI to my backend server. Here is the relevant part of vue.config.js:
devServer: {
contentBase: PATHS.build,
port: 9000,
https: false,
hot: true,
progress: true,
inline: true,
watchContentBase: true,
proxy: {
'^/': {
target: 'http://127.0.0.1:8089',
secure: false
},
}
},
I've noticed that if the HTTP response code from http://127.0.0.1:8089 is anything other than 2xx then the proxy fails with the following error:
Proxy error: Could not proxy request /api/test from localhost:9000 to http://127.0.0.1:8089.
See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (HPE_INVALID_CHUNK_SIZE).
This also causes the HTTP response code from the request to localhost:9000 to be 500 for any error and all the information about what went wrong on the server side is lost. This is problematic as I want to be able to extract information from error responses to display to the user.
I know it's possible to do because I had it working on an older Angular project which I think was using Webpack 3 (am now using Webpack 4). I tried copying all the dev-server config from this project but it just doesn't seem to work here!
EDIT: I was wrong. The Proxy error does not occur on every bad response but only for one of the requests which is a multipart file upload. Still unable to reproduce this in a smaller example to put on github though so struggling to pinpoint the cause.
This error message comes from node_modules/#vue/cli-service/lib/util/prepareProxy.js, which define a onError callback for node-http-proxy;
So I did some experiment, make back-end api generate 400 404 500 response, but I didn't got this error.
After I happen to close back-end api, error arise:
Proxy error: Could not proxy request /hello from localhost:8080 to http://localhost:8081 (ECONNREFUSED).
I search in the doc and find these:
The error event is emitted if the request to the target fail. We do not do any error handling of messages passed between client and proxy, and messages passed between proxy and target, so it is recommended that you listen on errors and handle them
So the onError do not handle error code, is called only when request fail (500 response is still treated as a complete request, connection refuse is not)
Go back to your error message, [HPE_INVALID_CHUNK_SIZE] means bad request to the back-end api. In this issue, it gives an solution: add a keep-alive header:
devServer: {
publicPath: 'http://localhost:9090/front/static-dev/build/',
port: 9090,
proxy: {
'/**': {
target: 'http://localhost:8080',
secure: false,
changeOrigin: true,
headers: {
Connection: 'keep-alive'
}
},
open: true
}
I have finally found the problem, and I apologise, it was a lot more of a specific issue than I originally thought when I wrote the question.
Issue was to do with a request which was proxied to another server using the Spring RestTemplate:
e.g.
#PostMapping("/upload")
public ResponseEntity upload(#RequestParam("file") MultipartFile file)
throws Exception {
String baseUrl = serviceProperties.getAddress();
HttpEntity<MultiValueMap<String, Object>> request = createMultipartRequest(file.getBytes());
return restTemplate.postForEntity(baseUrl + "/api/upload", filterRequest, String.class);
}
The ResponseEntity returning from the rest template proxy contained the header "Connection: close" when the response was anything other than 200 which cause the connection to close and caused this request to fail to return anything which subsequently made the dev-server proxy fail on the UI.
Fixed this by not passing the response headers from the rest template proxy to the response:
#PostMapping("/upload")
public ResponseEntity upload(#RequestParam("file") MultipartFile file)
throws Exception {
String baseUrl = serviceProperties.getAddress();
HttpEntity<MultiValueMap<String, Object>> request = createMultipartRequest(file.getBytes());
ResponseEntity response = restTemplate.postForEntity(baseUrl + "/api/upload", filterRequest, String.class);
return new ResponseEntity<>(response.getBody(), response.getStatusCode());
}