Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test suite improvements #284

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions .semaphore/semaphore.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
version: v1.0
name: AppSignal for JavaScript
agent:
machine:
type: e1-standard-2
os_image: ubuntu1804
auto_cancel:
running:
when: branch != 'master' AND branch != 'develop'
global_job_config:
env_vars:
- name: RUNNING_IN_CI
value: 'true'
- name: NODE_ENV
value: test
prologue:
commands:
- checkout
- sem-version node 12
blocks:
- name: "Install and Build"
dependencies: []
task:
prologue:
commands:
- yarn install --frozen-lockfile
- yarn lerna bootstrap
- yarn lerna link
- cache store
jobs:
- name: Build
commands:
- yarn build
- cache store packages-$SEMAPHORE_GIT_SHA-$SEMAPHORE_GIT_BRANCH packages
- name: "Core Library"
dependencies: ["Install and Build"]
task:
prologue:
commands:
- cache restore
- cache restore packages-$SEMAPHORE_GIT_SHA-$SEMAPHORE_GIT_BRANCH
jobs:
- name: JavaScript
commands:
- yarn lerna run test --scope="@appsignal/javascript"
- name: JavaScript Core
commands:
- yarn lerna run test --scope="@appsignal/core"
- name: "Integrations"
dependencies: ["Core Library"]
task:
prologue:
commands:
- cache restore
- cache restore packages-$SEMAPHORE_GIT_SHA-v$NODE_VERSION
jobs:
- name: React
commands:
- yarn lerna run test --scope="@appsignal/react"
- name: Preact
commands:
- yarn lerna run test --scope="@appsignal/preact"
- name: Vue
commands:
- yarn lerna run test --scope="@appsignal/vue"
- name: "Plugins"
dependencies: ["Core Library"]
task:
prologue:
commands:
- cache restore
- cache restore packages-$SEMAPHORE_GIT_SHA-v$NODE_VERSION
jobs:
- name: Window Events
commands:
- yarn lerna run test --scope="@appsignal/plugin-window-events"
10 changes: 0 additions & 10 deletions .travis.yml

This file was deleted.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,20 @@
"@testing-library/react": "^10.0.3",
"@types/jest": "^25.2.1",
"@types/node": "^13.13.4",
"@types/sinon": "^9.0.0",
"@types/webpack": "^4.41.12",
"husky": "^4.2.5",
"intern": "^4.8.4",
"jest": "^25.4.0",
"lerna": "^3.20.2",
"lint-staged": "^10.2.0",
"npm-run-all": "^4.1.5",
"preact": "^10.0.0-rc.0",
"preact": "^10.0.0",
"prettier": "2.0.5",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"rimraf": "^3.0.2",
"sinon": "^9.0.2",
"stimulus": "^1.1.1",
"ts-jest": "^25.4.0",
"typescript": "^3.8.3",
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"build:watch": "run-p build:cjs:watch build:esm:watch",
"clean": "rimraf dist coverage",
"link:yarn": "yarn link",
"test": "jest --passWithNoTests",
"test": "jest",
"test:watch": "jest --watch"
},
"publishConfig": {
Expand Down
37 changes: 37 additions & 0 deletions packages/core/src/__tests__/serializable.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Serializable } from "../serializable"

