-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Added testing utilities for building configured testing instances - Testing instances can be composed together to setup a specific configuration for tests easily
- Loading branch information
Derek Burgman
committed
Jan 27, 2022
1 parent
91dddd9
commit f21f421
Showing
16 changed files
with
723 additions
and
49 deletions.
There are no files selected for viewing
71 changes: 25 additions & 46 deletions
71
packages/dbx-firebase/src/lib/firestore/firestore.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,68 +1,47 @@ | ||
import { | ||
assertFails, | ||
assertSucceeds, | ||
initializeTestEnvironment, | ||
RulesTestEnvironment, | ||
} from "@firebase/rules-unit-testing"; | ||
|
||
import { Firestore } from '@firebase/firestore'; | ||
import { collection, CollectionReference, DocumentReference } from '@firebase/firestore'; | ||
import { FirestoreDocument } from './document'; | ||
import { FirestoreCollection, makeFirestoreCollection } from './firestore'; | ||
import { authorizedTestWithTestItemCollection, TestItem, testItemCollection, TestItemDocument } from "../../test/firebase.context.item"; | ||
|
||
/** | ||
* Data for a test item in our firestore collection. | ||
*/ | ||
export interface TestItem { | ||
|
||
} | ||
describe('FirestoreCollection', () => { | ||
|
||
export class TestItemDocument implements FirestoreDocument<TestItem> { | ||
authorizedTestWithTestItemCollection((f) => { | ||
|
||
constructor(readonly documentRef: DocumentReference<TestItem>) { } | ||
let firestore: Firestore; | ||
let firestoreCollection: FirestoreCollection<TestItem, TestItemDocument>; | ||
|
||
} | ||
beforeEach(async () => { | ||
firestore = f.parent.firestore; | ||
firestoreCollection = makeFirestoreCollection({ | ||
itemsPerPage: 50, | ||
collection: testItemCollection(firestore), | ||
makeDocument: (x) => new TestItemDocument(x.documentRef) | ||
}); | ||
}); | ||
|
||
export const testItemCollectionPath = 'test'; | ||
describe('makeFirestoreCollection()', () => { | ||
|
||
export function testItemCollection(firestore: Firestore): CollectionReference<TestItem> { | ||
return collection(firestore, testItemCollectionPath); | ||
} | ||
it('should create a new collection.', () => { | ||
|
||
describe('FirestoreCollection', () => { | ||
firestoreCollection = makeFirestoreCollection({ | ||
itemsPerPage: 50, | ||
collection: testItemCollection(firestore), | ||
makeDocument: (x) => new TestItemDocument(x.documentRef) | ||
}); | ||
|
||
let testEnv: RulesTestEnvironment; | ||
let firestore: Firestore; | ||
let firestoreCollection: FirestoreCollection<TestItem, TestItemDocument>; | ||
expect(firestoreCollection).toBeDefined(); | ||
}); | ||
|
||
beforeEach(async () => { | ||
testEnv = await initializeTestEnvironment({ | ||
// projectId: "demo-project-1234", | ||
firestore: { | ||
// rules: fs.readFileSync("firestore.rules", "utf8"), | ||
}, | ||
}); | ||
|
||
firestore = testEnv.authenticatedContext('test').firestore() as any as Firestore; | ||
}); | ||
describe('testItemCollection', () => { | ||
|
||
afterEach(() => { | ||
testEnv.clearFirestore(); | ||
}); | ||
it('should create a new document', () => { | ||
|
||
describe('makeFirestoreCollection()', () => { | ||
// TODO: | ||
|
||
it('should create a new collection.', () => { | ||
|
||
firestoreCollection = makeFirestoreCollection({ | ||
itemsPerPage: 50, | ||
collection: testItemCollection(firestore), | ||
makeDocument: (x) => new TestItemDocument(x.documentRef) | ||
}); | ||
|
||
expect(firestoreCollection).toBeDefined(); | ||
}); | ||
|
||
}); | ||
|
||
}); |
29 changes: 29 additions & 0 deletions
29
packages/dbx-firebase/src/test/firebase.context.item.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { setDoc } from '@firebase/firestore'; | ||
import { TestItemCollectionFixture, testWithTestItemFixture } from './firebase.context.item'; | ||
import { authorizedFirebase } from './firebase.context'; | ||
import { doc, getDoc } from 'firebase/firestore'; | ||
|
||
describe('testWithTestItemFixture', () => { | ||
|
||
const testWrapper = testWithTestItemFixture()(authorizedFirebase); | ||
|
||
testWrapper((f: TestItemCollectionFixture) => { | ||
|
||
it('should create a document', async () => { | ||
|
||
const documentRef = doc(f.instance.testItemCollection); | ||
|
||
await setDoc(documentRef, { | ||
test: true | ||
}); | ||
|
||
const snapshot = await getDoc(documentRef); | ||
|
||
expect(snapshot).toBeDefined(); | ||
expect(snapshot.exists()).toBe(true); | ||
|
||
}); | ||
|
||
}); | ||
|
||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
|
||
import { AbstractWrappedFixtureWithInstance, JestTestWrappedContextFactoryBuilder, instanceWrapJestTestContextFactory } from '@dereekb/util'; | ||
import { Firestore, collection, CollectionReference, DocumentReference } from '@firebase/firestore'; | ||
import { WithFieldValue, DocumentData, PartialWithFieldValue, SetOptions, QueryDocumentSnapshot, SnapshotOptions } from 'firebase/firestore'; | ||
import { FirestoreDocument } from '../lib/firestore'; | ||
import { FirebaseTestingContextFixture } from './firebase'; | ||
import { authorizedFirebase } from './firebase.context'; | ||
|
||
// MARK: Test Item | ||
/** | ||
* Data for a test item in our firestore collection. | ||
*/ | ||
export interface TestItem { | ||
test?: boolean; | ||
} | ||
|
||
export class TestItemDocument implements FirestoreDocument<TestItem> { | ||
|
||
constructor(readonly documentRef: DocumentReference<TestItem>) { } | ||
|
||
} | ||
|
||
export const testItemCollectionPath = 'test'; | ||
|
||
/** | ||
* A way to build a testItemCollection from a firestore instance. | ||
* | ||
* @param firestore | ||
* @returns | ||
*/ | ||
export function testItemCollection(firestore: Firestore): CollectionReference<TestItem> { | ||
return collection(firestore, testItemCollectionPath).withConverter<TestItem>({ | ||
|
||
// TODO: Change later? | ||
|
||
toFirestore(modelObject: WithFieldValue<TestItem>): DocumentData { | ||
return { | ||
test: false | ||
}; | ||
}, | ||
fromFirestore(snapshot: QueryDocumentSnapshot<DocumentData>, options?: SnapshotOptions): TestItem { | ||
const data = snapshot.data(); | ||
const result: TestItem = { test: data['test'] || false }; | ||
return result; | ||
} | ||
}); | ||
} | ||
|
||
// MARK: Test Item Testing Fixture | ||
export class TestItemCollectionFixtureInstance { | ||
|
||
readonly testItemCollection = testItemCollection(this.fixture.parent.firestore); | ||
|
||
constructor(readonly fixture: TestItemCollectionFixture) { } | ||
|
||
} | ||
|
||
/** | ||
* Used to expose a CollectionReference to TestItem for simple tests. | ||
*/ | ||
export class TestItemCollectionFixture extends AbstractWrappedFixtureWithInstance<TestItemCollectionFixtureInstance, FirebaseTestingContextFixture> { } | ||
|
||
export interface TestItemCollectionFirebaseContextConfig { } | ||
|
||
export function testWithTestItemFixture(config?: TestItemCollectionFirebaseContextConfig): JestTestWrappedContextFactoryBuilder<TestItemCollectionFixture, FirebaseTestingContextFixture> { | ||
return instanceWrapJestTestContextFactory({ | ||
wrapFixture: (fixture) => new TestItemCollectionFixture(fixture), | ||
makeInstance: (wrap) => new TestItemCollectionFixtureInstance(wrap), | ||
teardownInstance: (instance: TestItemCollectionFixtureInstance) => { | ||
// instance.fixture.parent.instance.clearFirestore(); | ||
} | ||
// TODO: Utilize config here using the setup/teardown later if needed. | ||
}); | ||
} | ||
|
||
/** | ||
* Tests within an authorized context. | ||
*/ | ||
export const authorizedTestWithTestItemCollection = testWithTestItemFixture()(authorizedFirebase); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { JestTestContextFactory } from '@dereekb/util'; | ||
import { firebaseTestBuilder, FirebaseTestingContextFixture } from './firebase'; | ||
|
||
import { getApp } from "firebase/app"; | ||
// import { getFunctions, connectFunctionsEmulator } from "firebase/functions"; | ||
|
||
// const functions = getFunctions(getApp()); | ||
// connectFunctionsEmulator(functions, "localhost", 5001); | ||
|
||
export const TESTING_AUTHORIZED_FIREBASE_USER_ID = '0'; | ||
|
||
export type FirebaseTestContextFactory = JestTestContextFactory<FirebaseTestingContextFixture>; | ||
|
||
export const authorizedFirebase: FirebaseTestContextFactory = firebaseTestBuilder({ | ||
testEnvironment: { | ||
firestore: { | ||
rules: `rules_version = '2'; | ||
service cloud.firestore { | ||
match /databases/{database}/documents { | ||
match /{document=**} { | ||
allow read, write: if true; | ||
} | ||
} | ||
} | ||
` | ||
} | ||
}, | ||
rulesContext: { userId: TESTING_AUTHORIZED_FIREBASE_USER_ID } | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import { Firestore } from '@firebase/firestore'; | ||
import { | ||
TestEnvironmentConfig, | ||
initializeTestEnvironment, | ||
RulesTestEnvironment, | ||
RulesTestContext, | ||
TokenOptions, | ||
} from "@firebase/rules-unit-testing"; | ||
|
||
import { AbstractJestTestContextFixture, jestTestContextBuilder, JestTestContextFactory, Maybe } from "@dereekb/util"; | ||
|
||
// import { connectFirestoreEmulator } from "firebase/firestore"; | ||
|
||
export interface FirebaseTestingRulesContextConfig { | ||
userId: string; | ||
tokenOptions?: Maybe<TokenOptions>; | ||
} | ||
|
||
export interface FirebaseTestingConfig { | ||
testEnvironment: TestEnvironmentConfig; | ||
rulesContext?: Maybe<FirebaseTestingRulesContextConfig>; | ||
} | ||
|
||
export class FirebaseTestInstance { | ||
|
||
private readonly _firestore: Firestore = this.rulesTestContext.firestore() as any; | ||
|
||
constructor(readonly rulesTestEnvironment: RulesTestEnvironment, readonly rulesTestContext: RulesTestContext) { } | ||
|
||
get firestore(): Firestore { | ||
return this._firestore; | ||
} | ||
|
||
clearFirestore(): Promise<void> { | ||
return this.rulesTestEnvironment.clearFirestore(); | ||
} | ||
|
||
// TODO: Add storage | ||
|
||
} | ||
|
||
export class FirebaseTestingContextFixture extends AbstractJestTestContextFixture<FirebaseTestInstance> { | ||
|
||
// MARK: From Instance | ||
get firestore(): Firestore { | ||
return this.instance.firestore; | ||
} | ||
|
||
} | ||
|
||
/** | ||
* A JestTestContextBuilderFunction for building firebase test context factories. | ||
* | ||
* This can be used to easily build a testing context that sets up RulesTestEnvironment for tests that sets itself up and tears itself down. | ||
*/ | ||
export const firebaseTestBuilder = jestTestContextBuilder<FirebaseTestInstance, FirebaseTestingContextFixture, FirebaseTestingConfig>({ | ||
buildConfig: (input?: Partial<FirebaseTestingConfig>) => { | ||
const config: FirebaseTestingConfig = { | ||
testEnvironment: input?.testEnvironment ?? {}, | ||
rulesContext: input?.rulesContext | ||
}; | ||
|
||
return config; | ||
}, | ||
buildFixture: () => new FirebaseTestingContextFixture(), | ||
setupInstance: async (config) => { | ||
const rulesTestEnv = await initializeTestEnvironment(config.testEnvironment); | ||
const rulesTestContext = rulesTestContextForConfig(rulesTestEnv, config.rulesContext); | ||
return new FirebaseTestInstance(rulesTestEnv, rulesTestContext); | ||
}, | ||
teardownInstance: async (instance) => { | ||
await instance.rulesTestEnvironment.cleanup(); // Cleanup | ||
} | ||
}); | ||
|
||
// MARK: Internal | ||
function rulesTestContextForConfig(rulesTestEnv: RulesTestEnvironment, testingRulesConfig?: Maybe<FirebaseTestingRulesContextConfig>): RulesTestContext { | ||
let rulesTestContext: RulesTestContext; | ||
|
||
if (testingRulesConfig != null) { | ||
rulesTestContext = rulesTestEnv.authenticatedContext(testingRulesConfig.userId, testingRulesConfig.tokenOptions ?? undefined); | ||
console.log('Authneticated?: ', rulesTestContext); | ||
} else { | ||
rulesTestContext = rulesTestEnv.unauthenticatedContext(); | ||
} | ||
|
||
return rulesTestContext; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export * from './lib'; | ||
export * from './test'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { Initialized, Destroyable } from '../lifecycle'; | ||
|
||
// export type UseContext<T> | ||
|
||
// export interface Context extends Initialized, Destroyable {} | ||
|
||
// export type ContextFactory<T> = () => Context<T>; |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './jest'; | ||
export * from './jest.wrap'; |
Oops, something went wrong.