No form found for selector 'form' in response

Hi, I’m trying to recycle a perf testing script written in K6 by a predecessor to test a new version of an access management application we use.

The the script we define a location to an authentication page served by the access management app.

The old version had this (which works):

group(‘login form’, function () {
res = http.get(“https://amserver.example.com/login/UI/Login?realm=realm1&goto=https%3A%2F%2Fwebserver.example.com%2Fhome%2Fhome.go&gx_charset=UTF-8”);
check(res, {
“status is 200”: (r) => r.status === 200,

The new version has this (which doesn’t work):

group(‘login form’, function () {
res = http.get(“https://newamserver.example.com:8444/login/UI/Login?realm=realm1&goto=https%3A%2F%2Fwebserver.example.com%2Fhome%2Fhome.go&gx_charset=UTF-8”);
check(res, {
“status is 200”: (r) => r.status === 200,

Script output looks thusly:

ERRO[0008] no form found for selector ‘form’ in response

    at reflect.methodValueCall (native)
    at file:///C:/Program%20Files/k6/orig_k6.txt:75:18(12)
    at go.k6.io/k6/js/modules/k6.(*K6).Group-fm (native)
    at file:///C:/Program%20Files/k6/orig_k6.txt:65:2(14)

What am I missing?

Hello! This will be tricky to answer without being able to see what kind of responses you are working with; the error is telling you that there was no <form> element in a response.body that your script is checking through.

The code you’ve shared isn’t doing any kind of extracting of forms from responses though - is there more code later on, perhaps?

Are you sure it is just this request that has changed? The only real way to know for sure would be to create a recording (e.g. using our Browser Recorder, or a web proxy) and comparing that to what was in the previously-working script, making changes as necessary to bring them in line.

Thanks for the response. The code for the rest of the script is the same in both instances. It is only the login page URL definition that has changed. It is possible that the mechanics of the page in the new version vs the old version are sufficiently different for the script to fail.

The bulk of the rest of the script looks like this:

  "title is correct": (r) => r.html("title").text() == "Login Page Title",
});

});

group(‘submit form’, function () {
res = res.submitForm({
formSelector: “form”,
fields: {
IDToken1: username,
IDToken2: password,
},
});
check(res, {
“status is 200”: (r) => r.status === 200,
“title is correct”: (r) => r.html(“title”).text() === “Page Title String”,
});
});

As part of the output I do see:

 █ login form

   ✓ status is 200
   ✓ title is correct

 █ submit form

At which point it falls over.

Been digging into the new system and the mechanics of what it does are very different compared to the old version. The login page is now sending a large block of JSON instead of just a couple of lines worth of form data.

I guess I need to work out how to replace that "group(‘submit form’, function () " code block with the necessary bits to send the JSON data.

This is the payload that needs to be sent now. The value of the Id data changes with each login so I’m not sure if this can be scripted. How would I go about submitting this?

{“Id”:“eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJvdGsiOiI2bmZzNDVyamRibTJjbnJ1dXUxZ29hY21mOCIsInJlYWxtIjoiL2ludGVybmFsX3VhdDQiLCJzZXNzaW9uSWQiOiJQcUc2Q19xUm43UkNXZjJkMFdyUlJBMTluc00uKkFBSlRTUUFDTURFQUFsTkxBQnhvY2xsU2JVd3daVkl3VVhWNWVtOHpTUzlLZUdkM2FXUTNTR3M5QUFSMGVYQmxBQWxKVGw5TlJVMVBVbGtBQWxNeEFBQS4qIn0.NqILhOlAbGb_Qk8_PVWwPx8epx5OV2Bvwp3WQ7JwBXc”,“template”:“”,“stage”:“DataStore1”,“header”:“Sign in”,“callbacks”:[{“type”:“NameCallback”,“output”:[{“name”:“prompt”,“value”:“User Name:”}],“input”:[{“name”:“IDToken1”,“value”:“username”}]},{“type”:“PasswordCallback”,“output”:[{“name”:“prompt”,“value”:“Password:”}],“input”:[{“name”:“IDToken2”,“value”:“password”}]}]}

That looks like a JWT (Bearer token). These are typically received from some earlier request’s response.body. The process of handling these kinds of dynamic IDs is called Data Correlation and it is something that one frequently encounters when working with k6 scripts.

I’d recommend reading the article and using the aforementioned Browser Recorder and web proxy to figure out where the value is coming from. The easiest way to extract it will probably be to use findBetween as it works regardless of Content-Type (but you might be dealing with JSON, in which case you can use response.json() to get the value with dot notation).