From 8df5b79728eddc9d790af83f068333ee2eca17aa Mon Sep 17 00:00:00 2001 From: Offir Golan Date: Tue, 24 Sep 2019 22:06:42 +0200 Subject: [PATCH] @pollyjs/core - Improve on previous types and update to match v2.6.2 (#38554) --- types/pollyjs__core/index.d.ts | 305 +++++++++++--------- types/pollyjs__core/pollyjs__core-tests.ts | 308 ++++++++++++--------- types/pollyjs__utils/index.d.ts | 18 +- 3 files changed, 359 insertions(+), 272 deletions(-) diff --git a/types/pollyjs__core/index.d.ts b/types/pollyjs__core/index.d.ts index ece0f4a71d..f3ac40be71 100644 --- a/types/pollyjs__core/index.d.ts +++ b/types/pollyjs__core/index.d.ts @@ -2,180 +2,209 @@ // Project: https://github.com/netflix/pollyjs/tree/master/packages/@pollyjs/core // Definitions by: feinoujc // Borui Gu +// Offir Golan // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped // TypeScript Version: 2.4 -import { MODES } from '@pollyjs/utils'; import Adapter from '@pollyjs/adapter'; import Persister from '@pollyjs/persister'; +export type MODE = 'record' | 'replay' | 'passthrough' | 'stopped'; +export type ACTION = 'record' | 'replay' | 'intercept' | 'passthrough'; +export type EXPIRY_STRATEGY = 'record' | 'warn' | 'error'; + export const Timing: { - fixed(ms: number): () => Promise; - relative(ratio: number): (ms: number) => Promise; + fixed(ms: number): () => Promise; + relative(ratio: number): (ms: number) => Promise; }; export type MatchBy = (input: T) => R; +export type Headers = Record; export interface PollyConfig { - mode?: MODES | string; + mode?: MODE; - adapters?: Array; - adapterOptions?: any; + adapters?: Array; + adapterOptions?: { + fetch?: { context?: any }; + puppeteer?: { page?: any }; + xhr?: { context?: any }; + [key: string]: any; + }; - persister?: string | typeof Persister; - persisterOptions?: { - keepUnusedRequests?: boolean; - [key: string]: any; - }; + persister?: string | typeof Persister; + persisterOptions?: { + keepUnusedRequests?: boolean; + fs?: { recordingsDir?: string }; + 'local-storage'?: { context?: any; key?: string }; + rest?: { host?: string; apiNamespace?: string }; + [key: string]: any; + }; - logging?: boolean; + logging?: boolean; - recordIfMissing?: boolean; - /** @deprecated use expiryStrategy */ - recordIfExpired?: boolean; - recordFailedRequests?: boolean; - expiryStrategy?: 'warn' | 'error' | 'record'; + recordIfMissing?: boolean; + /** @deprecated use expiryStrategy */ + recordIfExpired?: boolean; + recordFailedRequests?: boolean; + expiryStrategy?: EXPIRY_STRATEGY; - expiresIn?: string | null; - timing?: ((ms: number) => Promise) | (() => Promise); + expiresIn?: string | null; + timing?: ((ms: number) => Promise) | (() => Promise); - matchRequestsBy?: { - method?: boolean | MatchBy; - headers?: boolean | { exclude: string[] } | MatchBy>; - body?: boolean | MatchBy; - order?: boolean; + matchRequestsBy?: { + method?: boolean | MatchBy; + headers?: boolean | { exclude: string[] } | MatchBy; + body?: boolean | MatchBy; + order?: boolean; - url?: { - protocol?: boolean | MatchBy; - username?: boolean | MatchBy; - password?: boolean | MatchBy; - hostname?: boolean | MatchBy; - port?: boolean | MatchBy; - pathname?: boolean | MatchBy; - query?: boolean | MatchBy; - hash?: boolean | MatchBy; - }; - }; + url?: { + protocol?: boolean | MatchBy; + username?: boolean | MatchBy; + password?: boolean | MatchBy; + hostname?: boolean | MatchBy; + port?: boolean | MatchBy; + pathname?: boolean | MatchBy; + query?: boolean | MatchBy; + hash?: boolean | MatchBy; + }; + }; } -export interface Request { - getHeader(name: string): string | null; - setHeader(name: string, value?: string | null): Request; - setHeaders(headers: Record): Request; - removeHeader(name: string): Request; - removeHeaders(headers: string[]): Request; - hasHeader(name: string): boolean; - type(contentType: string): Request; - send(body: any): Request; - json(body: any): Request; - jsonBody(): any; +export interface HTTPBase { + headers: Headers; + body: any; - method: string; - url: string; - protocol: string; - hostname: string; - port: string; - pathname: string; - hash: string; - headers: Record; - body: any; - query: any; - params: any; - recordingName: string; - responseTime?: number; + getHeader(name: string): string | string[] | null; + setHeader(name: string, value?: string | string[] | null): this; + setHeaders(headers: Headers): this; + removeHeader(name: string): this; + removeHeaders(headers: string[]): this; + hasHeader(name: string): boolean; + type(contentType: string): this; + send(body: any): this; + json(body: any): this; + jsonBody(): any; } -export interface Response { - statusCode?: number; - headers: Record; - body: any; - status(status: number): Response; - getHeader(name: string): string | null; - setHeader(name: string, value?: string | null): Response; - setHeaders(headers: Record): Response; - removeHeader(name: string): Request; - removeHeaders(headers: string[]): Request; - hasHeader(name: string): boolean; - type(contentType: string): Response; - send(body: any): Response; - sendStatus(status: number): Response; - json(body: any): Response; - jsonBody(): any; - end(): Readonly; + +export interface Request extends HTTPBase { + method: string; + url: string; + readonly absoluteUrl: string; + readonly protocol: string; + readonly hostname: string; + readonly port: string; + readonly pathname: string; + hash: string; + query: { [key: string]: string | string[] }; + readonly params: { [key: string]: string }; + recordingName: string; + responseTime?: number; + timestamp?: string; + didRespond: boolean; + id?: string; + order?: number; + action: ACTION | null; } -export interface Intercept { - abort(): void; - passthrough(): void; +export interface Response extends HTTPBase { + statusCode: number; + readonly statusText: string; + readonly ok: boolean; + + status(status: number): this; + sendStatus(status: number): this; + end(): Readonly; +} +export interface Interceptor { + abort(): void; + passthrough(): void; } export type RequestRouteEvent = 'request'; export type RecordingRouteEvent = 'beforeReplay' | 'beforePersist'; export type ResponseRouteEvent = 'beforeResponse' | 'response'; export type ErrorRouteEvent = 'error'; -export type EventListenerResponse = any; -export type ErrorEventListener = (req: Request, error: any) => EventListenerResponse; -export type RequestEventListener = (req: Request) => EventListenerResponse; -export type RecordingEventListener = (req: Request, recording: any) => EventListenerResponse; -export type ResponseEventListener = (req: Request, res: Response) => EventListenerResponse; -export type InterceptHandler = ( - req: Request, - res: Response, - intercept: Intercept -) => EventListenerResponse; +export interface ListenerEvent { + readonly type: string; + stopPropagation: () => void; +} +export type ErrorEventListener = (req: Request, error: any, event: ListenerEvent) => void | Promise; +export type RequestEventListener = (req: Request, event: ListenerEvent) => void | Promise; +export type RecordingEventListener = (req: Request, recording: any, event: ListenerEvent) => void | Promise; +export type ResponseEventListener = (req: Request, res: Response, event: ListenerEvent) => void | Promise; +export type InterceptHandler = (req: Request, res: Response, interceptor: Interceptor) => void | Promise; export class RouteHandler { - on(event: RequestRouteEvent, listener: RequestEventListener): RouteHandler; - on(event: RecordingRouteEvent, listener: RecordingEventListener): RouteHandler; - on(event: ResponseRouteEvent, listener: ResponseEventListener): RouteHandler; - on(event: ErrorRouteEvent, listener: ErrorEventListener): RouteHandler; - off(event: RequestRouteEvent, listener: RequestEventListener): RouteHandler; - off(event: RecordingRouteEvent, listener: RecordingEventListener): RouteHandler; - off(event: ResponseRouteEvent, listener: ResponseEventListener): RouteHandler; - off(event: ErrorRouteEvent, listener: ErrorEventListener): RouteHandler; - once(event: RequestRouteEvent, listener: RequestEventListener): RouteHandler; - once(event: RecordingRouteEvent, listener: RecordingEventListener): RouteHandler; - once(event: ResponseRouteEvent, listener: ResponseEventListener): RouteHandler; - once(event: ErrorRouteEvent, listener: ErrorEventListener): RouteHandler; - filter: (callback: (req: Request) => boolean) => RouteHandler; - passthrough(value?: boolean): RouteHandler; - intercept( - fn: (req: Request, res: Response, intercept: Intercept) => EventListenerResponse - ): RouteHandler; - recordingName(recordingName?: string): RouteHandler; - configure(config: PollyConfig): RouteHandler; + on(event: RequestRouteEvent, listener: RequestEventListener): RouteHandler; + on(event: RecordingRouteEvent, listener: RecordingEventListener): RouteHandler; + on(event: ResponseRouteEvent, listener: ResponseEventListener): RouteHandler; + on(event: ErrorRouteEvent, listener: ErrorEventListener): RouteHandler; + off(event: RequestRouteEvent, listener: RequestEventListener): RouteHandler; + off(event: RecordingRouteEvent, listener: RecordingEventListener): RouteHandler; + off(event: ResponseRouteEvent, listener: ResponseEventListener): RouteHandler; + off(event: ErrorRouteEvent, listener: ErrorEventListener): RouteHandler; + once(event: RequestRouteEvent, listener: RequestEventListener): RouteHandler; + once(event: RecordingRouteEvent, listener: RecordingEventListener): RouteHandler; + once(event: ResponseRouteEvent, listener: ResponseEventListener): RouteHandler; + once(event: ErrorRouteEvent, listener: ErrorEventListener): RouteHandler; + filter: (callback: (req: Request) => boolean) => RouteHandler; + passthrough(value?: boolean): RouteHandler; + intercept(fn: InterceptHandler): RouteHandler; + recordingName(recordingName?: string): RouteHandler; + configure(config: PollyConfig): RouteHandler; + times(n?: number): RouteHandler; } export class PollyServer { - timeout: (ms: number) => Promise; - get: (...args: any[]) => RouteHandler; - put: (...args: any[]) => RouteHandler; - post: (...args: any[]) => RouteHandler; - delete: (...args: any[]) => RouteHandler; - patch: (...args: any[]) => RouteHandler; - head: (...args: any[]) => RouteHandler; - options: (...args: any[]) => RouteHandler; - any: (...args: any[]) => RouteHandler; - host(host: string, callback: () => void): void; - namespace(path: string, callback: () => void): void; + timeout: (ms: number) => Promise; + get: (routes?: string | string[]) => RouteHandler; + put: (routes?: string | string[]) => RouteHandler; + post: (routes?: string | string[]) => RouteHandler; + delete: (routes?: string | string[]) => RouteHandler; + patch: (routes?: string | string[]) => RouteHandler; + head: (routes?: string | string[]) => RouteHandler; + options: (routes?: string | string[]) => RouteHandler; + merge: (routes?: string | string[]) => RouteHandler; + any: (routes?: string | string[]) => RouteHandler; + host(host: string, callback: () => void): void; + namespace(path: string, callback: () => void): void; } export type PollyEvent = 'create' | 'stop' | 'register'; export type PollyEventListener = (poll: Polly) => void; export class Polly { - static register(Factory: typeof Adapter | typeof Persister): void; - static on(event: PollyEvent, listener: PollyEventListener): void; - static off(event: PollyEvent, listener: PollyEventListener): void; - static once(event: PollyEvent, listener: PollyEventListener): void; + static register(Factory: typeof Adapter | typeof Persister): void; + static unregister(Factory: typeof Adapter | typeof Persister): void; + static on(event: PollyEvent, listener: PollyEventListener): void; + static off(event: PollyEvent, listener: PollyEventListener): void; + static once(event: PollyEvent, listener: PollyEventListener): void; - constructor(name: string, options?: PollyConfig); + constructor(recordingName: string, options?: PollyConfig); - readonly recordingName: string | null; - mode: MODES; - server: PollyServer; - persister: Persister; + static VERSION: string; + recordingName: string; + recordingId: string; + mode: MODE; + server: PollyServer; + persister: Persister | null; + adapters: Map; + config: PollyConfig; - pause: () => void; - play: () => void; - replay: () => void; - record: () => void; - stop: () => Promise; - flush: () => Promise; - configure(config: PollyConfig): void; - connectTo(name: string | typeof Adapter): void; - disconnectFrom(name: string | typeof Adapter): void; - disconnect(): void; + pause: () => void; + play: () => void; + replay: () => void; + record: () => void; + stop: () => Promise; + flush: () => Promise; + configure(config: PollyConfig): void; + connectTo(name: string | typeof Adapter): void; + disconnectFrom(name: string | typeof Adapter): void; + disconnect(): void; } + +export const setupMocha: { + (config?: PollyConfig, context?: any): void; + beforeEach: (config?: PollyConfig, context?: any) => void; + afterEach: (context?: any) => void; +}; + +export const setupQunit: { + (hooks: any, config?: PollyConfig): void; + beforeEach: (hooks: any, config?: PollyConfig) => void; + afterEach: (hooks: any) => void; +}; diff --git a/types/pollyjs__core/pollyjs__core-tests.ts b/types/pollyjs__core/pollyjs__core-tests.ts index 596f078f43..3290f821ee 100644 --- a/types/pollyjs__core/pollyjs__core-tests.ts +++ b/types/pollyjs__core/pollyjs__core-tests.ts @@ -1,156 +1,200 @@ -import { Polly, Timing } from '@pollyjs/core'; -import { MODES } from '@pollyjs/utils'; +import { Polly, Timing, setupMocha, setupQunit } from '@pollyjs/core'; +import { EXPIRY_STRATEGIES, MODES } from '@pollyjs/utils'; const polly = new Polly('test recording', { - mode: MODES.PASSTHROUGH, - recordFailedRequests: true, - adapters: ['xhr', 'fetch'], + mode: MODES.PASSTHROUGH, + recordFailedRequests: true, + adapters: ['xhr', 'fetch'], persister: 'rest', - expiryStrategy: 'error', - timing: Timing.relative(3), - matchRequestsBy: { - method: true, - headers: true, - body: true, - order: true, + expiryStrategy: EXPIRY_STRATEGIES.ERROR, + timing: Timing.relative(3), + matchRequestsBy: { + method: true, + headers: true, + body: true, + order: true, - url: { - protocol: true, - username: true, - password: true, - hostname: true, - port: true, - pathname: true, - query: true, - hash: false - } - } + url: { + protocol: true, + username: true, + password: true, + hostname: true, + port: true, + pathname: true, + query: true, + hash: false, + }, + }, }); new Polly('test recording', { - mode: 'replay', - recordFailedRequests: true, - adapters: ['xhr', 'fetch'], - persister: 'rest', - timing: Timing.relative(3), - matchRequestsBy: { - method(method) { - return method.toLowerCase(); - }, - headers: 1 === 1 ? { exclude: ['X-Auth'] } : (headers) => { - delete headers['X-Auth']; - return headers; - }, - body(body) { - const json = JSON.parse(body); + mode: 'record', + expiryStrategy: 'warn', + recordFailedRequests: true, + adapters: ['xhr', 'fetch'], + adapterOptions: { + xhr: { + context: {}, + }, + fetch: { + context: {}, + }, + foo: { + bar: true, + }, + }, + persister: 'rest', + persisterOptions: { + keepUnusedRequests: false, + rest: { + apiNamespace: '/api/v2', + }, + foo: { + bar: true, + }, + }, + timing: Timing.relative(3), + matchRequestsBy: { + method(method) { + return method.toLowerCase(); + }, + headers: + 1 === 1 + ? { exclude: ['X-Auth'] } + : headers => { + delete headers['X-Auth']; + return headers; + }, + body(body) { + const json = JSON.parse(body); - delete json.email; - return JSON.stringify(json); - }, + delete json.email; + return JSON.stringify(json); + }, - url: { - protocol(protocol) { - return protocol === 'http' ? 'https:' : protocol; - }, - username(username) { - return username === 'johndoe' ? 'username' : username; - }, - password(password) { - return password || 'password'; - }, - hostname(hostname) { - return hostname.replace('.com', '.net'); - }, - port(port) { - return port > 80 ? 3000 : 433; - }, - pathname(pathname) { - return pathname.replace('/api/v1', '/api'); - }, - query(query) { - return { ...query, token: '' }; - }, - hash(hash) { - return hash.replace(/token=[0-9]+/, ''); - } - } - } + url: { + protocol(protocol) { + return protocol === 'http' ? 'https:' : protocol; + }, + username(username) { + return username === 'johndoe' ? 'username' : username; + }, + password(password) { + return password || 'password'; + }, + hostname(hostname) { + return hostname.replace('.com', '.net'); + }, + port(port) { + return port > 80 ? 3000 : 433; + }, + pathname(pathname) { + return pathname.replace('/api/v1', '/api'); + }, + query(query) { + return { ...query, token: '' }; + }, + hash(hash) { + return hash.replace(/token=[0-9]+/, ''); + }, + }, + }, }); function log(_: string) { - // no op + // no op } async function test() { - polly.pause(); - polly.play(); - const { server } = polly; - server.get('/session').on('request', req => { - req.headers['X-AUTH'] = ''; - req.query.email = 'test@app.com'; - }); - server.get('/session').on('beforeResponse', (req, res) => { - res.setHeader('X-AUTH', ''); - }); - server.get('/session').on('response', (req, res) => { - log(`${req.url} took ${req.responseTime}ms with a status of ${res.statusCode}.`); - }); + polly.pause(); + polly.play(); + const { server } = polly; + server.get('/session').on('request', req => { + req.headers['X-AUTH'] = ''; + req.query.email = 'test@app.com'; + }); + server.get('/session').on('beforeResponse', (req, res) => { + res.setHeader('X-AUTH', ''); + }); + server.get('/session').on('response', (req, res) => { + log(`${req.url} took ${req.responseTime}ms with a status of ${res.statusCode}.`); + }); - polly.configure({ - adapterOptions: { - fetch: { - context: null - } - } - }); + polly.configure({ + adapterOptions: { + fetch: { + context: null, + }, + }, + }); - server - .get('/session') - .once('request', req => { - req.headers['X-AUTH'] = ''; - req.query.email = 'test@app.com'; - }) - .once('request', () => { - /* Do something else */ - }); + server + .get('/session') + .once('request', req => { + req.headers['X-AUTH'] = ''; + req.query.email = 'test@app.com'; + }) + .once('beforeResponse', (req, res) => { + const data = res.jsonBody(); - server - .get('/users/:id') - .filter(req => req.params.id === '1') - .filter(req => req.params.id !== '2') - .recordingName('test') - .recordingName() - .intercept((_req, res) => { - res.status(200).json({ email: 'user1@test.com' }); - }); + data._sessionKey = 'foo'; + res.json(data); + }) + .once('request', (req, event) => { + /* Do something else */ + event.stopPropagation(); + }); - /* Intercept all Google Analytic requests and respond with a 200 */ - server.get('/google-analytics/*path').intercept((req, res, intercept) => { - if (req.pathname === 'test') { - intercept.abort(); - } else { - res.sendStatus(200); - } - }); + server + .get(['/users/:id', 'users/v2/:id']) + .filter(req => req.params.id === '1') + .filter(req => req.params.id !== '2') + .recordingName('test') + .recordingName() + .intercept((_req, res) => { + res.status(200).json({ email: 'user1@test.com' }); + }); - /* Pass-through all GET requests to /coverage */ - server.get('/coverage') - .configure({ expiresIn: '5d' }) - .passthrough(); + /* Intercept all Google Analytic requests and respond with a 200 */ + server.get('/google-analytics/*path').intercept((req, res, intercept) => { + if (req.pathname === 'test') { + intercept.abort(); + } else { + res.sendStatus(200); + } + }); - server.any().on('error', (req, error) => { - req - .setHeader('Content-Length', '2344') - .setHeaders({ - 'Content-Type': 'application/json', - 'Content-Length': '42' - }) - .removeHeader('Content-Length') - .removeHeaders(['Content-Type', 'Content-Length']); + /* Pass-through all GET requests to /coverage */ + server + .get('/coverage') + .configure({ expiresIn: '5d' }) + .passthrough(); - req.removeHeaders(['Content-Type', 'Content-Length']); - log(req.pathname + JSON.stringify(error)); - }); + server.any().on('error', (req, error) => { + req.setHeader('Content-Length', '2344') + .setHeaders({ + 'Content-Type': 'application/json', + 'Content-Length': '42', + }) + .removeHeader('Content-Length') + .removeHeaders(['Content-Type', 'Content-Length']); - await polly.flush(); + req.removeHeaders(['Content-Type', 'Content-Length']); + log(req.pathname + JSON.stringify(error)); + }); + + await polly.flush(); } + +setupMocha(); +setupMocha({ recordFailedRequests: false }); +setupMocha({ recordFailedRequests: false }, {}); +setupMocha.beforeEach(); +setupMocha.beforeEach({ recordFailedRequests: true }); +setupMocha.afterEach(); + +setupQunit({}); +setupQunit({}, { recordFailedRequests: false }); +setupQunit.beforeEach({}); +setupQunit.beforeEach({}, { recordFailedRequests: true }); +setupQunit.afterEach({}); diff --git a/types/pollyjs__utils/index.d.ts b/types/pollyjs__utils/index.d.ts index bf5d4d0c68..6757865ee5 100644 --- a/types/pollyjs__utils/index.d.ts +++ b/types/pollyjs__utils/index.d.ts @@ -1,6 +1,7 @@ -// Type definitions for @pollyjs/utils 2.0 +// Type definitions for @pollyjs/utils 2.6 // Project: https://github.com/netflix/pollyjs/tree/master/packages/@pollyjs/utils // Definitions by: feinoujc +// Offir Golan // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped // TypeScript Version: 2.4 @@ -8,5 +9,18 @@ export enum MODES { RECORD = 'record', REPLAY = 'replay', PASSTHROUGH = 'passthrough', - STOPPED = 'stopped' + STOPPED = 'stopped', +} + +export enum ACTIONS { + RECORD = 'record', + REPLAY = 'replay', + INTERCEPT = 'intercept', + PASSTHROUGH = 'passthrough', +} + +export enum EXPIRY_STRATEGIES { + RECORD = 'record', + WARN = 'warn', + ERROR = 'error', }