Skip to content

Commit

Permalink
fix: query string array support (#182)
Browse files Browse the repository at this point in the history
* fix: query string array support

* fix: remove elvis operator

* fix: use the objectToQueryString function at tepper runner

* chore: bump version to 0.4.2

* fix: skip undefined values
  • Loading branch information
DanielRamosAcosta authored Jun 23, 2022
1 parent 6ca1f1a commit b3b475a
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 11 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tepper",
"version": "0.4.1",
"version": "0.4.2",
"description": "Modern library for testing HTTP servers",
"main": "dist/tepper.js",
"engines": {
Expand Down Expand Up @@ -73,4 +73,4 @@
"node-fetch": "^2.6.1",
"@types/node-fetch": "2.6.2"
}
}
}
3 changes: 1 addition & 2 deletions src/TepperConfig.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { ParsedUrlQueryInput } from "querystring"
import { DebugOptions } from "./DebugOptions"

export type TepperConfig = {
readonly method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE"
readonly path: string
readonly body: string | object | null
readonly isForm: boolean
readonly query: ParsedUrlQueryInput | null
readonly query: object | null
readonly redirects: number
readonly expectedStatus: number | null
readonly expectedBody:
Expand Down
16 changes: 13 additions & 3 deletions src/TepperRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import fetch from "node-fetch"
import qs from "querystring"
import { Readable } from "stream"
import { FormDataEncoder } from "form-data-encoder"
import { URLSearchParams } from "url"
import { listenAppPromised, listenServerPromised } from "./utils/listenPromised"
import { getBaseUrl } from "./utils/getBaseUrl"
import { closePromised } from "./utils/closePromised"
Expand All @@ -12,6 +13,7 @@ import { TepperConfig } from "./TepperConfig"
import { TepperResult } from "./TepperResult"
import { BaseUrlServerOrExpress } from "./BaseUrlServerOrExpress"
import { objectToFormData } from "./forms/objectToFormData"
import { objectToQueryString } from "./queries/objectToQueryString"

function isExpressApp(
baseUrlServerOrExpress: BaseUrlServerOrExpress,
Expand Down Expand Up @@ -63,9 +65,7 @@ export class TepperRunner {
endpoint: string,
config: TepperConfig,
): Promise<TepperResult> {
const endpointWithQuery = config.query
? endpoint.concat("?").concat(qs.stringify(config.query))
: endpoint
const endpointWithQuery = this.appendQuery(endpoint, config)

const { body, headers } = this.insertBodyIfPresent(config)

Expand Down Expand Up @@ -112,6 +112,16 @@ export class TepperRunner {
return result
}

private static appendQuery(endpoint: string, config: TepperConfig) {
if (!config.query) {
return endpoint
}

return endpoint
.concat("?")
.concat(objectToQueryString(config.query).toString())
}

private static insertBodyIfPresent(config: TepperConfig): {
body: any
headers: object
Expand Down
31 changes: 31 additions & 0 deletions src/queries/objectToQueryString.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { objectToQueryString } from "./objectToQueryString"

describe("objectToQueryString", () => {
it("serializes a query with strings", () => {
const query = {
hello: "world",
}

const result = objectToQueryString(query)

expect(result).toEqual("hello=world")
})

it("serializes a query with an array", () => {
const query = {
tags: ["first-tag", "second-tag"],
}

const result = objectToQueryString(query)

expect(result).toEqual("tags[]=first-tag&tags[]=second-tag")
})

it("skips undefined values", () => {
const query = { empty: undefined }

const result = objectToQueryString(query)

expect(result).toEqual("")
})
})
18 changes: 18 additions & 0 deletions src/queries/objectToQueryString.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { URLSearchParams } from "url"

export function objectToQueryString(query: object) {
const params = new URLSearchParams()
const arrayIndicator = "__array_indicator__"

for (const [key, value] of Object.entries(query)) {
if (Array.isArray(value)) {
for (const v of value) {
params.append(key + arrayIndicator, v)
}
} else if (value != null) {
params.append(key, (value && value.toString()) || "")
}
}

return params.toString().replace(new RegExp(arrayIndicator, "g"), "[]")
}
41 changes: 41 additions & 0 deletions test/query-params.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,45 @@ describe("query params", () => {

expect(body).toEqual({ hello: "world" })
})

it("skips undefined values", async () => {
const app = express().get("/", (req, res) => {
res.send(req.query)
})

const { body } = await tepper(app)
.get("")
.withQuery({ hello: undefined })
.run()

expect(body).toEqual({})
})

describe("array params", () => {
it("parses correctly an array", async () => {
const app = express().get("/", (req, res) => {
res.send(req.query)
})

const { body } = await tepper(app)
.get("")
.withQuery({ tags: ["first-tag", "second-tag"] })
.run()

expect(body).toEqual({ tags: ["first-tag", "second-tag"] })
})

it("parses correctly an array with one element", async () => {
const app = express().get("/", (req, res) => {
res.send(req.query)
})

const { body } = await tepper(app)
.get("")
.withQuery({ tags: ["first-tag"] })
.run()

expect(body).toEqual({ tags: ["first-tag"] })
})
})
})

0 comments on commit b3b475a

Please sign in to comment.