describe("Serializable", () => {
let sz: Serializable

describe("with array", () => {
const fixture = ["test"]

beforeEach(() => {
sz = new Serializable(fixture)
})

it("should serialize to JSON", () => {
expect(sz.toJSON()).toEqual(JSON.stringify(fixture))
})

it("should serialize to an object", () => {
expect(sz.serialize()).toBe(fixture)
})
})

describe("with object", () => {
const fixture = { test: "test" }

beforeEach(() => {
sz = new Serializable(fixture)
})

it("should serialize to JSON", () => {
expect(sz.toJSON()).toEqual(JSON.stringify(fixture))
})

it("should serialize to an object", () => {
expect(sz.serialize()).toBe(fixture)
})
})
})
2 changes: 1 addition & 1 deletion packages/core/src/serializable.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export class Serializable<T> {
export class Serializable<T = [] | {}> {
protected _data: T

constructor(data: T) {
Expand Down
66 changes: 66 additions & 0 deletions packages/core/src/utils/__tests__/functional.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { compose } from "../functional"

describe("compose", () => {
it("composes from right to left", () => {
const double = (x: number) => x * 2
const square = (x: number) => x * x

expect(compose(square)(5)).toBe(25)
expect(compose(square, double)(5)).toBe(100)
expect(compose(double, square, double)(5)).toBe(200)
})

it("composes functions from right to left", () => {
const a = (next: (x: string) => string) => (x: string) => next(x + "a")
const b = (next: (x: string) => string) => (x: string) => next(x + "b")
const c = (next: (x: string) => string) => (x: string) => next(x + "c")
const final = (x: string) => x

expect(compose(a, b, c)(final)("")).toBe("abc")
expect(compose(b, c, a)(final)("")).toBe("bca")
expect(compose(c, a, b)(final)("")).toBe("cab")
})

it("throws at runtime if argument is not a function", () => {
type sFunc = (x: number, y: number) => number
const square = (x: number, _: number) => x * x
const add = (x: number, y: number) => x + y

expect(() =>
compose(square, add, (false as unknown) as sFunc)(1, 2)
).toThrow()

expect(() => compose(square, add, undefined!)(1, 2)).toThrow()

expect(() =>
compose(square, add, (true as unknown) as sFunc)(1, 2)
).toThrow()

expect(() =>
compose(square, add, (NaN as unknown) as sFunc)(1, 2)
).toThrow()

expect(() =>
compose(square, add, ("42" as unknown) as sFunc)(1, 2)
).toThrow()
})

it("can be seeded with multiple arguments", () => {
const square = (x: number, _: number) => x * x
const add = (x: number, y: number) => x + y

expect(compose(square, add)(1, 2)).toBe(9)
})

it("returns the first given argument if given no functions", () => {
expect(compose<number>()(1, 2)).toBe(1)
expect(compose()(3)).toBe(3)
expect(compose()(undefined)).toBe(undefined)
})

it("returns the first function if given only one", () => {
const fn = () => {}

expect(compose(fn)).toBe(fn)
})
})
44 changes: 44 additions & 0 deletions packages/core/src/utils/__tests__/hashmap.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { toHashMap, toHashMapString } from "../hashmap"

describe("toHashMap", () => {
it("converts any non-string, boolean or number value in an object to a string", () => {
const hm = toHashMap({
string: "abc",
number: 123,
boolean: true,
object: { test: 123 }
})

if (hm) {
Object.values(hm).forEach(el =>
expect(
typeof el === "string" ||
typeof el === "number" ||
typeof el === "boolean"
).toBeTruthy()
)
}
})

it("returns undefined if argument is undefined", () => {
const hm = toHashMapString()
expect(hm).toBeUndefined()
})
})

describe("toHashMapString", () => {
it("converts all values in a flat object to a string", () => {
const hm = toHashMapString({ string: "abc", number: 123, boolean: true })

if (hm) {
Object.values(hm).forEach((el: string) =>
expect(typeof el === "string").toBeTruthy()
)
}
})

it("returns undefined if argument is undefined", () => {
const hm = toHashMapString()
expect(hm).toBeUndefined()
})
})
8 changes: 8 additions & 0 deletions packages/core/src/utils/__tests__/url.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { urlEncode } from "../url"

describe("urlEncode", () => {
it("encodes a url string", () => {
const url = urlEncode({ string: "abc", number: 123, boolean: true })
expect(url).toEqual("string=abc&number=123&boolean=true")
})
})
63 changes: 54 additions & 9 deletions packages/core/src/utils/functional.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,64 @@
/**
* Taken from from https://github.com/reduxjs/redux
*
* (This file used to be a TypeScript port of the `compose` function, but since
* Redux has been ported to TypeScript, it's now an exact copy)
*/

type Func<T extends any[], R> = (...a: T) => R

/**
* Composes single-argument functions from right to left. The rightmost
* function can take multiple arguments as it provides the signature for
* the resulting composite function.
* function can take multiple arguments as it provides the signature for the
* resulting composite function.
*
* Adapted from https://github.com/reduxjs/redux
* @param {Function[]} funcs The functions to compose.
* @returns {Function} A function obtained by composing the argument functions
* from right to left. For example, compose(f, g, h) is identical to doing
* (...args) => f(g(h(...args))).
* @param funcs The functions to compose.
* @returns A function obtained by composing the argument functions from right
* to left. For example, `compose(f, g, h)` is identical to doing
* `(...args) => f(g(h(...args)))`.
*/
export function compose(): <R>(a: R) => R

export function compose<F extends Function>(f: F): F

/* two functions */
export function compose<A, T extends any[], R>(
f1: (a: A) => R,
f2: Func<T, A>
): Func<T, R>

/* three functions */
export function compose<A, B, T extends any[], R>(
f1: (b: B) => R,
f2: (a: A) => B,
f3: Func<T, A>
): Func<T, R>

/* four functions */
export function compose<A, B, C, T extends any[], R>(
f1: (c: C) => R,
f2: (b: B) => C,
f3: (a: A) => B,
f4: Func<T, A>
): Func<T, R>

/* rest */
export function compose<R>(
f1: (a: any) => R,
...funcs: Function[]
): (...args: any[]) => R

export function compose<R>(...funcs: Function[]): (...args: any[]) => R

export function compose(...funcs: Function[]) {
if (funcs.length === 0) {
// infer the argument type so it is usable in inference down the line
return <T>(arg: T) => arg
}

export function compose<T>(...funcs: ((arg: T) => T)[]) {
if (funcs.length === 1) {
return funcs[0]
}

return funcs.reduce((a, b) => (...args) => a(b(...args)))
return funcs.reduce((a, b) => (...args: any) => a(b(...args)))
}
7 changes: 3 additions & 4 deletions packages/core/src/utils/hashmap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ import { HashMap, HashMapValue } from "@appsignal/types"
* Converts all values in a flat object to a string.
*
* Adapted from https://stackoverflow.com/questions/46982698/how-do-i-convert-all-property-values-in-an-object-to-type-string
*
* @param {object} obj: A flat object structure
*
* @return {object} A sanitized object
*/
export function toHashMapString(
obj?: HashMap<any>
Expand All @@ -25,6 +21,9 @@ export function toHashMapString(
return obj
}

/**
* Converts any non-string, boolean or number value in an object to a string
*/
export function toHashMap(
obj?: HashMap<any>
): HashMap<HashMapValue> | undefined {
Expand Down
4 changes: 4 additions & 0 deletions packages/javascript/intern.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"suites": "src/tests/unit/**/*.ts",
"environments": ["node"]
}
10 changes: 0 additions & 10 deletions packages/javascript/jest.config.js

This file was deleted.

Loading