Repository: restqa/restqa (js)

Issue:
🐞 [Bug]: Query string is are not handle when passed on the path
### Contact Details

_No response_

### What happened?

If we add the query strings in the step definition:
```gherkin
When GET "/?q=keyword"
```

The query strings are ignored

### Relevant log output

```shell
> DEBUG: server is running on the port 8887
❌ FAILED - GET / > Receive a message containing the query string sent
    AssertionError [ERR_ASSERTION]: [GET /%3Fq=keyword] The response httpCode is invalid, received 404 should be 200
    + expected - actual

    -404
    +200

    at ../../../examples/scenarios/04-request-querystring/tests/local/get.feature:6
```


### 🙏 Would you be willing to submit a PR?

Yes

### Code of Conduct

- [X] I agree to follow this project's Code of Conduct

Pull request:
fix: Allow to be pass query string in the same step of the path
## Change description

>  Let's allow to pass query parameters in the path as well

## Type of change
- [X] Bug fix (fixes an issue)
- [ ] New feature (adds functionality)

## Related issues

Fix #331 

## Checklists

### Development

- [x] Lint rules pass locally
- [x] Application changes have been tested thoroughly
- [x] Automated tests covering modified code pass

### Security

- [x] Security impact of change has been considered
- [x] Code follows project security practices and guidelines

### Code review 

- [x] Pull request has a descriptive title and context useful to a reviewer. Screenshots or screencasts are attached as necessary
- [x] Changes have been reviewed by at least one other contributor
- [x] Pull request linked to issue tracker where applicable



