[@hapi/*] new namespace for hapi modules followup #1

This commit is contained in:
Silas Rech
2019-04-12 15:26:23 +02:00
parent 01fd72c035
commit 9cd106ba1b
17 changed files with 1596 additions and 0 deletions
+170
View File
@@ -0,0 +1,170 @@
import { expect, settings, fail, count, incomplete, thrownAt } from "@hapi/code";
expect(10).to.be.above(5);
expect("abc").to.be.a.string();
expect([1, 2]).to.be.an.array();
expect(20).to.be.at.least(20);
expect("abc").to.have.length(3);
expect("abc").to.be.a.string().and.contain(["a", "b"]);
expect(6).to.be.in.range(5, 6);
expect(10).to.not.be.above(20);
expect([1, 2, 3]).to.shallow.include(3);
expect([1, 1, 2]).to.only.include([1, 2]);
expect([1, 2]).to.once.include([1, 2]);
expect([1, 2, 3]).to.part.include([1, 4]);
expect(10, "Age").to.be.above(5);
const func = function() { return arguments; }; // tslint:disable-line only-arrow-functions
expect(func()).to.be.arguments();
expect([1, 2]).to.be.an.array();
expect(true).to.be.a.boolean();
expect(new Date()).to.be.a.date();
const err = new Error("Oops an error occured.");
expect(err).to.be.an.error();
expect(err).to.be.an.error(Error);
expect(err).to.be.an.error("Oops an error occured.");
expect(err).to.be.an.error(Error, /occured/);
expect(func).to.be.a.function();
expect(123).to.be.a.number();
expect(/abc/).to.be.a.regexp();
expect("abc").to.be.a.string();
expect({ a: "1" }).to.be.an.object();
expect(true).to.be.true();
expect(false).to.be.false();
expect(null).to.be.null();
expect(undefined).to.be.undefined();
expect("abc").to.include("ab");
expect("abc").to.only.include("abc");
expect("aaa").to.only.include("a");
expect("abc").to.once.include("b");
expect("abc").to.include(["a", "c"]);
expect("abc").to.part.include(["a", "d"]);
expect([1, 2, 3]).to.include(1);
expect([{ a: 1 }]).to.include({ a: 1 });
expect([1, 2, 3]).to.include([1, 2]);
expect([{ a: 1 }]).to.include([{ a: 1 }]);
expect([1, 1, 2]).to.only.include([1, 2]);
expect([1, 2]).to.once.include([1, 2]);
expect([1, 2, 3]).to.part.include([1, 4]);
expect([[1], [2]]).to.include([[1]]);
interface TestType {
a: number;
b?: number;
c?: number;
d?: number;
}
interface TestType2 {
a: number[];
b?: number[];
c: number[];
}
expect({ a: 1, b: 2, c: 3 }).to.include("a");
expect({ a: 1, b: 2, c: 3 }).to.include(["a", "c"]);
expect({ a: 1, b: 2, c: 3 }).to.only.include(["a", "b", "c"]);
expect<TestType>({ a: 1, b: 2, c: 3 }).to.include({ a: 1 });
expect<any>({ a: 1, b: 2, c: 3 }).to.include({ a: 1 });
expect<TestType>({ a: 1, b: 2, c: 3 }).to.include({ a: 1, c: 3 });
expect<TestType>({ a: 1, b: 2, c: 3 }).to.part.include({ a: 1, d: 4 });
expect<any>({ a: 1, b: 2, c: 3 }).to.part.include({ a: 1, d: 4 });
expect({ a: 1, b: 2, c: 3 }).to.only.include({ a: 1, b: 2, c: 3 });
expect<TestType2>({ a: [1], b: [2], c: [3] }).to.include({ a: [1], c: [3] });
expect<any>({ a: [1], b: [2], c: [3] }).to.include({ a: [1], c: [3] });
expect("https://example.org/secure").to.startWith("https://");
expect("http://example.org/relative").to.endWith("/relative");
expect(4).to.exist();
expect(null).to.not.exist();
expect("abc").to.be.empty();
expect("abcd").to.have.length(4);
expect(5).to.equal(5);
expect({ a: 1 }).to.equal({ a: 1 });
expect([1, 2, 3]).to.equal([1, 2, 3]);
expect(Object.create(null)).to.equal({}, { prototype: false });
expect(5).to.shallow.equal(5);
expect({ a: 1 }).to.shallow.equal({ a: 1 });
expect(10).to.be.above(5);
expect(10).to.be.at.least(10);
expect(10).to.be.below(20);
expect(10).to.be.at.most(10);
expect(10).to.be.within(10, 20);
expect(20).to.be.within(10, 20);
expect(15).to.be.between(10, 20);
expect(10).to.be.about(9, 1);
expect(new Date()).to.be.an.instanceof(Date);
expect("a5").to.match(/\w\d/);
expect(["abc", "def"]).to.match(/^[\w\d,]*$/);
expect(1).to.match(/^\d$/);
expect("x").to.satisfy(value => value === "x");
class CustomError extends Error {
call: (message: string) => Error;
}
const throws = () => {
throw new CustomError("Oh no!");
};
expect(throws).to.throw(CustomError, "Oh no!");
fail("This should not occur");
expect(count()).to.be.a.number();
expect(incomplete() as null).to.be.null().and.not.be.an.array();
const error = thrownAt(new Error("oops"));
expect(error).to.not.be.undefined();
expect(error.column).to.exist();
const foo = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
settings.truncateMessages = false;
expect<number[]>(foo).to.equal([]);
const bar = Object.create(null);
settings.comparePrototypes = false;
expect(bar).to.equal({});
const rejection = Promise.reject(new Error('Oh no!'));
/* await */ expect(rejection).to.reject('Oh no!');
/* await */ expect(rejection).rejects('Oh no!');
const typedRejection = Promise.reject(new CustomError('Oh no!'));
/* await */ expect(typedRejection).to.reject(CustomError, 'Oh no!');
/* await */ expect(typedRejection).rejects(CustomError, 'Oh no!');
+195
View File
@@ -0,0 +1,195 @@
// Type definitions for @hapi/code 5.3
// Project: https://github.com/hapijs/code
// Definitions by: Prashant Tiwari <https://github.com/prashaantt>
// Silas Rech <https://github.com/lenovouser>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
/** Generates an assertion object. */
export function expect<T>(value: T | T[], prefix?: string): AssertionChain<T>;
/** Makes the test fail with the given message. */
export function fail(message: string): void;
/** Returns the total number of assertions created using the expect() method. */
export function count(): number;
/** Returns an array of the locations where incomplete assertions were declared or null if no incomplete assertions found. */
export function incomplete(): string[] | null;
/** Returns the filename, line number, and column number of where the error was created. */
export function thrownAt(error?: Error): CodeError;
/** Configure code. */
export const settings: Settings;
export type AssertionChain<T> = Assertion<T> & Expectation<T>;
export type Assertion<T> = Grammar<T> & Flags<T>;
export type Expectation<T> = Types<T> & Values<T>;
export interface Grammar<T> {
/** Connecting word. */
a: AssertionChain<T>;
/** Connecting word. */
an: AssertionChain<T>;
/** Connecting word. */
and: AssertionChain<T>;
/** Connecting word. */
at: AssertionChain<T>;
/** Connecting word. */
be: AssertionChain<T>;
/** Connecting word. */
have: AssertionChain<T>;
/** Connecting word. */
in: AssertionChain<T>;
/** Connecting word. */
to: AssertionChain<T>;
}
export interface Flags<T> {
/** Inverses the expected result of any assertion */
not: AssertionChain<T>;
/**
* Requires that inclusion matches appear only once in the provided value.
* Used by include().
*/
once: AssertionChain<T>;
/**
* Requires that only the provided elements appear in the provided value.
* Used by include().
*/
only: AssertionChain<T>;
/**
* Allows a partial match when asserting inclusion
* Used by include(). Defaults to false.
*/
part: AssertionChain<T>;
/**
* Performs a comparison using strict equality (===).
* Code defaults to deep comparison. Used by equal() and include().
*/
shallow: AssertionChain<T>;
}
export interface Types<T> {
/** Asserts that the reference value is an arguments object. */
arguments(): AssertionChain<T>;
/** Asserts that the reference value is an Array. */
array(): AssertionChain<T>;
/** Asserts that the reference value is a boolean. */
boolean(): AssertionChain<T>;
/** Asserts that the reference value is a Buffer. */
buffer(): AssertionChain<T>;
/** Asserts that the reference value is a Date. */
date(): AssertionChain<T>;
/** Asserts that the reference value is an error. */
error(type?: any, message?: string | RegExp): AssertionChain<T>;
/** Asserts that the reference value is a function. */
function(): AssertionChain<T>;
/** Asserts that the reference value is a number. */
number(): AssertionChain<T>;
/** Asserts that the reference value is a RegExp. */
regexp(): AssertionChain<T>;
/** Asserts that the reference value is a string. */
string(): AssertionChain<T>;
/** Asserts that the reference value is an object (excluding array, buffer, or other native objects). */
object(): AssertionChain<T>;
}
export interface Values<T> {
/** Asserts that the reference value is true. */
true(): AssertionChain<T>;
/** Asserts that the reference value is false. */
false(): AssertionChain<T>;
/** Asserts that the reference value is null. */
null(): AssertionChain<T>;
/** Asserts that the reference value is undefined. */
undefined(): AssertionChain<T>;
/** Asserts that the reference value (a string, array, or object) includes the provided values. */
include(values: string | string[] | T | T[]): AssertionChain<T>;
/** Asserts that the reference value (a string, array, or object) includes the provided values. */
includes(values: string | string[] | T | T[]): AssertionChain<T>;
/** Asserts that the reference value (a string, array, or object) includes the provided values. */
contain(values: string | string[] | T | T[]): AssertionChain<T>;
/** Asserts that the reference value (a string, array, or object) includes the provided values. */
contains(values: string | string[] | T | T[]): AssertionChain<T>;
/** Asserts that the reference value (a string) starts with the provided value. */
startWith(value: string): AssertionChain<T>;
/** Asserts that the reference value (a string) starts with the provided value. */
startsWith(value: string): AssertionChain<T>;
/** Asserts that the reference value (a string) ends with the provided value. */
endWith(value: string): AssertionChain<T>;
/** Asserts that the reference value (a string) ends with the provided value. */
endsWith(value: string): AssertionChain<T>;
/** Asserts that the reference value exists (not null or undefined). */
exist(): AssertionChain<T>;
/** Asserts that the reference value exists (not null or undefined). */
exists(): AssertionChain<T>;
/** Asserts that the reference value has a length property equal to zero or an object with no keys. */
empty(): AssertionChain<T>;
/** Asserts that the reference value has a length property matching the provided size or an object with the specified number of keys. */
length(size: number): AssertionChain<T>;
/** Asserts that the reference value equals the provided value. */
equal(value: T | T[], options?: any): AssertionChain<T>;
/** Asserts that the reference value equals the provided value. */
equals(value: T | T[], options?: any): AssertionChain<T>;
/** Asserts that the reference value is greater than (>) the provided value. */
above(value: T): AssertionChain<T>;
/** Asserts that the reference value is greater than (>) the provided value. */
greaterThan(value: T): AssertionChain<T>;
/** Asserts that the reference value is at least (>=) the provided value. */
least(value: T): AssertionChain<T>;
/** Asserts that the reference value is at least (>=) the provided value. */
min(value: T): AssertionChain<T>;
/** Asserts that the reference value is less than (<) the provided value. */
below(value: T): AssertionChain<T>;
/** Asserts that the reference value is less than (<) the provided value. */
lessThan(value: T): AssertionChain<T>;
/** Asserts that the reference value is at most (<=) the provided value. */
most(value: T): AssertionChain<T>;
/** Asserts that the reference value is at most (<=) the provided value. */
max(value: T): AssertionChain<T>;
/** Asserts that the reference value is within (from <= value <= to) the provided values. */
within(from: T, to: T): AssertionChain<T>;
/** Asserts that the reference value is within (from <= value <= to) the provided values. */
range(from: T, to: T): AssertionChain<T>;
/** Asserts that the reference value is between but not equal (from < value < to) the provided values. */
between(from: T, to: T): AssertionChain<T>;
/** Asserts that the reference value is about the provided value within a delta margin of difference. */
about(value: number, delta: number): AssertionChain<T>;
/** Asserts that the reference value has the provided instanceof value. */
instanceof(type: any): AssertionChain<T>;
/** Asserts that the reference value has the provided instanceof value. */
instanceOf(type: any): AssertionChain<T>;
/** Asserts that the reference value's toString() representation matches the provided regular expression. */
match(regex: RegExp): AssertionChain<T>;
/** Asserts that the reference value's toString() representation matches the provided regular expression. */
matches(regex: RegExp): AssertionChain<T>;
/** Asserts that the Promise reference value rejects with an exception when called */
reject(type?: any, message?: string | RegExp): AssertionChain<T>;
/** Asserts that the Promise reference value rejects with an exception when called */
rejects(type?: any, message?: string | RegExp): AssertionChain<T>;
/** Asserts that the reference value satisfies the provided validator function. */
satisfy(validator: (value: T) => boolean): AssertionChain<T>;
/** Asserts that the reference value satisfies the provided validator function. */
satisfies(validator: (value: T) => boolean): AssertionChain<T>;
/** Asserts that the function reference value throws an exception when called. */
throw(type?: any, message?: string | RegExp): AssertionChain<T>;
/** Asserts that the function reference value throws an exception when called. */
throws(type?: any, message?: string | RegExp): AssertionChain<T>;
}
export interface Settings {
/**
* Truncate long assertion error messages for readability?
* Defaults to true.
*/
truncateMessages?: boolean;
/**
* Ignore object prototypes when doing a deep comparison?
* Defaults to false.
*/
comparePrototypes?: boolean;
}
export interface CodeError {
filename: string;
line: string;
column: string;
}
+28
View File
@@ -0,0 +1,28 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"baseUrl": "../",
"typeRoots": [
"../"
],
"paths": {
"@hapi/code": [
"hapi__code"
]
},
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"hapi__code-tests.ts"
]
}
+1
View File
@@ -0,0 +1 @@
{ "extends": "dtslint/dt.json" }
+106
View File
@@ -0,0 +1,106 @@
import {
Server,
Lifecycle,
} from '@hapi/hapi';
import * as path from 'path';
import * as inert from '@hapi/inert';
const server = new Server({
port: 3000,
routes: {
files: {
relativeTo: path.join(__dirname, 'public')
}
}
});
const provision = async () => {
await server.register(inert);
await server.register({
plugin: inert,
options: { etagsCacheMaxSize: 400 },
});
await server.register({
plugin: inert,
once: true,
});
server.route({
method: 'GET',
path: '/{param*}',
handler: {
directory: {
path: '.',
redirectToSlash: true,
index: true
}
}
});
// https://github.com/hapijs/inert#serving-a-single-file
server.route({
method: 'GET',
path: '/{path*}',
handler: {
file: 'page.html'
}
});
// https://github.com/hapijs/inert#customized-file-response
server.route({
method: 'GET',
path: '/file',
handler(request, reply) {
let path = 'plain.txt';
if (request.headers['x-magic'] === 'sekret') {
path = 'awesome.png';
}
return reply.file(path).vary('x-magic');
}
});
const handler: Lifecycle.Method = (request, h) => {
const response = request.response;
if (response instanceof Error && response.output.statusCode === 404) {
return h.file('404.html').code(404);
}
return h.continue;
};
server.ext('onPostHandler', handler);
const file: inert.FileHandlerRouteObject = {
path: '',
confine: true,
};
const directory: inert.DirectoryHandlerRouteObject = {
path: '',
listing: true
};
server.route({
path: '',
method: 'GET',
handler: {
file,
directory: {
path() {
if (Math.random() > 0.5) {
return '';
} else if (Math.random() > 0) {
return [''];
}
return new Error('');
},
BAD_listing: true,
},
},
options: { files: { relativeTo: __dirname } }
});
};
+169
View File
@@ -0,0 +1,169 @@
// Type definitions for @hapi/inert 5.2
// Project: https://github.com/hapijs/inert/
// Definitions by: Steve Ognibene <https://github.com/nycdotnet>
// Alexander James Phillips <https://github.com/AJamesPhillips>
// Silas Rech <https://github.com/lenovouser>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.8
import {
Plugin,
Request,
} from '@hapi/hapi';
declare namespace inert {
type RequestHandler<T> = (request: Request) => T;
interface ReplyFileHandlerOptions {
/**
* confine - serve file relative to this directory and returns 403 Forbidden if the path resolves outside the confine directory.
* Defaults to true which uses the relativeTo route option as the confine. Set to false to disable this security feature.
*/
confine?: boolean;
/**
* filename - an optional filename to specify if sending a 'Content-Disposition' header, defaults to the basename of path
*/
filename?: string;
/**
* mode - specifies whether to include the 'Content-Disposition' header with the response. Available values:
* * false - header is not included. This is the default value.
* * 'attachment'
* *'inline'
*/
mode?: false | 'attachment' | 'inline';
/**
* lookupCompressed - if true, looks for for a pre-compressed version of the file with the same filename with an extension, depending on the accepted encoding. Defaults to false.
*/
lookupCompressed?: boolean;
/**
* lookupMap - an object which maps content encoding to expected file name extension. Defaults to `{ gzip: '.gz' }.
*/
lookupMap?: {[index: string]: string};
/**
* etagMethod - specifies the method used to calculate the ETag header response. Available values:
* * 'hash' - SHA1 sum of the file contents, suitable for distributed deployments. Default value.
* * 'simple' - Hex encoded size and modification date, suitable when files are stored on a single server.
* * false - Disable ETag computation.
*/
etagMethod?: 'hash' | 'simple' | false;
/**
* start - offset in file to reading from, defaults to 0.
*/
start?: number;
/**
* end - offset in file to stop reading from. If not set, will read to end of file.
*/
end?: number;
}
interface FileHandlerRouteObject extends ReplyFileHandlerOptions {
/**
* path - a path string or function as described above (required).
*/
path: string | RequestHandler<string>;
}
interface DirectoryHandlerRouteObject {
/**
* path - (required) the directory root path (relative paths are resolved based on the route files configuration). Value can be:
* * a single path string used as the prefix for any resources requested by appending the request path parameter to the provided string.
* * an array of path strings. Each path will be attempted in order until a match is found (by following the same process as the single path string).
* * a function with the signature function(request) which returns the path string or an array of path strings.
* If the function returns an error, the error is passed back to the client in the response.
*/
path: string | string[] | RequestHandler<string | string[] | Error>;
/**
* index - optional boolean|string|string[], determines if an index file will be served if found in the folder when requesting a directory.
* The given string or strings specify the name(s) of the index file to look for. If true, looks for 'index.html'.
* Any falsy value disables index file lookup. Defaults to true.
*/
index?: boolean | string | string[];
/**
* listing - optional boolean, determines if directory listing is generated when a directory is requested without an index document. Defaults to false.
*/
listing?: boolean;
/**
* showHidden - optional boolean, determines if hidden files will be shown and served. Defaults to false.
*/
showHidden?: boolean;
/**
* redirectToSlash - optional boolean, determines if requests for a directory without a trailing slash are redirected to the same path with the missing slash.
* Useful for ensuring relative links inside the response are resolved correctly.
* Disabled when the server config router.stripTrailingSlash is true.Defaults to false.
*/
redirectToSlash?: boolean;
/**
* lookupCompressed - optional boolean, instructs the file processor to look for the same filename with the '.gz' suffix for a pre-compressed
* version of the file to serve if the request supports content encoding. Defaults to false.
*/
lookupCompressed?: boolean;
/**
* etagMethod - specifies the method used to calculate the ETag header response. Available values:
* * 'hash' - SHA1 sum of the file contents, suitable for distributed deployments. Default value.
* * 'simple' - Hex encoded size and modification date, suitable when files are stored on a single server.
* * false - Disable ETag computation.
*/
etagMethod?: 'hash' | 'simple' | false;
/**
* defaultExtension - optional string, appended to file requests if the requested file is not found. Defaults to no extension.
*/
defaultExtension?: string;
}
/**
* inert accepts the following registration options
* @see {@link https://github.com/hapijs/inert#registration-options}
*/
interface OptionalRegistrationOptions {
/**
* sets the maximum number of file etag hash values stored in the etags cache. Defaults to 10000.
*/
etagsCacheMaxSize?: number;
}
}
declare const inert: Plugin<inert.OptionalRegistrationOptions>;
export = inert;
declare module '@hapi/hapi' {
interface HandlerDecorations {
/**
* The file handler
*
* Generates a static file endpoint for serving a single file. file can be set to:
* * a relative or absolute file path string (relative paths are resolved based on the route files configuration).
* * a function with the signature function(request) which returns the relative or absolute file path.
* * an object with one or more of the following options @see IFileHandler
* @see {@link https://github.com/hapijs/inert#the-file-handler}
*/
file?: string | inert.RequestHandler<string> | inert.FileHandlerRouteObject;
/**
* The directory handler
*
* Generates a directory endpoint for serving static content from a directory.
* Routes using the directory handler must include a path parameter at the end of the path string
* (e.g. /path/to/somewhere/{param} where the parameter name does not matter).
* The path parameter can use any of the parameter options (e.g. {param} for one level files only,
* {param?} for one level files or the directory root, {param*} for any level, or {param*3} for a specific level).
* If additional path parameters are present, they are ignored for the purpose of selecting the file system resource.
* The directory handler is an object with the following options:
* @see {@link https://github.com/hapijs/inert#the-directory-handler}
*/
directory?: inert.DirectoryHandlerRouteObject;
files?: {
/**
* Set the relative path
*/
relativeTo: string;
};
}
interface ResponseToolkit {
/**
* Transmits a file from the file system. The 'Content-Type' header defaults to the matching mime type based on filename extension.
* @see {@link https://github.com/hapijs/inert#replyfilepath-options}
*/
file(path: string, options?: inert.ReplyFileHandlerOptions): ResponseObject;
}
}
+52
View File
@@ -0,0 +1,52 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"baseUrl": "../",
"typeRoots": [
"../"
],
"paths": {
"@hapi/hapi": [
"hapi__hapi"
],
"@hapi/boom": [
"hapi__boom"
],
"@hapi/shot": [
"hapi__shot"
],
"@hapi/mimos": [
"hapi__mimos"
],
"@hapi/iron": [
"hapi__iron"
],
"@hapi/joi": [
"hapi__joi"
],
"@hapi/podium": [
"hapi__podium"
],
"@hapi/catbox": [
"hapi__catbox"
],
"@hapi/inert": [
"hapi__inert"
]
},
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"hapi__inert-tests.ts"
]
}
+1
View File
@@ -0,0 +1 @@
{ "extends": "dtslint/dt.json" }
+169
View File
@@ -0,0 +1,169 @@
import { script, assertions } from "@hapi/lab";
const { experiment, describe, suite, test, it, before, beforeEach, after, afterEach } = script();
const expect = assertions.expect;
const fail = assertions.fail;
experiment('math', () => {
before((done) => {
setTimeout(() => {
done();
}, 1000);
});
beforeEach((done) => {
done();
});
test('returns true when 1 + 1 equals 2', (done) => {
expect(1 + 1).to.equal(2);
done();
});
});
experiment('math', () => {
before(() => {
return Promise.resolve();
});
test('returns true when 1 + 1 equals 2', () => {
return Promise.resolve()
.then((aValue) => {
const expectedValue = aValue;
expect(aValue).to.equal(expectedValue);
});
});
});
experiment.only('with only experiment', () => {
test('this test will run', (done) => {
expect(1 + 1).to.equal(2);
done();
});
test('another test that will run', (done) => {
expect(true).to.equal(true);
done();
});
});
experiment('with only test', () => {
test.only('only this test will run', (done) => {
expect(1 + 1).to.equal(2);
done();
});
test('another test that will not be executed', (done) => {
done();
});
});
test('attaches notes', (done) => {
expect(1 + 1).to.equal(2);
done.note(`The current time is ${Date.now()}`);
done();
});
test('cleanups after test', (done, onCleanup) => {
if (onCleanup) {
onCleanup((next) => {
return next();
});
}
expect(1 + 1).to.equal(2);
done();
});
experiment('my plan', () => {
test('only a single assertion executes', { plan: 1 }, (done) => {
expect(1 + 1).to.equal(2);
done();
});
});
experiment('math', { timeout: 1000 }, () => {
before({ timeout: 500 }, (done) => {
done();
});
test('returns true when 1 + 1 equals 2', { parallel: true }, (done) => {
expect(1 + 1).to.equal(2);
done();
});
});
describe('math', () => {
before((done) => {
done();
});
after((done) => {
done();
});
afterEach((done) => {
done();
});
it('returns true when 1 + 1 equals 2', (done) => {
expect(1 + 1).to.equal(2);
done();
});
});
suite('math', () => {
test('returns true when 1 + 1 equals 2', (done) => {
expect(1 + 1).to.equal(2);
done();
});
});
describe('expectation', () => {
it('should be able to expect', (done) => {
expect(true).to.be.true();
done();
});
it('should be able to fail (This test should fail)', (done) => {
fail('Should fail');
done();
});
});
+188
View File
@@ -0,0 +1,188 @@
// Type definitions for @hapi/lab 18.1
// Project: https://github.com/hapijs/lab
// Definitions by: Prashant Tiwari <https://github.com/prashaantt>
// Silas Rech <https://github.com/lenovouser>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
/** The test script. */
export function script(options?: ScriptOptions): Lab & ExperimentAlt & TestAlt;
/** Access the configured assertion library. */
export const assertions: any;
interface Lab {
/** Organise tests into an experiment */
experiment(desc: string, cb: EmptyCallback): void;
/** Organise tests into an experiment with options */
experiment(desc: string, options: ExperimentOptions, cb: EmptyCallback): void;
/** Create a test suite */
describe(desc: string, cb: EmptyCallback): void;
/** Create a test suite with options */
describe(desc: string, options: ExperimentOptions, cb: EmptyCallback): void;
/** Create a test suite */
suite(desc: string, cb: EmptyCallback): void;
/** Create a test suite with options */
suite(desc: string, options: ExperimentOptions, cb: EmptyCallback): void;
/** The test spec */
test(desc: string, cb: TestCallback): void;
/** The test spec using a promise */
test(desc: string, promise: TestPromise): void;
/** The test spec with options */
test(desc: string, options: TestOptions, cb: TestCallback): void;
/** The test spec using a promise with options */
test(desc: string, options: TestOptions, promise: TestPromise): void;
/** The test spec */
it(desc: string, cb: TestCallback): void;
/** The test spec using a promise */
it(desc: string, promise: TestPromise): void;
/** The test spec with options */
it(desc: string, options: TestOptions, cb: TestCallback): void;
/** The test spec using a promise with options */
it(desc: string, options: TestOptions, promise: TestPromise): void;
/** Perform async actions before the test suite */
before(cb: AsyncCallback): void;
/** Perform async actions before the test suite using a promise */
before(promise: AsyncPromise): void;
/** Perform async actions before the test suite with options */
before(options: AsyncOptions, cb: AsyncCallback): void;
/** Perform async actions before the test suite with otions, using a promise */
before(options: AsyncOptions, promise: AsyncPromise): void;
/** Perform async actions before each test */
beforeEach(cb: AsyncCallback): void;
/** Perform async actions before each test using a promise */
beforeEach(promise: AsyncPromise): void;
/** Perform async actions before each test with options */
beforeEach(options: AsyncOptions, cb: AsyncCallback): void;
/** Perform async actions before each test with options, using a promise */
beforeEach(options: AsyncOptions, promise: AsyncPromise): void;
/** Perform async actions after the test suite */
after(cb: AsyncCallback): void;
/** Perform async actions after the test suite using a promise */
after(promise: AsyncPromise): void;
/** Perform async actions after the test suite with options */
after(options: AsyncOptions, cb: AsyncCallback): void;
/** Perform async actions after the test suite with options, using a promise */
after(options: AsyncOptions, promise: AsyncPromise): void;
/** Perform async actions after each test */
afterEach(cb: AsyncCallback): void;
/** Perform async actions after each test using a promise */
afterEach(promise: AsyncPromise): void;
/** Perform async actions after each test with options */
afterEach(options: AsyncOptions, cb: AsyncCallback): void;
/** Perform async actions after each test with options, using a promise */
afterEach(options: AsyncOptions, promise: AsyncPromise): void;
}
interface ExperimentAlt {
experiment: SkipOnlyExperiment;
suite: SkipOnlyExperiment;
describe: SkipOnlyExperiment;
}
interface TestAlt {
test: SkipOnlyTest;
it: SkipOnlyTest;
}
interface SkipOnlyExperiment {
/** Skip this test suite */
skip: ExperimentArgs & ExperimentWithOptionsArgs;
/** Only execute this test suite */
only: ExperimentArgs & ExperimentWithOptionsArgs;
}
interface SkipOnlyTest {
/** Skip this test */
skip: TestArgs & TestWithOptionsArgs;
/** Only execute this test */
only: TestArgs & TestWithOptionsArgs;
}
interface ScriptOptions {
/** Enable auto-execution of the script? (true) */
schedule?: boolean;
/** Pass Lab CLI options */
cli?: any;
}
interface ExperimentOptions {
/** Set a specific timeout in milliseconds (2000) */
timeout?: number;
/** Execute tests in parallel? (false) */
parallel?: boolean;
/** Skip execution? (false) */
skip?: boolean;
/** Execute only this test/experiment? (false) */
only?: boolean;
}
interface TestOptions extends ExperimentOptions {
/** The expected number of assertions to execute */
plan?: number;
}
interface AsyncOptions {
/** Set a specific timeout in milliseconds (disabled) */
timeout?: number;
}
interface DoneNote {
/** Attach a note to the test case */
note: (text: string) => void;
}
type EmptyCallback = () => void;
type DoneFunction = (err?: Error) => void;
type CleanupFunction = (func: (next: Function) => void) => void;
type TestCallback = (done: DoneFunction & DoneNote, onCleanup?: CleanupFunction) => void;
type TestPromise = () => Promise<any>;
type AsyncCallback = (done: DoneFunction) => void;
type AsyncPromise = () => Promise<any>;
type ExperimentArgs = (desc: string, cb: EmptyCallback) => {};
type ExperimentWithOptionsArgs = (desc: string, options: ExperimentOptions, cb: EmptyCallback) => {};
type TestArgs = (desc: string, cb: TestCallback) => {};
type TestWithOptionsArgs = (desc: string, options: TestOptions, cb: TestCallback) => {}
+29
View File
@@ -0,0 +1,29 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6",
"dom"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"baseUrl": "../",
"typeRoots": [
"../"
],
"paths": {
"@hapi/lab": [
"hapi__lab"
]
},
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"hapi__lab-tests.ts"
]
}
+80
View File
@@ -0,0 +1,80 @@
{
"extends": "dtslint/dt.json",
"rules": {
"adjacent-overload-signatures": false,
"array-type": false,
"arrow-return-shorthand": false,
"ban-types": false,
"callable-types": false,
"comment-format": false,
"dt-header": false,
"npm-naming": false,
"eofline": false,
"export-just-namespace": false,
"import-spacing": false,
"interface-name": false,
"interface-over-type-literal": false,
"jsdoc-format": false,
"max-line-length": false,
"member-access": false,
"new-parens": false,
"no-any-union": false,
"no-boolean-literal-compare": false,
"no-conditional-assignment": false,
"no-consecutive-blank-lines": false,
"no-construct": false,
"no-declare-current-package": false,
"no-duplicate-imports": false,
"no-duplicate-variable": false,
"no-empty-interface": false,
"no-for-in-array": false,
"no-inferrable-types": false,
"no-internal-module": false,
"no-irregular-whitespace": false,
"no-mergeable-namespace": false,
"no-misused-new": false,
"no-namespace": false,
"no-object-literal-type-assertion": false,
"no-padding": false,
"no-redundant-jsdoc": false,
"no-redundant-jsdoc-2": false,
"no-redundant-undefined": false,
"no-reference-import": false,
"no-relative-import-in-test": false,
"no-self-import": false,
"no-single-declare-module": false,
"no-string-throw": false,
"no-unnecessary-callback-wrapper": false,
"no-unnecessary-class": false,
"no-unnecessary-generics": false,
"no-unnecessary-qualifier": false,
"no-unnecessary-type-assertion": false,
"no-useless-files": false,
"no-var-keyword": false,
"no-var-requires": false,
"no-void-expression": false,
"no-trailing-whitespace": false,
"object-literal-key-quotes": false,
"object-literal-shorthand": false,
"one-line": false,
"one-variable-per-declaration": false,
"only-arrow-functions": false,
"prefer-conditional-expression": false,
"prefer-const": false,
"prefer-declare-function": false,
"prefer-for-of": false,
"prefer-method-signature": false,
"prefer-template": false,
"radix": false,
"semicolon": false,
"space-before-function-paren": false,
"space-within-parens": false,
"strict-export-declare-modifiers": false,
"trim-file": false,
"triple-equals": false,
"typedef-whitespace": false,
"unified-signatures": false,
"void-return": false,
"whitespace": false
}
}
+81
View File
@@ -0,0 +1,81 @@
import {
Server,
Request,
ResponseToolkit,
} from '@hapi/hapi';
import * as Vision from '@hapi/vision';
import * as Handlebars from 'handlebars';
const server = new Server({
port: 80,
});
const provision = async () => {
await server.register({
plugin: Vision,
options: {
engines: { hbs: Handlebars },
path: __dirname + '/templates',
}
});
server.views({
engines: { hbs: Handlebars },
path: __dirname + '/templates',
});
const context = {
title: 'Views Example',
message: 'Hello, World',
};
console.log(await server.render('hello', context));
server.route({
method: 'GET',
path: '/view',
handler: async (request: Request, h: ResponseToolkit) => {
return request.render('test', { message: 'hello' });
},
});
server.route({
method: 'GET',
path: '/',
handler: {
view: {
template: 'hello',
context: {
title: 'Views Example',
message: 'Hello, World',
},
},
},
});
const handler = (request: Request, h: ResponseToolkit) => {
const context = {
title: 'Views Example',
message: 'Hello, World',
};
return h.view('hello', context);
};
server.route({ method: 'GET', path: '/', handler });
server.route({
method: 'GET',
path: '/temp1',
handler: {
view: {
template: 'temp1',
options: {
compileOptions: {
noEscape: true,
},
},
},
},
});
};
+268
View File
@@ -0,0 +1,268 @@
// Type definitions for @hapi/vision 5.5
// Project: https://github.com/hapijs/vision
// Definitions by: Jason Swearingen <https://github.com/jasonswearingen>
// Alexander James Phillips <https://github.com/AJamesPhillips>
// Silas Rech <https://github.com/lenovouser>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.8
import {
Plugin,
Request,
ResponseObject,
} from '@hapi/hapi';
declare namespace vision {
interface EnginesConfiguration {
/**
* Required object where each key is a file extension (e.g. 'html', 'hbr'), mapped to the npm module used for rendering the templates.
* Alternatively, the extension can be mapped to an object
*/
engines: {[fileExtension: string]: NpmModule} | ServerViewsEnginesOptions;
/** defines the default filename extension to append to template names when multiple engines are configured and no explicit extension is provided for a given template. No default value. */
defaultExtension?: string;
}
/**
* Includes `module` and any of the views options listed below (@see ServerViewsAdditionalOptions) (except defaultExtension) to override the defaults for a specific engine.
*/
interface ServerViewsEnginesOptions extends ServerViewsConfiguration {
/**
* The npm module used for rendering the templates. The module object must contain the compile() function
* @see {@link https://github.com/hapijs/vision/blob/master/API.md#serverviewsoptions} > options > engines > module
*/
module: NpmModule;
}
interface ServerViewsConfiguration extends ViewHandlerOrReplyOptions, EnginesConfiguration {
/**
* The root file path, or array of file paths, where partials are located.
* Partials are small segments of template code that can be nested and reused throughout other templates.
* Defaults to no partials support (empty path).
*/
partialsPath?: string | string[];
/**
* The directory path, or array of directory paths, where helpers are located.
* Helpers are functions used within templates to perform transformations and other data manipulations using the template context or other inputs.
* Each '.js' file in the helpers directory is loaded and the file name is used as the helper name.
* The files must export a single method with the signature function(context) and return a string.
* Sub-folders are not supported and are ignored. Defaults to no helpers support (empty path).
* Note that jade does not support loading helpers this way.
*/
helpersPath?: string | string[];
/** if set to false, templates will not be cached (thus will be read from file on every use). Defaults to true. */
isCached?: boolean;
}
/**
*
* @see {@link https://github.com/hapijs/vision/blob/master/API.md#the-view-handler} > options for the list of attributes it can not have (isCached, partialsPath, helpersPath)
*/
interface ViewHandlerOrReplyOptions {
/** the root file path, or array of file paths, used to resolve and load the templates identified when calling reply.view(). Defaults to current working directory. */
path?: string | string[];
/**
* a base path used as prefix for path and partialsPath. No default.
*/
relativeTo?: string;
/**
* If set to true or a layout filename, layout support is enabled.
* A layout is a single template file used as the parent template for other view templates in the same engine.
* If true, the layout template name must be 'layout.ext' where 'ext' is the engine's extension.
* Otherwise, the provided filename is suffixed with the engine's extension and loaded.
* Disable layout when using Jade as it will handle including any layout files independently.
* Defaults to false.
*/
layout?: boolean | string;
/** the root file path, or array of file paths, where layout templates are located (using the relativeTo prefix if present). Defaults to path. */
layoutPath?: string | string[];
/** the key used by the template engine to denote where primary template content should go. Defaults to 'content'. */
layoutKeyword?: string;
/** the text encoding used by the templates when reading the files and outputting the result. Defaults to 'utf8'. */
encoding?: string;
/** if set to true, allows absolute template paths passed to reply.view(). Defaults to false. */
allowAbsolutePaths?: boolean;
/** if set to true, allows template paths passed to reply.view() to contain '../'. Defaults to false. */
allowInsecureAccess?: boolean;
/** options object passed to the engine's compile function. Defaults to empty options {}. */
compileOptions?: CompileOptions;
/** options object passed to the returned function from the compile operation. Defaults to empty options {}. */
runtimeOptions?: RuntimeOptions;
/** the content type of the engine results. Defaults to 'text/html'. */
contentType?: string;
/** specify whether the engine compile() method is 'sync' or 'async'. Defaults to 'sync'. */
compileMode?: 'sync' | 'async';
/**
* A global context used with all templates.
* The global context option can be either an object or a function that takes the request as its only argument and returns a context object.
* The request object is only provided when using the view handler or reply.view().
* When using server.render() or request.render(), the request argument will be null.
* When rendering views, the global context will be merged with any context object specified on the handler or using reply.view().
* When multiple context objects are used, values from the global context always have lowest precedence.
*/
context?: object | ((request: Request) => object);
}
/**
* Options passed to module when compiling template.
* Cast your options to this interface or extend it with:
* declare module '@hapi/hapi' {
* interface CompileOptions {
* noEscape: boolean;
* }
* }
*/
type CompileOptions = object;
type RuntimeOptions = object;
/**
* The rendering function. The required function signature depends on the compileMode settings (see below).
*
* If compileMode is 'sync', the signature is compile(template, options),
* the return value is a function with signature function(context, options) (the compiled sync template),
* and the method is allowed to throw errors.
*
* If compileMode is 'async',
* the signature is compile(template, options, next) where next has the signature function(err, compiled),
* compiled is a function with signature function(context, options, callback) (the compiled async template)
* and callback has the signature function(err, rendered).
*/
type ServerViewCompileSync = (template: string, options: any) => (context: any, options: any) => void;
type ServerViewCompileAsync = (template: string, options: any, next: ServerViewCompileNext) => void;
type ServerViewCompile = ServerViewCompileSync | ServerViewCompileAsync;
type ServerViewCompileNext = (err: Error | null, compiled: (context: any, options: any, callback: (err: null | Error, rendered: string | null) => void) => void) => void;
/**
* The npm module used for rendering the templates. The module object must contain the compile() function
* @see {@link https://github.com/hapijs/vision/blob/master/API.md#serverviewsoptions} > options > engines > module
*/
interface NpmModule {
/**
* The rendering function. The required function signature depends on the compileMode settings
*/
compile: ServerViewCompile;
/**
* Initializes additional engine state.The config object is the engine configuration object allowing updates to be made.
* This is useful for engines like Nunjucks that rely on additional state for rendering. next has the signature function(err).
*/
prepare?(config: EngineConfigurationObject, next: (err?: Error) => void): void;
/**
* Registers a partial for use during template rendering.
* The name is the partial path that templates should use to reference the partial and src is the uncompiled template string for the partial.
*/
registerPartial?(name: string, src: string): void;
/**
* Registers a helper for use during template rendering.
* The name is the name that templates should use to reference the helper and helper is the function that will be invoked when the helper is called.
*/
registerHelper?(name: string, helper: (...args: any[]) => any): void;
}
type EngineConfigurationObject = object;
/**
* Renders a template
* @param template - the template filename and path, relative to the views manager templates path (path or relativeTo).
* @param context - optional object used by the template to render context-specific result. Defaults to no context ({}).
* @param options - optional object used to override the views manager configuration.
*/
type RenderMethod = (template: string, context?: any, options?: ServerViewsConfiguration) => Promise<string>;
/**
* View Manager
* @see {@link https://github.com/hapijs/vision/blob/master/API.md#view-manager}
*/
interface ViewManager {
/**
* Registers a helper, on all configured engines that have a registerHelper() method, for use during template rendering.
* Engines without a registerHelper() method will be skipped.
* The name is the name that templates should use to reference the helper and helper is the function that will be invoked when the helper is called.
* @param name
* @param helper
* @see {@link https://github.com/hapijs/vision/blob/master/API.md#managerregisterhelpername-helper}
*/
registerHelper(name: string, helper: (...args: any[]) => any): void;
/**
* Renders a template. This is typically not needed and it is usually more convenient to use server.render().
* @see {@link https://github.com/hapijs/vision/blob/master/API.md#managerrendertemplate-context-options-callback}
*/
render: RenderMethod;
}
}
declare const vision: Plugin<vision.ServerViewsConfiguration>;
export = vision;
declare module '@hapi/hapi' {
interface Server {
/**
* Initializes the server views manager
* @see {@link https://github.com/hapijs/vision/blob/master/API.md#serverviewsoptions}
*/
views(options: vision.ServerViewsConfiguration): vision.ViewManager;
/**
* Utilizes the server views manager to render a template
* @see {@link https://github.com/hapijs/vision/blob/master/API.md#serverrendertemplate-context-options-callback}
*/
render: vision.RenderMethod;
}
}
declare module '@hapi/hapi' {
interface Request {
/**
* request.render() works the same way as server.render() but is for use inside of request handlers.
* server.render() does not work inside request handlers when called via request.server.render() if the view manager was created by a plugin.
* This is because the request.server object does not have access to the plugin realm where the view manager was configured.
* request.render() gets its realm from the route that the request was bound to.
* Note that this will not work in onRequest extensions added by the plugin because the route isn't yet set at this point in the request
* lifecycle and the request.render() method will produce the same limited results server.render() can.
* @see {@link https://github.com/hapijs/vision/blob/master/API.md#requestrendertemplate-context-options-callback}
*/
render: vision.RenderMethod;
}
}
declare module '@hapi/hapi' {
interface ResponseToolkit {
/**
* Concludes the handler activity by returning control over to the router with a templatized view response
* Returns a response object. The generated response will have the variety property set to view.
* The response flow control rules apply.
* @param template the template filename and path, relative to the templates path configured via the server views manager.
* @param context optional object used by the template to render context-specific result. Defaults to no context {}.
* @param options optional object used to override the server's views manager configuration for this response.
* Cannot override isCached, partialsPath, or helpersPath which are only loaded at initialization.
* @see {@link https://github.com/hapijs/vision/blob/master/API.md#replyviewtemplate-context-options}
*/
view(templatePath: string, context?: any, options?: vision.ViewHandlerOrReplyOptions): ResponseObject;
}
}
declare module '@hapi/hapi' {
interface HandlerDecorations {
/**
* The view handler can be used with routes registered in the same realm as the view manager.
* The handler takes an options parameter that can be either a string or an object.
* When the options parameter is a string, it should be the filename and path of the template relative
* to the templates path configured via the views manager. When the options parameter is an object, it may have the following keys:
* The rendering context contains the `params`, `payload`, `query`, and `pre` values from the request by default
* (these can be overriden by values explicitly set via the options).
* @see {@link https://github.com/hapijs/vision/blob/master/API.md#the-view-handler}
*/
view?: string | {
/** the template filename and path, relative to the templates path configured via the server views manager. */
template: string;
/** optional object used by the template to render context-specific result. Defaults to no context {}. */
context?: object;
/**
* optional object used to override the server's views manager configuration for this response.
* Cannot override isCached, partialsPath, or helpersPath which are only loaded at initialization.
* TODO check if it can have `defaultExtension`.
*/
options?: vision.ViewHandlerOrReplyOptions;
};
}
}
+6
View File
@@ -0,0 +1,6 @@
{
"private": true,
"dependencies": {
"handlebars": "^4.1"
}
}
+52
View File
@@ -0,0 +1,52 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"baseUrl": "../",
"typeRoots": [
"../"
],
"paths": {
"@hapi/hapi": [
"hapi__hapi"
],
"@hapi/boom": [
"hapi__boom"
],
"@hapi/shot": [
"hapi__shot"
],
"@hapi/mimos": [
"hapi__mimos"
],
"@hapi/iron": [
"hapi__iron"
],
"@hapi/joi": [
"hapi__joi"
],
"@hapi/podium": [
"hapi__podium"
],
"@hapi/catbox": [
"hapi__catbox"
],
"@hapi/vision": [
"hapi__vision"
]
},
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"hapi__vision-tests.ts"
]
}
+1
View File
@@ -0,0 +1 @@
{ "extends": "dtslint/dt.json" }