Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Amine Ben hammou committed Aug 13, 2023
1 parent 7111077 commit bc2bcb3
Show file tree
Hide file tree
Showing 14 changed files with 954 additions and 26 deletions.
7 changes: 4 additions & 3 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ jobs:
- run: yarn
- run: yarn build
- name: Publish to npm
uses: JS-DevTools/npm-publish@v2
with:
token: ${{ secrets.NPM_TOKEN }}
uses: pascalgn/npm-publish-action@1.3.9
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
11 changes: 11 additions & 0 deletions bin/japa_types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import '@japa/runner'

declare module '@japa/runner' {
interface TestContext {
// notify TypeScript about custom context properties
}

interface Test<TestData> {
// notify TypeScript about custom test properties
}
}
17 changes: 17 additions & 0 deletions bin/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { expect } from '@japa/expect'
import { pathToFileURL } from 'node:url'
import { specReporter } from '@japa/spec-reporter'
import { expectTypeOf } from '@japa/expect-type'
import { processCliArgs, configure, run } from '@japa/runner'

configure({
...processCliArgs(process.argv.slice(2)),
...{
files: ['tests/**/*.test.ts'],
plugins: [expect(), expectTypeOf()],
reporters: [specReporter()],
importer: (filePath) => import(pathToFileURL(filePath).href),
},
})

run()
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@
"node": ">=16"
},
"scripts": {
"test": "node --loader tsx --test tests/make.test.ts",
"build": "rm -rf dist && tsc"
"test": "tsc --noEmit && tsx bin/test.ts",
"build": "rm -rf dist && tsc -p tsconfig.build.ts"
},
"devDependencies": {
"@japa/expect": "^2.0.2",
"@japa/expect-type": "^1.0.3",
"@japa/runner": "^2.5.1",
"@japa/spec-reporter": "^1.3.3",
"@types/node": "^20.4.10",
"expect-type": "^0.16.0",
"prettier": "^2.8.8",
"tsx": "^3.12.7"
}
Expand Down
2 changes: 1 addition & 1 deletion src/WariError.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ErrorDetails, ErrorKey } from "./types.js";
import type { ErrorDetails, ErrorKey } from "./types.js";