Patch:
diff --git a/packages/cli/src/core/initialize.js b/packages/cli/src/core/initialize.js
index 4c6a2d1..c972a79 100644
--- a/packages/cli/src/core/initialize.js
+++ b/packages/cli/src/core/initialize.js
@@ -148,9 +148,9 @@ initialize.generate = async function (options) {
     createRecursiveFolder(output, folder);
 
     const content = [
-      "Feature: Welcome to the RestQA community",
+      "Feature: GET /",
       "",
-      "Scenario: Initial scenario",
+      "Scenario: Initial API (generated)",
       response
     ];
 
diff --git a/packages/cli/src/formatters/export-formatter.js b/packages/cli/src/formatters/export-formatter.js
index 69314c7..0b4f80b 100644
--- a/packages/cli/src/formatters/export-formatter.js
+++ b/packages/cli/src/formatters/export-formatter.js
@@ -1,5 +1,5 @@
 const Welcome = require("../utils/welcome");
-const Pkg = require("../../package.json");
+const {getPackageJson} = require("../utils/fs");
 const path = require("path");
 const {getFormatter} = require("@restqa/cucumber-export");
 const Report = require("@restqa/report-ui");
@@ -83,13 +83,14 @@ options.customExporters = {
 };
 
 function getDataOutput(RESTQA_RESULT) {
-  let RESTQA_SPECIFICATION,
-    RESTQA_INTEGRATION,
-    RESTQA_PERFORMANCE,
-    RESTQA_COLLECTION,
-    RESTQA_HTTP_MOCKS,
-    RESTQA_COVERAGE,
-    RESTQA_CONTRIBUTORS;
+  let RESTQA_SPECIFICATION;
+  let RESTQA_INTEGRATION;
+  let RESTQA_PERFORMANCE;
+  let RESTQA_COLLECTION;
+  let RESTQA_HTTP_MOCKS;
+  let RESTQA_COVERAGE;
+  let RESTQA_VERSION = "0.0.0";
+  let RESTQA_CONTRIBUTORS;
 
   if (global.restqa && global.restqa.specification) {
     RESTQA_SPECIFICATION = global.restqa.specification;
@@ -115,6 +116,11 @@ function getDataOutput(RESTQA_RESULT) {
     RESTQA_COVERAGE = global.restqa.coverage.filename;
   }
 
+  const pkg = getPackageJson(path.join(__dirname, "..", "..", "..")); // from the dist/src/formatters folder...
+  if (pkg) {
+    RESTQA_VERSION = pkg.version;
+  }
+
   return {
     RESTQA_FOLDER: process.cwd(),
     RESTQA_RESULT,
@@ -126,7 +132,7 @@ function getDataOutput(RESTQA_RESULT) {
     RESTQA_CONFIG: config,
     RESTQA_HTTP_MOCKS,
     RESTQA_COVERAGE,
-    RESTQA_VERSION: Pkg.version
+    RESTQA_VERSION
   };
 }
 
diff --git a/packages/plugin-rest-api/src/rest-api/steps/2-when/functions.js b/packages/plugin-rest-api/src/rest-api/steps/2-when/functions.js
index 204d2c8..801c797 100644
--- a/packages/plugin-rest-api/src/rest-api/steps/2-when/functions.js
+++ b/packages/plugin-rest-api/src/rest-api/steps/2-when/functions.js
@@ -1,11 +1,17 @@
+const {URL} = require("url");
 const When = {};
 
 When.callApi = function (method) {
-  return async function (path) {
+  return async function (pathname) {
     this.api = this.api || this.createApi();
     try {
-      path = this.data.get(path);
-      this.api.request.setPath(encodeURI(path));
+      pathname = this.data.get(pathname);
+      const url = new URL(pathname, "http://fake.com");
+      url.searchParams.forEach((value, key) => {
+        this.api.request.setQueryString(key, value);
+      });
+
+      this.api.request.setPath(encodeURI(url.pathname));
 
       method = method.toLowerCase();
       const allowed = [


Test patch:
diff --git a/packages/cli/src/core/initialize.test.js b/packages/cli/src/core/initialize.test.js
index 61d5391..fd10a85 100644
--- a/packages/cli/src/core/initialize.test.js
+++ b/packages/cli/src/core/initialize.test.js
@@ -209,9 +209,9 @@ describe("#Cli - Initialize", () => {
           .readFileSync(filenameWelcome)
           .toString("utf-8");
         const expectedWelcomeFeature = `
-Feature: Welcome to the RestQA community
+Feature: GET /
 
-Scenario: Initial scenario
+Scenario: Initial API (generated)
 Given I have an example`;
 
         expect(contentWelcome.trim()).toEqual(expectedWelcomeFeature.trim());
@@ -289,9 +289,9 @@ Given I have an example`;
           .readFileSync(filenameWelcome)
           .toString("utf-8");
         const expectedWelcomeFeature = `
-Feature: Welcome to the RestQA community
+Feature: GET /
 
-Scenario: Initial scenario
+Scenario: Initial API (generated)
 Given I have an example`;
 
         expect(contentWelcome.trim()).toEqual(expectedWelcomeFeature.trim());
diff --git a/packages/plugin-rest-api/src/rest-api/steps/2-when/functions.test.js b/packages/plugin-rest-api/src/rest-api/steps/2-when/functions.test.js
index ffa84dd..b47a0dd 100644
--- a/packages/plugin-rest-api/src/rest-api/steps/2-when/functions.test.js
+++ b/packages/plugin-rest-api/src/rest-api/steps/2-when/functions.test.js
@@ -54,5 +54,81 @@ describe("#StepDefinition - given - functions", () => {
         '"PUTT" is not a valid http method. Accepted : https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods'
       );
     });
+
+    test("callApi GET with 1 query parameter", async () => {
+      const $this = {
+        data: {
+          get: (_) => _
+        },
+        api: {
+          request: {
+            setMethod: jest.fn(),
+            setPath: jest.fn(),
+            setQueryString: jest.fn()
+          },
+          run: jest.fn().mockResolvedValue(true),
+          getCurl: () => "curl http://localhost/"
+        },
+        attach: jest.fn()
+      };
+
+      const fn = When.callApi("GET");
+
+      await fn.call($this, "/users?firstname=john");
+
+      expect($this.api.run.mock.calls).toHaveLength(1);
+      expect($this.api.run.mock.calls[0][0]).toBeUndefined();
+      expect($this.attach.mock.calls).toHaveLength(1);
+      expect($this.attach.mock.calls[0][0]).toEqual("curl http://localhost/");
+      expect($this.api.request.setMethod.mock.calls).toHaveLength(1);
+      expect($this.api.request.setMethod.mock.calls[0][0]).toEqual("get");
+      expect($this.api.request.setPath.mock.calls).toHaveLength(1);
+      expect($this.api.request.setPath.mock.calls[0][0]).toEqual("/users");
+      expect($this.api.request.setQueryString.mock.calls).toHaveLength(1);
+      expect($this.api.request.setQueryString.mock.calls[0][0]).toEqual(
+        "firstname"
+      );
+      expect($this.api.request.setQueryString.mock.calls[0][1]).toEqual("john");
+    });
+
+    test("callApi GET with multiple query parameters", async () => {
+      const $this = {
+        data: {
+          get: (_) => _
+        },
+        api: {
+          request: {
+            setMethod: jest.fn(),
+            setPath: jest.fn(),
+            setQueryString: jest.fn()
+          },
+          run: jest.fn().mockResolvedValue(true),
+          getCurl: () => "curl http://localhost/"
+        },
+        attach: jest.fn()
+      };
+
+      const fn = When.callApi("GET");
+
+      await fn.call($this, "/users?firstname=john&lastname=doe");
+
+      expect($this.api.run.mock.calls).toHaveLength(1);
+      expect($this.api.run.mock.calls[0][0]).toBeUndefined();
+      expect($this.attach.mock.calls).toHaveLength(1);
+      expect($this.attach.mock.calls[0][0]).toEqual("curl http://localhost/");
+      expect($this.api.request.setMethod.mock.calls).toHaveLength(1);
+      expect($this.api.request.setMethod.mock.calls[0][0]).toEqual("get");
+      expect($this.api.request.setPath.mock.calls).toHaveLength(1);
+      expect($this.api.request.setPath.mock.calls[0][0]).toEqual("/users");
+      expect($this.api.request.setQueryString.mock.calls).toHaveLength(2);
+      expect($this.api.request.setQueryString.mock.calls[0][0]).toEqual(
+        "firstname"
+      );
+      expect($this.api.request.setQueryString.mock.calls[0][1]).toEqual("john");
+      expect($this.api.request.setQueryString.mock.calls[1][0]).toEqual(
+        "lastname"
+      );
+      expect($this.api.request.setQueryString.mock.calls[1][1]).toEqual("doe");
+    });
   });
 });