From 81c0f60b9d2ba2c1878f2b0ddc801fe06f163b79 Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Fri, 24 Jun 2016 15:45:59 +1000 Subject: [PATCH 1/3] First pass at disabling force-fetch in SSR contexts --- src/index.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/index.ts b/src/index.ts index 45379a9c6d0..dd036caed9e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -55,6 +55,7 @@ export default class ApolloClient { public reducerConfig: ApolloReducerConfig; public queryTransformer: QueryTransformer; public shouldBatch: boolean; + public shouldForceFetch: boolean; constructor({ networkInterface, @@ -63,6 +64,8 @@ export default class ApolloClient { dataIdFromObject, queryTransformer, shouldBatch = false, + ssrMode = false, + ssrForceFetchDelay = 0, }: { networkInterface?: NetworkInterface, reduxRootKey?: string, @@ -70,6 +73,8 @@ export default class ApolloClient { dataIdFromObject?: IdGetter, queryTransformer?: QueryTransformer, shouldBatch?: boolean, + ssrMode?: boolean, + ssrForceFetchDelay?: number } = {}) { this.reduxRootKey = reduxRootKey ? reduxRootKey : 'apollo'; this.initialState = initialState ? initialState : {}; @@ -77,6 +82,10 @@ export default class ApolloClient { createNetworkInterface('/graphql'); this.queryTransformer = queryTransformer; this.shouldBatch = shouldBatch; + this.shouldForceFetch = !(ssrMode || ssrForceFetchDelay > 0); + if (ssrForceFetchDelay) { + setTimeout(() => this.shouldForceFetch = true, ssrForceFetchDelay); + } this.reducerConfig = { dataIdFromObject, @@ -86,12 +95,16 @@ export default class ApolloClient { public watchQuery = (options: WatchQueryOptions): ObservableQuery => { this.initStore(); + options.forceFetch = this.shouldForceFetch && options.forceFetch; + return this.queryManager.watchQuery(options); }; public query = (options: WatchQueryOptions): Promise => { this.initStore(); + options.forceFetch = this.shouldForceFetch && options.forceFetch; + return this.queryManager.query(options); }; From aa7d9c3ab6964f6d4d7589a51c54e054110c025f Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Mon, 27 Jun 2016 14:51:03 +1000 Subject: [PATCH 2/3] Added test for ssr/forceFetch functionality. --- package.json | 5 +- test/client.ts | 100 +++++ typings/browser/index.d.ts | 1 + typings/browser/modules/sinon/index.d.ts | 446 +++++++++++++++++++++ typings/browser/modules/sinon/typings.json | 11 + typings/main/index.d.ts | 1 + typings/main/modules/sinon/index.d.ts | 446 +++++++++++++++++++++ typings/main/modules/sinon/typings.json | 11 + 8 files changed, 1019 insertions(+), 2 deletions(-) create mode 100644 typings/browser/modules/sinon/index.d.ts create mode 100644 typings/browser/modules/sinon/typings.json create mode 100644 typings/main/modules/sinon/index.d.ts create mode 100644 typings/main/modules/sinon/typings.json diff --git a/package.json b/package.json index 28e286be829..60c22c39033 100644 --- a/package.json +++ b/package.json @@ -78,12 +78,13 @@ "pretty-bytes": "^3.0.1", "remap-istanbul": "^0.5.1", "request-promise": "^2.0.1", + "rxjs": "^5.0.0-beta.7", + "sinon": "^1.17.4", "source-map-support": "^0.4.0", "swapi-graphql": "^0.0.4", "tslint": "3.7.3", "typescript": "^1.8.9", "typings": "^1.0.0", - "uglify-js": "^2.6.2", - "rxjs": "^5.0.0-beta.7" + "uglify-js": "^2.6.2" } } diff --git a/test/client.ts b/test/client.ts index 796b451fc41..0d3b4d9684f 100644 --- a/test/client.ts +++ b/test/client.ts @@ -1,5 +1,6 @@ import * as chai from 'chai'; const { assert } = chai; +import * as sinon from 'sinon'; import ApolloClient from '../src'; @@ -879,4 +880,103 @@ describe('client', () => { }); }); }); + + describe.only('forceFetch', () => { + const query = gql` + query number { + myNumber { + n + } + } + `; + + const firstFetch = { + myNumber: { + n: 1, + }, + }; + const secondFetch = { + myNumber: { + n: 2, + }, + }; + + + let networkInterface; + let clock; + beforeEach(() => { + networkInterface = mockNetworkInterface({ + request: { query }, + result: { data: firstFetch }, + }, { + request: { query }, + result: { data: secondFetch }, + }); + }); + + afterEach(() => { + if (clock) { + clock.reset(); + } + }); + + it('forces the query to rerun', () => { + const client = new ApolloClient({ + networkInterface, + }); + + // Run a query first to initialize the store + return client.query({ query }) + // then query for real + .then(() => client.query({ query, forceFetch: true })) + .then((result) => { + assert.deepEqual(result.data, { myNumber: { n: 2 } }); + }); + }); + + it('can be disabled with ssrMode', () => { + const client = new ApolloClient({ + networkInterface, + ssrMode: true, + }); + + // Run a query first to initialize the store + return client.query({ query }) + // then query for real + .then(() => client.query({ query, forceFetch: true })) + .then((result) => { + assert.deepEqual(result.data, { myNumber: { n: 1 } }); + }); + }); + + it('can temporarily be disabled with ssrForceFetchDelay', () => { + clock = sinon.useFakeTimers(); + + const client = new ApolloClient({ + networkInterface, + ssrForceFetchDelay: 100, + }); + + // Run a query first to initialize the store + const outerPromise = client.query({ query }) + // then query for real + .then(() => { + const promise = client.query({ query, forceFetch: true }); + clock.tick(0); + return promise; + }) + .then((result) => { + assert.deepEqual(result.data, { myNumber: { n: 1 } }); + clock.tick(100); + const promise = client.query({ query, forceFetch: true }); + clock.tick(0); + return promise; + }) + .then((result) => { + assert.deepEqual(result.data, { myNumber: { n: 2 } }); + }); + clock.tick(0); + return outerPromise; + }); + }); }); diff --git a/typings/browser/index.d.ts b/typings/browser/index.d.ts index c608f911191..8bae1e81076 100644 --- a/typings/browser/index.d.ts +++ b/typings/browser/index.d.ts @@ -10,3 +10,4 @@ /// /// /// +/// diff --git a/typings/browser/modules/sinon/index.d.ts b/typings/browser/modules/sinon/index.d.ts new file mode 100644 index 00000000000..4941c94dce3 --- /dev/null +++ b/typings/browser/modules/sinon/index.d.ts @@ -0,0 +1,446 @@ +// Generated by typings +// Source: https://raw.githubusercontent.com/typed-typings/npm-sinon/e18b1896080e7ae16c16a2725ed5e43d2f2fea65/lib/sinon.d.ts +declare module '~sinon/lib/sinon' { +// Type definitions for Sinon 1.16.0 +// Project: http://sinonjs.org/ +// Definitions by: William Sears +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +module Sinon { + interface SinonSpyCallApi { + // Properties + thisValue: any; + args: any[]; + exception: any; + returnValue: any; + + // Methods + calledOn(obj: any): boolean; + calledWith(...args: any[]): boolean; + calledWithExactly(...args: any[]): boolean; + calledWithMatch(...args: any[]): boolean; + notCalledWith(...args: any[]): boolean; + notCalledWithMatch(...args: any[]): boolean; + returned(value: any): boolean; + threw(): boolean; + threw(type: string): boolean; + threw(obj: any): boolean; + callArg(pos: number): void; + callArgOn(pos: number, obj: any, ...args: any[]): void; + callArgWith(pos: number, ...args: any[]): void; + callArgOnWith(pos: number, obj: any, ...args: any[]): void; + yield(...args: any[]): void; + yieldOn(obj: any, ...args: any[]): void; + yieldTo(property: string, ...args: any[]): void; + yieldToOn(property: string, obj: any, ...args: any[]): void; + } + + interface SinonSpyCall extends SinonSpyCallApi { + calledBefore(call: SinonSpyCall): boolean; + calledAfter(call: SinonSpyCall): boolean; + calledWithNew(call: SinonSpyCall): boolean; + } + + interface SinonSpy extends SinonSpyCallApi { + // Properties + callCount: number; + called: boolean; + notCalled: boolean; + calledOnce: boolean; + calledTwice: boolean; + calledThrice: boolean; + firstCall: SinonSpyCall; + secondCall: SinonSpyCall; + thirdCall: SinonSpyCall; + lastCall: SinonSpyCall; + thisValues: any[]; + args: any[][]; + exceptions: any[]; + returnValues: any[]; + + // Methods + (...args: any[]): any; + calledBefore(anotherSpy: SinonSpy): boolean; + calledAfter(anotherSpy: SinonSpy): boolean; + calledWithNew(spy: SinonSpy): boolean; + withArgs(...args: any[]): SinonSpy; + alwaysCalledOn(obj: any): boolean; + alwaysCalledWith(...args: any[]): boolean; + alwaysCalledWithExactly(...args: any[]): boolean; + alwaysCalledWithMatch(...args: any[]): boolean; + neverCalledWith(...args: any[]): boolean; + neverCalledWithMatch(...args: any[]): boolean; + alwaysThrew(): boolean; + alwaysThrew(type: string): boolean; + alwaysThrew(obj: any): boolean; + alwaysReturned(): boolean; + invokeCallback(...args: any[]): void; + getCall(n: number): SinonSpyCall; + getCalls(): SinonSpyCall[]; + reset(): void; + printf(format: string, ...args: any[]): string; + restore(): void; + } + + interface SinonSpyStatic { + (): SinonSpy; + (func: any): SinonSpy; + (obj: any, method: string): SinonSpy; + } + + interface SinonStatic { + spy: SinonSpyStatic; + } + + interface SinonStub extends SinonSpy { + resetBehavior(): void; + returns(obj: any): SinonStub; + returnsArg(index: number): SinonStub; + returnsThis(): SinonStub; + throws(type?: string): SinonStub; + throws(obj: any): SinonStub; + callsArg(index: number): SinonStub; + callsArgOn(index: number, context: any): SinonStub; + callsArgWith(index: number, ...args: any[]): SinonStub; + callsArgOnWith(index: number, context: any, ...args: any[]): SinonStub; + callsArgAsync(index: number): SinonStub; + callsArgOnAsync(index: number, context: any): SinonStub; + callsArgWithAsync(index: number, ...args: any[]): SinonStub; + callsArgOnWithAsync(index: number, context: any, ...args: any[]): SinonStub; + onCall(n: number): SinonStub; + onFirstCall(): SinonStub; + onSecondCall(): SinonStub; + onThirdCall(): SinonStub; + yields(...args: any[]): SinonStub; + yieldsOn(context: any, ...args: any[]): SinonStub; + yieldsTo(property: string, ...args: any[]): SinonStub; + yieldsToOn(property: string, context: any, ...args: any[]): SinonStub; + yieldsAsync(...args: any[]): SinonStub; + yieldsOnAsync(context: any, ...args: any[]): SinonStub; + yieldsToAsync(property: string, ...args: any[]): SinonStub; + yieldsToOnAsync(property: string, context: any, ...args: any[]): SinonStub; + withArgs(...args: any[]): SinonStub; + } + + interface SinonStubStatic { + (): SinonStub; + (obj: any): SinonStub; + (obj: any, method: string): SinonStub; + (obj: any, method: string, func: any): SinonStub; + } + + interface SinonStatic { + stub: SinonStubStatic; + } + + interface SinonExpectation extends SinonStub { + atLeast(n: number): SinonExpectation; + atMost(n: number): SinonExpectation; + never(): SinonExpectation; + once(): SinonExpectation; + twice(): SinonExpectation; + thrice(): SinonExpectation; + exactly(n: number): SinonExpectation; + withArgs(...args: any[]): SinonExpectation; + withExactArgs(...args: any[]): SinonExpectation; + on(obj: any): SinonExpectation; + verify(): SinonExpectation; + restore(): void; + } + + interface SinonExpectationStatic { + create(methodName?: string): SinonExpectation; + } + + interface SinonMock { + expects(method: string): SinonExpectation; + restore(): void; + verify(): void; + } + + interface SinonMockStatic { + (): SinonExpectation; + (obj: any): SinonMock; + } + + interface SinonStatic { + expectation: SinonExpectationStatic; + mock: SinonMockStatic; + } + + interface SinonFakeTimers { + now: number; + create(now: number): SinonFakeTimers; + setTimeout(callback: (...args: any[]) => void, timeout: number, ...args: any[]): number; + clearTimeout(id: number): void; + setInterval(callback: (...args: any[]) => void, timeout: number, ...args: any[]): number; + clearInterval(id: number): void; + tick(ms: number): number; + reset(): void; + Date(): Date; + Date(year: number): Date; + Date(year: number, month: number): Date; + Date(year: number, month: number, day: number): Date; + Date(year: number, month: number, day: number, hour: number): Date; + Date(year: number, month: number, day: number, hour: number, minute: number): Date; + Date(year: number, month: number, day: number, hour: number, minute: number, second: number): Date; + Date(year: number, month: number, day: number, hour: number, minute: number, second: number, ms: number): Date; + restore(): void; + + /** + * Simulate the user changing the system clock while your program is running. It changes the 'now' timestamp + * without affecting timers, intervals or immediates. + * @param now The new 'now' in unix milliseconds + */ + setSystemTime(now: number): void; + /** + * Simulate the user changing the system clock while your program is running. It changes the 'now' timestamp + * without affecting timers, intervals or immediates. + * @param now The new 'now' as a JavaScript Date + */ + setSystemTime(date: Date): void; + } + + interface SinonFakeTimersStatic { + (): SinonFakeTimers; + (...timers: string[]): SinonFakeTimers; + (now: number, ...timers: string[]): SinonFakeTimers; + } + + interface SinonStatic { + useFakeTimers: SinonFakeTimersStatic; + clock: SinonFakeTimers; + } + + interface SinonFakeUploadProgress { + eventListeners: { + progress: any[]; + load: any[]; + abort: any[]; + error: any[]; + }; + + addEventListener(event: string, listener: (e: Event) => any): void; + removeEventListener(event: string, listener: (e: Event) => any): void; + dispatchEvent(event: Event): void; + } + + interface SinonFakeXMLHttpRequest { + // Properties + onCreate: (xhr: SinonFakeXMLHttpRequest) => void; + url: string; + method: string; + requestHeaders: any; + requestBody: string; + status: number; + statusText: string; + async: boolean; + username: string; + password: string; + withCredentials: boolean; + upload: SinonFakeUploadProgress; + responseXML: Document; + getResponseHeader(header: string): string; + getAllResponseHeaders(): any; + + // Methods + restore(): void; + useFilters: boolean; + addFilter(filter: (method: string, url: string, async: boolean, username: string, password: string) => boolean): void; + setResponseHeaders(headers: any): void; + setResponseBody(body: string): void; + respond(status: number, headers: any, body: string): void; + autoRespond(ms: number): void; + } + + interface SinonFakeXMLHttpRequestStatic { + (): SinonFakeXMLHttpRequest; + } + + interface SinonStatic { + useFakeXMLHttpRequest: SinonFakeXMLHttpRequestStatic; + FakeXMLHttpRequest: SinonFakeXMLHttpRequest; + } + + interface SinonFakeServer { + // Properties + autoRespond: boolean; + autoRespondAfter: number; + fakeHTTPMethods: boolean; + getHTTPMethod: (request: SinonFakeXMLHttpRequest) => string; + requests: SinonFakeXMLHttpRequest[]; + respondImmediately: boolean; + + // Methods + respondWith(body: string): void; + respondWith(response: any[]): void; + respondWith(fn: (xhr: SinonFakeXMLHttpRequest) => void): void; + respondWith(url: string, body: string): void; + respondWith(url: string, response: any[]): void; + respondWith(url: string, fn: (xhr: SinonFakeXMLHttpRequest) => void): void; + respondWith(method: string, url: string, body: string): void; + respondWith(method: string, url: string, response: any[]): void; + respondWith(method: string, url: string, fn: (xhr: SinonFakeXMLHttpRequest) => void): void; + respondWith(url: RegExp, body: string): void; + respondWith(url: RegExp, response: any[]): void; + respondWith(url: RegExp, fn: (xhr: SinonFakeXMLHttpRequest) => void): void; + respondWith(method: string, url: RegExp, body: string): void; + respondWith(method: string, url: RegExp, response: any[]): void; + respondWith(method: string, url: RegExp, fn: (xhr: SinonFakeXMLHttpRequest) => void): void; + respond(): void; + restore(): void; + } + + interface SinonFakeServerStatic { + create(): SinonFakeServer; + } + + interface SinonStatic { + fakeServer: SinonFakeServerStatic; + fakeServerWithClock: SinonFakeServerStatic; + } + + interface SinonExposeOptions { + prefix?: string; + includeFail?: boolean; + } + + interface SinonAssert { + // Properties + failException: string; + fail: (message?: string) => void; // Overridable + pass: (assertion: any) => void; // Overridable + + // Methods + notCalled(spy: SinonSpy): void; + called(spy: SinonSpy): void; + calledOnce(spy: SinonSpy): void; + calledTwice(spy: SinonSpy): void; + calledThrice(spy: SinonSpy): void; + callCount(spy: SinonSpy, count: number): void; + callOrder(...spies: SinonSpy[]): void; + calledOn(spy: SinonSpy, obj: any): void; + alwaysCalledOn(spy: SinonSpy, obj: any): void; + calledWith(spy: SinonSpy, ...args: any[]): void; + alwaysCalledWith(spy: SinonSpy, ...args: any[]): void; + neverCalledWith(spy: SinonSpy, ...args: any[]): void; + calledWithExactly(spy: SinonSpy, ...args: any[]): void; + alwaysCalledWithExactly(spy: SinonSpy, ...args: any[]): void; + calledWithMatch(spy: SinonSpy, ...args: any[]): void; + alwaysCalledWithMatch(spy: SinonSpy, ...args: any[]): void; + neverCalledWithMatch(spy: SinonSpy, ...args: any[]): void; + threw(spy: SinonSpy): void; + threw(spy: SinonSpy, exception: string): void; + threw(spy: SinonSpy, exception: any): void; + alwaysThrew(spy: SinonSpy): void; + alwaysThrew(spy: SinonSpy, exception: string): void; + alwaysThrew(spy: SinonSpy, exception: any): void; + expose(obj: any, options?: SinonExposeOptions): void; + } + + interface SinonStatic { + assert: SinonAssert; + } + + interface SinonMatcher { + and(expr: SinonMatcher): SinonMatcher; + or(expr: SinonMatcher): SinonMatcher; + } + + interface SinonMatch { + (value: number): SinonMatcher; + (value: string): SinonMatcher; + (expr: RegExp): SinonMatcher; + (obj: any): SinonMatcher; + (callback: (value: any) => boolean): SinonMatcher; + any: SinonMatcher; + defined: SinonMatcher; + truthy: SinonMatcher; + falsy: SinonMatcher; + bool: SinonMatcher; + number: SinonMatcher; + string: SinonMatcher; + object: SinonMatcher; + func: SinonMatcher; + array: SinonMatcher; + regexp: SinonMatcher; + date: SinonMatcher; + same(obj: any): SinonMatcher; + typeOf(type: string): SinonMatcher; + instanceOf(type: any): SinonMatcher; + has(property: string, expect?: any): SinonMatcher; + hasOwn(property: string, expect?: any): SinonMatcher; + } + + interface SinonStatic { + match: SinonMatch; + } + + interface SinonSandboxConfig { + injectInto?: any; + properties?: string[]; + useFakeTimers?: any; + useFakeServer?: any; + } + + interface SinonSandbox { + clock: SinonFakeTimers; + requests: SinonFakeXMLHttpRequest; + server: SinonFakeServer; + spy: SinonSpyStatic; + stub: SinonStubStatic; + mock: SinonMockStatic; + useFakeTimers: SinonFakeTimersStatic; + useFakeXMLHttpRequest: SinonFakeXMLHttpRequestStatic; + useFakeServer(): SinonFakeServer; + restore(): void; + } + + interface SinonSandboxStatic { + create(): SinonSandbox; + create(config: SinonSandboxConfig): SinonSandbox; + } + + interface SinonStatic { + sandbox: SinonSandboxStatic; + } + + interface SinonTestConfig { + injectIntoThis?: boolean; + injectInto?: any; + properties?: string[]; + useFakeTimers?: boolean; + useFakeServer?: boolean; + } + + interface SinonTestWrapper extends SinonSandbox { + (...args: any[]): any; + } + + interface SinonStatic { + config: SinonTestConfig; + test(fn: (...args: any[]) => any): SinonTestWrapper; + testCase(tests: any): any; + } + + // Utility overridables + interface SinonStatic { + createStubInstance(constructor: any): SinonStub; + format(obj: any): string; + log(message: string): void; + restore(object: any): void; + } +} + +var Sinon: Sinon.SinonStatic; + +export = Sinon; +} +declare module 'sinon/lib/sinon' { +import main = require('~sinon/lib/sinon'); +export = main; +} +declare module 'sinon' { +import main = require('~sinon/lib/sinon'); +export = main; +} diff --git a/typings/browser/modules/sinon/typings.json b/typings/browser/modules/sinon/typings.json new file mode 100644 index 00000000000..89d26fbc527 --- /dev/null +++ b/typings/browser/modules/sinon/typings.json @@ -0,0 +1,11 @@ +{ + "resolution": "browser", + "tree": { + "src": "https://raw.githubusercontent.com/typed-typings/npm-sinon/e18b1896080e7ae16c16a2725ed5e43d2f2fea65/typings.json", + "raw": "registry:npm/sinon#1.16.0+20160427193336", + "main": "lib/sinon.d.ts", + "global": false, + "name": "sinon", + "type": "typings" + } +} diff --git a/typings/main/index.d.ts b/typings/main/index.d.ts index c608f911191..8bae1e81076 100644 --- a/typings/main/index.d.ts +++ b/typings/main/index.d.ts @@ -10,3 +10,4 @@ /// /// /// +/// diff --git a/typings/main/modules/sinon/index.d.ts b/typings/main/modules/sinon/index.d.ts new file mode 100644 index 00000000000..4941c94dce3 --- /dev/null +++ b/typings/main/modules/sinon/index.d.ts @@ -0,0 +1,446 @@ +// Generated by typings +// Source: https://raw.githubusercontent.com/typed-typings/npm-sinon/e18b1896080e7ae16c16a2725ed5e43d2f2fea65/lib/sinon.d.ts +declare module '~sinon/lib/sinon' { +// Type definitions for Sinon 1.16.0 +// Project: http://sinonjs.org/ +// Definitions by: William Sears +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +module Sinon { + interface SinonSpyCallApi { + // Properties + thisValue: any; + args: any[]; + exception: any; + returnValue: any; + + // Methods + calledOn(obj: any): boolean; + calledWith(...args: any[]): boolean; + calledWithExactly(...args: any[]): boolean; + calledWithMatch(...args: any[]): boolean; + notCalledWith(...args: any[]): boolean; + notCalledWithMatch(...args: any[]): boolean; + returned(value: any): boolean; + threw(): boolean; + threw(type: string): boolean; + threw(obj: any): boolean; + callArg(pos: number): void; + callArgOn(pos: number, obj: any, ...args: any[]): void; + callArgWith(pos: number, ...args: any[]): void; + callArgOnWith(pos: number, obj: any, ...args: any[]): void; + yield(...args: any[]): void; + yieldOn(obj: any, ...args: any[]): void; + yieldTo(property: string, ...args: any[]): void; + yieldToOn(property: string, obj: any, ...args: any[]): void; + } + + interface SinonSpyCall extends SinonSpyCallApi { + calledBefore(call: SinonSpyCall): boolean; + calledAfter(call: SinonSpyCall): boolean; + calledWithNew(call: SinonSpyCall): boolean; + } + + interface SinonSpy extends SinonSpyCallApi { + // Properties + callCount: number; + called: boolean; + notCalled: boolean; + calledOnce: boolean; + calledTwice: boolean; + calledThrice: boolean; + firstCall: SinonSpyCall; + secondCall: SinonSpyCall; + thirdCall: SinonSpyCall; + lastCall: SinonSpyCall; + thisValues: any[]; + args: any[][]; + exceptions: any[]; + returnValues: any[]; + + // Methods + (...args: any[]): any; + calledBefore(anotherSpy: SinonSpy): boolean; + calledAfter(anotherSpy: SinonSpy): boolean; + calledWithNew(spy: SinonSpy): boolean; + withArgs(...args: any[]): SinonSpy; + alwaysCalledOn(obj: any): boolean; + alwaysCalledWith(...args: any[]): boolean; + alwaysCalledWithExactly(...args: any[]): boolean; + alwaysCalledWithMatch(...args: any[]): boolean; + neverCalledWith(...args: any[]): boolean; + neverCalledWithMatch(...args: any[]): boolean; + alwaysThrew(): boolean; + alwaysThrew(type: string): boolean; + alwaysThrew(obj: any): boolean; + alwaysReturned(): boolean; + invokeCallback(...args: any[]): void; + getCall(n: number): SinonSpyCall; + getCalls(): SinonSpyCall[]; + reset(): void; + printf(format: string, ...args: any[]): string; + restore(): void; + } + + interface SinonSpyStatic { + (): SinonSpy; + (func: any): SinonSpy; + (obj: any, method: string): SinonSpy; + } + + interface SinonStatic { + spy: SinonSpyStatic; + } + + interface SinonStub extends SinonSpy { + resetBehavior(): void; + returns(obj: any): SinonStub; + returnsArg(index: number): SinonStub; + returnsThis(): SinonStub; + throws(type?: string): SinonStub; + throws(obj: any): SinonStub; + callsArg(index: number): SinonStub; + callsArgOn(index: number, context: any): SinonStub; + callsArgWith(index: number, ...args: any[]): SinonStub; + callsArgOnWith(index: number, context: any, ...args: any[]): SinonStub; + callsArgAsync(index: number): SinonStub; + callsArgOnAsync(index: number, context: any): SinonStub; + callsArgWithAsync(index: number, ...args: any[]): SinonStub; + callsArgOnWithAsync(index: number, context: any, ...args: any[]): SinonStub; + onCall(n: number): SinonStub; + onFirstCall(): SinonStub; + onSecondCall(): SinonStub; + onThirdCall(): SinonStub; + yields(...args: any[]): SinonStub; + yieldsOn(context: any, ...args: any[]): SinonStub; + yieldsTo(property: string, ...args: any[]): SinonStub; + yieldsToOn(property: string, context: any, ...args: any[]): SinonStub; + yieldsAsync(...args: any[]): SinonStub; + yieldsOnAsync(context: any, ...args: any[]): SinonStub; + yieldsToAsync(property: string, ...args: any[]): SinonStub; + yieldsToOnAsync(property: string, context: any, ...args: any[]): SinonStub; + withArgs(...args: any[]): SinonStub; + } + + interface SinonStubStatic { + (): SinonStub; + (obj: any): SinonStub; + (obj: any, method: string): SinonStub; + (obj: any, method: string, func: any): SinonStub; + } + + interface SinonStatic { + stub: SinonStubStatic; + } + + interface SinonExpectation extends SinonStub { + atLeast(n: number): SinonExpectation; + atMost(n: number): SinonExpectation; + never(): SinonExpectation; + once(): SinonExpectation; + twice(): SinonExpectation; + thrice(): SinonExpectation; + exactly(n: number): SinonExpectation; + withArgs(...args: any[]): SinonExpectation; + withExactArgs(...args: any[]): SinonExpectation; + on(obj: any): SinonExpectation; + verify(): SinonExpectation; + restore(): void; + } + + interface SinonExpectationStatic { + create(methodName?: string): SinonExpectation; + } + + interface SinonMock { + expects(method: string): SinonExpectation; + restore(): void; + verify(): void; + } + + interface SinonMockStatic { + (): SinonExpectation; + (obj: any): SinonMock; + } + + interface SinonStatic { + expectation: SinonExpectationStatic; + mock: SinonMockStatic; + } + + interface SinonFakeTimers { + now: number; + create(now: number): SinonFakeTimers; + setTimeout(callback: (...args: any[]) => void, timeout: number, ...args: any[]): number; + clearTimeout(id: number): void; + setInterval(callback: (...args: any[]) => void, timeout: number, ...args: any[]): number; + clearInterval(id: number): void; + tick(ms: number): number; + reset(): void; + Date(): Date; + Date(year: number): Date; + Date(year: number, month: number): Date; + Date(year: number, month: number, day: number): Date; + Date(year: number, month: number, day: number, hour: number): Date; + Date(year: number, month: number, day: number, hour: number, minute: number): Date; + Date(year: number, month: number, day: number, hour: number, minute: number, second: number): Date; + Date(year: number, month: number, day: number, hour: number, minute: number, second: number, ms: number): Date; + restore(): void; + + /** + * Simulate the user changing the system clock while your program is running. It changes the 'now' timestamp + * without affecting timers, intervals or immediates. + * @param now The new 'now' in unix milliseconds + */ + setSystemTime(now: number): void; + /** + * Simulate the user changing the system clock while your program is running. It changes the 'now' timestamp + * without affecting timers, intervals or immediates. + * @param now The new 'now' as a JavaScript Date + */ + setSystemTime(date: Date): void; + } + + interface SinonFakeTimersStatic { + (): SinonFakeTimers; + (...timers: string[]): SinonFakeTimers; + (now: number, ...timers: string[]): SinonFakeTimers; + } + + interface SinonStatic { + useFakeTimers: SinonFakeTimersStatic; + clock: SinonFakeTimers; + } + + interface SinonFakeUploadProgress { + eventListeners: { + progress: any[]; + load: any[]; + abort: any[]; + error: any[]; + }; + + addEventListener(event: string, listener: (e: Event) => any): void; + removeEventListener(event: string, listener: (e: Event) => any): void; + dispatchEvent(event: Event): void; + } + + interface SinonFakeXMLHttpRequest { + // Properties + onCreate: (xhr: SinonFakeXMLHttpRequest) => void; + url: string; + method: string; + requestHeaders: any; + requestBody: string; + status: number; + statusText: string; + async: boolean; + username: string; + password: string; + withCredentials: boolean; + upload: SinonFakeUploadProgress; + responseXML: Document; + getResponseHeader(header: string): string; + getAllResponseHeaders(): any; + + // Methods + restore(): void; + useFilters: boolean; + addFilter(filter: (method: string, url: string, async: boolean, username: string, password: string) => boolean): void; + setResponseHeaders(headers: any): void; + setResponseBody(body: string): void; + respond(status: number, headers: any, body: string): void; + autoRespond(ms: number): void; + } + + interface SinonFakeXMLHttpRequestStatic { + (): SinonFakeXMLHttpRequest; + } + + interface SinonStatic { + useFakeXMLHttpRequest: SinonFakeXMLHttpRequestStatic; + FakeXMLHttpRequest: SinonFakeXMLHttpRequest; + } + + interface SinonFakeServer { + // Properties + autoRespond: boolean; + autoRespondAfter: number; + fakeHTTPMethods: boolean; + getHTTPMethod: (request: SinonFakeXMLHttpRequest) => string; + requests: SinonFakeXMLHttpRequest[]; + respondImmediately: boolean; + + // Methods + respondWith(body: string): void; + respondWith(response: any[]): void; + respondWith(fn: (xhr: SinonFakeXMLHttpRequest) => void): void; + respondWith(url: string, body: string): void; + respondWith(url: string, response: any[]): void; + respondWith(url: string, fn: (xhr: SinonFakeXMLHttpRequest) => void): void; + respondWith(method: string, url: string, body: string): void; + respondWith(method: string, url: string, response: any[]): void; + respondWith(method: string, url: string, fn: (xhr: SinonFakeXMLHttpRequest) => void): void; + respondWith(url: RegExp, body: string): void; + respondWith(url: RegExp, response: any[]): void; + respondWith(url: RegExp, fn: (xhr: SinonFakeXMLHttpRequest) => void): void; + respondWith(method: string, url: RegExp, body: string): void; + respondWith(method: string, url: RegExp, response: any[]): void; + respondWith(method: string, url: RegExp, fn: (xhr: SinonFakeXMLHttpRequest) => void): void; + respond(): void; + restore(): void; + } + + interface SinonFakeServerStatic { + create(): SinonFakeServer; + } + + interface SinonStatic { + fakeServer: SinonFakeServerStatic; + fakeServerWithClock: SinonFakeServerStatic; + } + + interface SinonExposeOptions { + prefix?: string; + includeFail?: boolean; + } + + interface SinonAssert { + // Properties + failException: string; + fail: (message?: string) => void; // Overridable + pass: (assertion: any) => void; // Overridable + + // Methods + notCalled(spy: SinonSpy): void; + called(spy: SinonSpy): void; + calledOnce(spy: SinonSpy): void; + calledTwice(spy: SinonSpy): void; + calledThrice(spy: SinonSpy): void; + callCount(spy: SinonSpy, count: number): void; + callOrder(...spies: SinonSpy[]): void; + calledOn(spy: SinonSpy, obj: any): void; + alwaysCalledOn(spy: SinonSpy, obj: any): void; + calledWith(spy: SinonSpy, ...args: any[]): void; + alwaysCalledWith(spy: SinonSpy, ...args: any[]): void; + neverCalledWith(spy: SinonSpy, ...args: any[]): void; + calledWithExactly(spy: SinonSpy, ...args: any[]): void; + alwaysCalledWithExactly(spy: SinonSpy, ...args: any[]): void; + calledWithMatch(spy: SinonSpy, ...args: any[]): void; + alwaysCalledWithMatch(spy: SinonSpy, ...args: any[]): void; + neverCalledWithMatch(spy: SinonSpy, ...args: any[]): void; + threw(spy: SinonSpy): void; + threw(spy: SinonSpy, exception: string): void; + threw(spy: SinonSpy, exception: any): void; + alwaysThrew(spy: SinonSpy): void; + alwaysThrew(spy: SinonSpy, exception: string): void; + alwaysThrew(spy: SinonSpy, exception: any): void; + expose(obj: any, options?: SinonExposeOptions): void; + } + + interface SinonStatic { + assert: SinonAssert; + } + + interface SinonMatcher { + and(expr: SinonMatcher): SinonMatcher; + or(expr: SinonMatcher): SinonMatcher; + } + + interface SinonMatch { + (value: number): SinonMatcher; + (value: string): SinonMatcher; + (expr: RegExp): SinonMatcher; + (obj: any): SinonMatcher; + (callback: (value: any) => boolean): SinonMatcher; + any: SinonMatcher; + defined: SinonMatcher; + truthy: SinonMatcher; + falsy: SinonMatcher; + bool: SinonMatcher; + number: SinonMatcher; + string: SinonMatcher; + object: SinonMatcher; + func: SinonMatcher; + array: SinonMatcher; + regexp: SinonMatcher; + date: SinonMatcher; + same(obj: any): SinonMatcher; + typeOf(type: string): SinonMatcher; + instanceOf(type: any): SinonMatcher; + has(property: string, expect?: any): SinonMatcher; + hasOwn(property: string, expect?: any): SinonMatcher; + } + + interface SinonStatic { + match: SinonMatch; + } + + interface SinonSandboxConfig { + injectInto?: any; + properties?: string[]; + useFakeTimers?: any; + useFakeServer?: any; + } + + interface SinonSandbox { + clock: SinonFakeTimers; + requests: SinonFakeXMLHttpRequest; + server: SinonFakeServer; + spy: SinonSpyStatic; + stub: SinonStubStatic; + mock: SinonMockStatic; + useFakeTimers: SinonFakeTimersStatic; + useFakeXMLHttpRequest: SinonFakeXMLHttpRequestStatic; + useFakeServer(): SinonFakeServer; + restore(): void; + } + + interface SinonSandboxStatic { + create(): SinonSandbox; + create(config: SinonSandboxConfig): SinonSandbox; + } + + interface SinonStatic { + sandbox: SinonSandboxStatic; + } + + interface SinonTestConfig { + injectIntoThis?: boolean; + injectInto?: any; + properties?: string[]; + useFakeTimers?: boolean; + useFakeServer?: boolean; + } + + interface SinonTestWrapper extends SinonSandbox { + (...args: any[]): any; + } + + interface SinonStatic { + config: SinonTestConfig; + test(fn: (...args: any[]) => any): SinonTestWrapper; + testCase(tests: any): any; + } + + // Utility overridables + interface SinonStatic { + createStubInstance(constructor: any): SinonStub; + format(obj: any): string; + log(message: string): void; + restore(object: any): void; + } +} + +var Sinon: Sinon.SinonStatic; + +export = Sinon; +} +declare module 'sinon/lib/sinon' { +import main = require('~sinon/lib/sinon'); +export = main; +} +declare module 'sinon' { +import main = require('~sinon/lib/sinon'); +export = main; +} diff --git a/typings/main/modules/sinon/typings.json b/typings/main/modules/sinon/typings.json new file mode 100644 index 00000000000..19eedad950b --- /dev/null +++ b/typings/main/modules/sinon/typings.json @@ -0,0 +1,11 @@ +{ + "resolution": "main", + "tree": { + "src": "https://raw.githubusercontent.com/typed-typings/npm-sinon/e18b1896080e7ae16c16a2725ed5e43d2f2fea65/typings.json", + "raw": "registry:npm/sinon#1.16.0+20160427193336", + "main": "lib/sinon.d.ts", + "global": false, + "name": "sinon", + "type": "typings" + } +} From 0bef530360f20f25f87257b9bb65851a7f70b8dc Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Mon, 27 Jun 2016 15:43:44 +1000 Subject: [PATCH 3/3] Fix up problems with tests --- test/client.ts | 4 ++-- test/mocks/mockNetworkInterface.ts | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/test/client.ts b/test/client.ts index 0d3b4d9684f..9818361df1b 100644 --- a/test/client.ts +++ b/test/client.ts @@ -881,7 +881,7 @@ describe('client', () => { }); }); - describe.only('forceFetch', () => { + describe('forceFetch', () => { const query = gql` query number { myNumber { @@ -916,7 +916,7 @@ describe('client', () => { afterEach(() => { if (clock) { - clock.reset(); + clock.restore(); } }); diff --git a/test/mocks/mockNetworkInterface.ts b/test/mocks/mockNetworkInterface.ts index 016aa0cba4c..d20ee9fbf86 100644 --- a/test/mocks/mockNetworkInterface.ts +++ b/test/mocks/mockNetworkInterface.ts @@ -65,12 +65,13 @@ export class MockNetworkInterface implements NetworkInterface { }; const key = requestToKey(parsedRequest); + const responses = this.mockedResponsesByKey[key]; - if (!this.mockedResponsesByKey[key]) { + if (!responses || responses.length === 0) { throw new Error('No more mocked responses for the query: ' + request.query); } - const { result, error, delay } = this.mockedResponsesByKey[key].shift(); + const { result, error, delay } = responses.shift(); if (!result && !error) { throw new Error(`Mocked response should contain either result or error: ${key}`);