export class WariError<K extends ErrorKey> extends Error {
constructor(public type: K, public details: ErrorDetails<K>) {
Expand Down
6 changes: 3 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { WariError } from "./WariError.js"
import { ErrorDetails, ErrorHandlers, ErrorKey, GetErrorKeys, MatchReturn, Normalize } from "./types.js"
import { WariError } from './WariError.js'
import type { ErrorDetails, ErrorHandlers, ErrorKey, GetErrorKeys, MatchReturn, Normalize } from './types.js'

export {ErrorTypes} from './types.js'
export type { ErrorTypes } from './types.js'

export function make<K extends ErrorKey>(type: K, details: ErrorDetails<K>) {
return new WariError(type, details)
Expand Down
13 changes: 8 additions & 5 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import type { WariError } from "./WariError.js"
import type { WariError } from './WariError.js'

export interface ErrorTypes {}
export type ErrorKey = keyof ErrorTypes
export type ErrorDetails<K extends ErrorKey = ErrorKey> = ErrorTypes[K]
export type GetErrorKeys<E> = E extends WariError<infer K> ? K : never
export type ErrorHandlers<E> = {
[key in GetErrorKeys<E>]: (x: WariError<key>) => any
}
export type ErrorHandlers<E> = E extends WariError<infer K>
? {
[key in K]: (x: WariError<key>) => any
}
: never
// @ts-expect-error

Check failure on line 12 in src/types.ts

View workflow job for this annotation

GitHub Actions / test

Unused '@ts-expect-error' directive.
export type MatchReturn<E, H extends ErrorHandlers<E>> = Exclude<E, WariError<keyof H & keyof ErrorTypes>> | VoidToUndefined<ReturnType<H[keyof H]>>
export type Normalize<T> = {[key in keyof T]: T[key]} & {}
export type Normalize<T> = { [key in keyof T]: T[key] } & {}

type VoidToUndefined<T> = T extends void ? undefined : T
18 changes: 18 additions & 0 deletions tests/is.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { test } from '@japa/runner'
import { is } from '../src/index.js'
import { WariError } from '../src/WariError.js'

test.group('is', () => {
test('it checks the type of a WariError', ({ expect }) => {
const error = new WariError('JsonError', {text: ''})
expect(is(error, 'JsonError')).toBe(true)
// @ts-expect-error
expect(is(error, 'HttpError')).toBe(false)
})

test(`it's typed`, ({expectTypeOf}) => {
expectTypeOf(is).toMatchTypeOf((_x: WariError<'FileError'>, _type: 'FileError'): boolean => true)
expectTypeOf(is).toMatchTypeOf((_x: WariError<'FileError'|'JsonError'>, _type: 'FileError'|'JsonError'): boolean => true)
expectTypeOf(is).not.toMatchTypeOf((_x: Error, _type: 'JsonError'): boolean => false)
})
})
28 changes: 23 additions & 5 deletions tests/make.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
import test from 'node:test';
import assert from 'assert/strict';
import { test } from '@japa/runner'
import {make} from '../src/index.js'
import { WariError } from '../src/WariError.js'

test('1 is equal to 1.', () => {
assert.strictEqual(1, 1);
});
test.group('make', () => {
test('it creates a new WariError', ({ expect }) => {
const error = make('JsonError', {text: 'foo'})
expect(error).toBeInstanceOf(WariError)
expect(error).toMatchObject({
type: 'JsonError',
details: {text: 'foo'}
})
expect(String(error)).toBe(`Error: JsonError: {"text":"foo"}`)
})

test(`it's typed`, ({expectTypeOf}) => {
expectTypeOf(make).toMatchTypeOf((type: 'HttpError', details: {method: 'GET' | 'POST', url: string, status: number}) => new WariError(type, details))
expectTypeOf(make).toMatchTypeOf((type: 'JsonError', details: {text: string}) => new WariError(type, details))
expectTypeOf(make).toMatchTypeOf((type: 'FileError', details: {operation: 'read' | 'write', filePath: string, error: Error}) => new WariError(type, details))
expectTypeOf(make).not.toMatchTypeOf((type: 'HttpError', details: {url: string, status: number}) => new WariError(type, details as any))
expectTypeOf(make).not.toMatchTypeOf((_type: 'HttpError', _details: {url: string, status: number}) => new Error('Oups!'))
expectTypeOf(make).not.toMatchTypeOf((type: 'OtherError', details: {method: 'GET' | 'POST', url: string, status: number}) => new WariError(type as any, details))
})
})
59 changes: 59 additions & 0 deletions tests/match.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { test } from '@japa/runner'
import { make, match } from '../src/index.js'
import { WariError } from '../src/WariError.js'

test.group('match', () => {
test('it returns the non-error value', ({ expect }) => {
const fn = (): number | WariError<'JsonError'> | WariError<'HttpError'> => 1
const res = match(fn(), {
HttpError: console.error,
JsonError: console.error,
})
expect(res).toBe(1)
})

test('it returns the error handler return if error is matched', ({ expect }) => {
const fn = (): number | WariError<'JsonError'> | WariError<'HttpError'> => make('JsonError', { text: '' })
const res = match(fn(), {
HttpError: () => 100,
JsonError: () => 200,
})
expect(res).toBe(200)
})

test(`it's typed`, ({ expectTypeOf }) => {
const foo = (): WariError<'JsonError'> | WariError<'HttpError'> => make('JsonError', { text: '' })
const bar = (): number | WariError<'JsonError'> | WariError<'HttpError'> => 1
const baz = (): number => 2

expectTypeOf(
match(foo(), {
HttpError: () => {},
JsonError: () => {},
})
).toBeUndefined()

expectTypeOf(
match(foo(), {
HttpError: () => 1,
JsonError: () => 'Oups!',
})
).toEqualTypeOf<number | string>()

expectTypeOf(
match(bar(), {
HttpError: () => {},
JsonError: () => {},
})
).toEqualTypeOf<number | undefined>()

expectTypeOf(
match(bar(), {
HttpError: () => {},
JsonError: () => 'Oups',
})
).toEqualTypeOf<string | number | undefined>()

expectTypeOf(match(baz(), {} as never)).toBeNumber()
})
})
9 changes: 9 additions & 0 deletions tests/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
declare module '../src/index.js' {
interface ErrorTypes {
'HttpError': {method: 'GET' | 'POST', url: string, status: number}
'JsonError': {text: string}
'FileError': {operation: 'read' | 'write', filePath: string, error: Error}
}
}

export {}
8 changes: 8 additions & 0 deletions tsconfig.build.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "dist",
"noEmit": false
}
}
8 changes: 4 additions & 4 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"include": ["src", "types"],
"exclude": ["dist", "node_modules"],
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "nodenext",
"lib": ["esnext", "DOM"],
"rootDir": "./src",
"outDir": "./dist",
"lib": ["esnext"],
"rootDir": ".",
"noEmit": true,
"sourceMap": true,
"importHelpers": true,
"declaration": true,
Expand Down
Loading

0 comments on commit bc2bcb3

Please sign in to comment.