From aa81b452be13ec312bac974565269b3c3196c0d3 Mon Sep 17 00:00:00 2001 From: Brian Cavalier Date: Sun, 31 Mar 2019 17:08:55 -0400 Subject: [PATCH 1/3] Add unit tests --- src/index.test.ts | 143 ++++++++++++++++++++++++++-------------------- tsconfig.json | 2 +- 2 files changed, 83 insertions(+), 62 deletions(-) diff --git a/src/index.test.ts b/src/index.test.ts index 2de9099..0702d8b 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -1,83 +1,104 @@ -import { count, index, indexed, keepIndex, withCount, withIndex, withIndexStart } from './index' -import { constant, periodic, runEffects, take, tap } from '@most/core' -import { newDefaultScheduler } from '@most/scheduler' -import { Stream } from '@most/types' -import { describe, it } from '@typed/test' +import { + count, + index, + indexed, + keepIndex, + withCount, + withIndex, + withIndexStart +} from "./index"; +import { constant, periodic, runEffects, take, tap } from "@most/core"; +import { newDefaultScheduler } from "@most/scheduler"; +import { Stream } from "@most/types"; +import { describe, it } from "@typed/test"; -const collect = async(n: number, s: Stream): Promise => { - const eventValues: A[] = [] - const collectStream = tap(x => eventValues.push(x), s) - await runEffects(take(n, collectStream), newDefaultScheduler()) - return eventValues -} +const collect = async (n: number, s: Stream): Promise => { + const eventValues: A[] = []; + const collectStream = tap(x => eventValues.push(x), s); + await runEffects(take(n, collectStream), newDefaultScheduler()); + return eventValues; +}; const range = (start: number, n: number): number[] => - Array.from(Array(n), (_, i) => start + i) + Array.from(Array(n), (_, i) => start + i); const randomInt = (min: number, max: number) => - min + Math.floor(Math.random() * (max - min)) + min + Math.floor(Math.random() * (max - min)); -export const indexTests = describe('index', [ - it('replaces events with 0-based index', async ({ equal }) => { - const n = randomInt(10, 20) - const events = await collect(n, index(periodic(1))) - equal(range(0, n), events) +export const indexTests = describe("index", [ + it("replaces events with 0-based index", async ({ equal }) => { + const n = randomInt(10, 20); + const events = await collect(n, index(periodic(1))); + equal(range(0, n), events); }) -]) +]); -export const withIndexTests = describe('withIndex', [ - it('pairs events with 0-based count', async ({ equal }) => { - const n = randomInt(10, 20) - const events = await collect(n, withIndex(constant('test', periodic(1)))) - equal(range(0, n).map(x => [x, 'test']), events) +export const withIndexTests = describe("withIndex", [ + it("pairs events with 0-based count", async ({ equal }) => { + const n = randomInt(10, 20); + const events = await collect(n, withIndex(constant("test", periodic(1)))); + equal(range(0, n).map(x => [x, "test"]), events); }) -]) +]); -export const countTests = describe('count', [ - it('replaces events with 1-based count', async ({ equal }) => { - const n = randomInt(10, 20) - const events = await collect(n, count(periodic(1))) - equal(range(1, n), events) +export const countTests = describe("count", [ + it("replaces events with 1-based count", async ({ equal }) => { + const n = randomInt(10, 20); + const events = await collect(n, count(periodic(1))); + equal(range(1, n), events); }) -]) +]); -export const withCountTests = describe('withCount', [ - it('pairs events with 1-based count', async ({ equal }) => { - const n = randomInt(10, 20) - const events = await collect(n, withCount(constant('test', periodic(1)))) - equal(range(1, n).map(x => [x, 'test']), events) +export const withCountTests = describe("withCount", [ + it("pairs events with 1-based count", async ({ equal }) => { + const n = randomInt(10, 20); + const events = await collect(n, withCount(constant("test", periodic(1)))); + equal(range(1, n).map(x => [x, "test"]), events); }) -]) +]); -export const withIndexStartTests = describe('withIndexStart', [ - it('pairs events with start-based index', async ({ equal }) => { - const start = randomInt(0, 10000) - const n = randomInt(10, 20) - const events = await collect(n, withIndexStart(start, constant('test', periodic(1)))) - equal(range(start, n).map(x => [x, 'test']), events) +export const withIndexStartTests = describe("withIndexStart", [ + it("pairs events with start-based index", async ({ equal }) => { + const start = randomInt(0, 10000); + const n = randomInt(10, 20); + const events = await collect( + n, + withIndexStart(start, constant("test", periodic(1))) + ); + equal(range(start, n).map(x => [x, "test"]), events); }) -]) +]); -export const indexedTests = describe('indexed', [ - it('pairs events with computed index', async ({ equal }) => { - const n = randomInt(10, 20) +export const indexedTests = describe("indexed", [ + it("pairs events with computed index", async ({ equal }) => { + const n = randomInt(10, 20); - const s = Array(n).fill('a').join('') - const expected = range(0, n).map((_, i) => [s.slice(0, i), 'test']) + const s = Array(n) + .fill("a") + .join(""); + const expected = range(0, n).map((_, i) => [s.slice(0, i), "test"]); - const stringIndex = (s: string) => (prev: string): [string, string] => - [prev, prev + s] + const stringIndex = (s: string) => (prev: string): [string, string] => [ + prev, + prev + s + ]; - const events = await collect(n, indexed(stringIndex('a'), '', constant('test', periodic(1)))) - equal(expected, events) + const events = await collect( + n, + indexed(stringIndex("a"), "", constant("test", periodic(1))) + ); + equal(expected, events); }) -]) +]); -export const keepIndexTests = describe('keepIndex', [ - it('keeps index and discards value', async ({ equal }) => { - const start = randomInt(0, 10000) - const n = randomInt(10, 20) - const events = await collect(n, keepIndex(withIndexStart(start, constant('test', periodic(1))))) - equal(range(start, n), events) +export const keepIndexTests = describe("keepIndex", [ + it("keeps index and discards value", async ({ equal }) => { + const start = randomInt(0, 10000); + const n = randomInt(10, 20); + const events = await collect( + n, + keepIndex(withIndexStart(start, constant("test", periodic(1)))) + ); + equal(range(start, n), events); }) -]) \ No newline at end of file +]); diff --git a/tsconfig.json b/tsconfig.json index 7b8b447..8454889 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "moduleResolution": "node", "target": "es5", - "module": "es2015", + "module": "commonjs", "lib": [ "es5", "es2015", From f5f3d2a37541530ad1b99dbb20a9c9686945fb9d Mon Sep 17 00:00:00 2001 From: Frederik Krautwald Date: Tue, 2 Apr 2019 17:00:31 +0200 Subject: [PATCH 2/3] Set up tsconfigs This commit sets up tsconfigs to avoid type check errors and conflicts between @typed/test and microbundle. It does so by extending a base configuration in for both production and testing environments. The latter prepares the setup for @typed/test v10.0, where a tsconfig can be specified. Signed-off-by: Frederik Krautwald --- package.json | 2 +- src/{index.test.ts => index.__test__.ts} | 0 tsconfig.base.json | 21 +++++++++++++++++ tsconfig.json | 30 +++--------------------- tsconfig.test.json | 9 +++++++ 5 files changed, 34 insertions(+), 28 deletions(-) rename src/{index.test.ts => index.__test__.ts} (100%) create mode 100644 tsconfig.base.json create mode 100644 tsconfig.test.json diff --git a/package.json b/package.json index 2f4d331..b0147bd 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "scripts": { "prepublishOnly": "npm run build", "build": "microbundle -i src/index.ts -f es,cjs", - "test": "typed-test --typeCheck 'src/**/*.test.ts'" + "test": "typed-test --typeCheck 'src/**/*.__test__.ts'" }, "keywords": [ "most", diff --git a/src/index.test.ts b/src/index.__test__.ts similarity index 100% rename from src/index.test.ts rename to src/index.__test__.ts diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 0000000..e80a90b --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "es2015", + "moduleResolution": "node", + "lib": ["es2015", "es2016", "es2017"], + "declaration": true, + "sourceMap": true, + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true + }, + "exclude": ["node_modules", "src/**/*.__test__.ts"] +} diff --git a/tsconfig.json b/tsconfig.json index 8454889..8eeb7d6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,31 +1,7 @@ { + "extends": "./tsconfig.base.json", "compilerOptions": { - "moduleResolution": "node", - "target": "es5", - "module": "commonjs", - "lib": [ - "es5", - "es2015", - "es2016", - "es2017" - ], - "incremental": true, - "strict": true, - "sourceMap": true, - "declaration": true, - "declarationDir": "dist", - "outDir": "dist", - "typeRoots": [ - "node_modules/@types" - ] + "outDir": "dist" }, - "exclude": [ - "experiments/**/*.ts", - "examples/**/*.ts", - "node_modules", - "src/**/*.test.ts" - ], - "include": [ - "src" - ] + "include": ["src/**/*.ts"] } diff --git a/tsconfig.test.json b/tsconfig.test.json new file mode 100644 index 0000000..b9c47be --- /dev/null +++ b/tsconfig.test.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "lib": ["dom", "es2015", "es2016", "es2017"], + "outDir": "__test__" + }, + "include": ["src/**/*.__test__.ts"], + "exclude": ["node_modules"] +} From 925d63472f94277998580f474728881993f260c6 Mon Sep 17 00:00:00 2001 From: Frederik Krautwald Date: Tue, 2 Apr 2019 18:18:23 +0200 Subject: [PATCH 3/3] Clarify tests This commit factors out details and adds comments by applying the four-phase testing pattern to increase the readability and understanding of the tests. Signed-off-by: Frederik Krautwald --- src/index.__test__.ts | 184 ++++++++++++++++++++++++++++-------------- 1 file changed, 122 insertions(+), 62 deletions(-) diff --git a/src/index.__test__.ts b/src/index.__test__.ts index 0702d8b..cfe2cb4 100644 --- a/src/index.__test__.ts +++ b/src/index.__test__.ts @@ -10,13 +10,15 @@ import { import { constant, periodic, runEffects, take, tap } from "@most/core"; import { newDefaultScheduler } from "@most/scheduler"; import { Stream } from "@most/types"; -import { describe, it } from "@typed/test"; +import { describe, it, given } from "@typed/test"; + +const TEST_EVENT_VALUE = "test"; const collect = async (n: number, s: Stream): Promise => { - const eventValues: A[] = []; - const collectStream = tap(x => eventValues.push(x), s); - await runEffects(take(n, collectStream), newDefaultScheduler()); - return eventValues; + const xs: A[] = []; + const sa = tap(x => xs.push(x), s); + await runEffects(take(n, sa), newDefaultScheduler()); + return xs; }; const range = (start: number, n: number): number[] => @@ -26,79 +28,137 @@ const randomInt = (min: number, max: number) => min + Math.floor(Math.random() * (max - min)); export const indexTests = describe("index", [ - it("replaces events with 0-based index", async ({ equal }) => { - const n = randomInt(10, 20); - const events = await collect(n, index(periodic(1))); - equal(range(0, n), events); - }) + given("a stream", [ + it("replaces events with 0-based index", async ({ equal }) => { + // Fixture setup + const s = periodic(1); + const n = randomInt(10, 20); + const expectedEvents = range(0, n); + // Exercise system + const result = index(s); + // Verify outcome + const actualEvents = await collect(n, result); + equal(expectedEvents, actualEvents); + }) + ]) ]); export const withIndexTests = describe("withIndex", [ - it("pairs events with 0-based count", async ({ equal }) => { - const n = randomInt(10, 20); - const events = await collect(n, withIndex(constant("test", periodic(1)))); - equal(range(0, n).map(x => [x, "test"]), events); - }) + given("a stream", [ + it("pairs events with 0-based count", async ({ equal }) => { + // Fixture setup + const s = constant(TEST_EVENT_VALUE, periodic(1)); + const n = randomInt(10, 20); + const expectedEvents = range(0, n).map<[number, string]>(n => [ + n, + TEST_EVENT_VALUE + ]); + // Exercise system + const result = withIndex(s); + // Verify outcome + const actualEvents = await collect(n, result); + equal(expectedEvents, actualEvents); + }) + ]) ]); export const countTests = describe("count", [ - it("replaces events with 1-based count", async ({ equal }) => { - const n = randomInt(10, 20); - const events = await collect(n, count(periodic(1))); - equal(range(1, n), events); - }) + given("a stream", [ + it("replaces events with 1-based count", async ({ equal }) => { + // Fixture setup + const s = periodic(1); + const n = randomInt(10, 20); + const expectedEvents = range(1, n); + // Exercise system + const result = count(s); + // Verify outcome + const actualEvents = await collect(n, result); + equal(expectedEvents, actualEvents); + }) + ]) ]); export const withCountTests = describe("withCount", [ - it("pairs events with 1-based count", async ({ equal }) => { - const n = randomInt(10, 20); - const events = await collect(n, withCount(constant("test", periodic(1)))); - equal(range(1, n).map(x => [x, "test"]), events); - }) + given("a stream", [ + it("pairs events with 1-based count", async ({ equal }) => { + // Fixture setup + const s = constant(TEST_EVENT_VALUE, periodic(1)); + const n = randomInt(10, 20); + const expectedEvents = range(1, n).map<[number, string]>(n => [ + n, + TEST_EVENT_VALUE + ]); + // Exercise system + const result = withCount(s); + // Verify outcome + const actualEvents = await collect(n, result); + equal(expectedEvents, actualEvents); + }) + ]) ]); export const withIndexStartTests = describe("withIndexStart", [ - it("pairs events with start-based index", async ({ equal }) => { - const start = randomInt(0, 10000); - const n = randomInt(10, 20); - const events = await collect( - n, - withIndexStart(start, constant("test", periodic(1))) - ); - equal(range(start, n).map(x => [x, "test"]), events); - }) + given("a start index and a stream", [ + it("pairs events with start-based index", async ({ equal }) => { + // Fixture setup + const idx = randomInt(0, 10000); + const s = constant(TEST_EVENT_VALUE, periodic(1)); + const n = randomInt(10, 20); + const expectedEvents = range(idx, n).map<[number, string]>(n => [ + n, + TEST_EVENT_VALUE + ]); + // Exercise system + const result = withIndexStart(idx, s); + // Verify outcome + const actualEvents = await collect(n, result); + equal(expectedEvents, actualEvents); + }) + ]) ]); export const indexedTests = describe("indexed", [ - it("pairs events with computed index", async ({ equal }) => { - const n = randomInt(10, 20); - - const s = Array(n) - .fill("a") - .join(""); - const expected = range(0, n).map((_, i) => [s.slice(0, i), "test"]); - - const stringIndex = (s: string) => (prev: string): [string, string] => [ - prev, - prev + s - ]; - - const events = await collect( - n, - indexed(stringIndex("a"), "", constant("test", periodic(1))) - ); - equal(expected, events); - }) + given("a function, an initial value, and a stream", [ + it("should pair events with computed index", async ({ equal }) => { + // Fixture setup + const n = randomInt(10, 20); + const arbitraryChar = "a"; + const strOfLenN = Array(n) + .fill(arbitraryChar) + .join(""); + const stringIndex = (s: string) => (prev: string): [string, string] => [ + prev, + prev + s + ]; + const f = stringIndex(arbitraryChar); + const init = ""; + const s = constant(TEST_EVENT_VALUE, periodic(1)); + const expectedEvents = range(0, n).map<[string, string]>((_, i) => [ + strOfLenN.slice(0, i), + TEST_EVENT_VALUE + ]); + // Exercise system + const result = indexed(f, init, s); + // Verify outcome + const actualEvents = await collect(n, result); + equal(expectedEvents, actualEvents); + }) + ]) ]); export const keepIndexTests = describe("keepIndex", [ - it("keeps index and discards value", async ({ equal }) => { - const start = randomInt(0, 10000); - const n = randomInt(10, 20); - const events = await collect( - n, - keepIndex(withIndexStart(start, constant("test", periodic(1)))) - ); - equal(range(start, n), events); - }) + given("a stream of an [index, value] pair", [ + it("should keep index and discard value", async ({ equal }) => { + // Fixture setup + const idx = randomInt(0, 10000); + const s = withIndexStart(idx, constant(TEST_EVENT_VALUE, periodic(1))); + const n = randomInt(10, 20); + const expectedEvents = range(idx, n); + // Exercise system + const result = keepIndex(s); + // Verify outcome + const actualEvents = await collect(n, result); + equal(expectedEvents, actualEvents); + }) + ]) ]);