Skip to content

Commit 0669009

Browse files
[Multi-Tab] Making Platform, Window and Document Mock available in all tests (#993)
1 parent 3cb3db6 commit 0669009

File tree

2 files changed

+250
-226
lines changed

2 files changed

+250
-226
lines changed

packages/firestore/test/unit/specs/spec_test_runner.ts

Lines changed: 2 additions & 226 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ import { JsonObject } from '../../../src/model/field_value';
5252
import { Mutation } from '../../../src/model/mutation';
5353
import {
5454
emptyByteString,
55-
Platform,
5655
PlatformSupport
5756
} from '../../../src/platform/platform';
5857
import { Connection, Stream } from '../../../src/remote/connection';
@@ -99,6 +98,7 @@ import {
9998
SharedClientState,
10099
WebStorageSharedClientState
101100
} from '../../../src/local/shared_client_state';
101+
import { TestPlatform, SharedFakeWebStorage } from '../../util/test_platform';
102102

103103
class MockConnection implements Connection {
104104
watchStream: StreamBridge<
@@ -1137,230 +1137,6 @@ class MemoryTestRunner extends TestRunner {
11371137
}
11381138
}
11391139

1140-
/**
1141-
* `Document` mock that implements the `visibilitychange` API used by Firestore.
1142-
*/
1143-
class MockDocument {
1144-
private _visibilityState: VisibilityState = 'unloaded';
1145-
private visibilityListener: EventListener | null;
1146-
1147-
get visibilityState(): VisibilityState {
1148-
return this._visibilityState;
1149-
}
1150-
1151-
addEventListener(type: string, listener: EventListener): void {
1152-
assert(
1153-
type === 'visibilitychange',
1154-
"MockDocument only supports events of type 'visibilitychange'"
1155-
);
1156-
this.visibilityListener = listener;
1157-
}
1158-
1159-
removeEventListener(type: string, listener: EventListener): void {
1160-
if (listener === this.visibilityListener) {
1161-
this.visibilityListener = null;
1162-
}
1163-
}
1164-
1165-
raiseVisibilityEvent(visibility: VisibilityState): void {
1166-
this._visibilityState = visibility;
1167-
if (this.visibilityListener) {
1168-
this.visibilityListener(new Event('visibilitychange'));
1169-
}
1170-
}
1171-
}
1172-
1173-
/**
1174-
* `WebStorage` mock that implements the WebStorage behavior for multiple
1175-
* clients. To get a client-specific storage area that implements the WebStorage
1176-
* API, invoke `getStorageArea(storageListener)`.
1177-
*/
1178-
class SharedMockStorage {
1179-
private readonly data = new Map<string, string>();
1180-
private readonly activeClients: Array<{
1181-
storageListener: EventListener;
1182-
storageArea: Storage;
1183-
}> = [];
1184-
1185-
getStorageArea(storageListener: EventListener): Storage {
1186-
const clientIndex = this.activeClients.length;
1187-
const self = this;
1188-
1189-
const storageArea: Storage = {
1190-
get length(): number {
1191-
return self.length;
1192-
},
1193-
getItem: (key: string) => this.getItem(key),
1194-
key: (index: number) => this.key(index),
1195-
clear: () => this.clear(),
1196-
removeItem: (key: string) => {
1197-
const oldValue = this.getItem(key);
1198-
this.removeItem(key);
1199-
this.raiseStorageEvent(clientIndex, key, oldValue, null);
1200-
},
1201-
setItem: (key: string, value: string) => {
1202-
const oldValue = this.getItem(key);
1203-
this.setItem(key, value);
1204-
this.raiseStorageEvent(clientIndex, key, oldValue, value);
1205-
}
1206-
};
1207-
1208-
this.activeClients[clientIndex] = { storageListener, storageArea };
1209-
1210-
return storageArea;
1211-
}
1212-
1213-
private clear(): void {
1214-
this.data.clear();
1215-
}
1216-
1217-
private getItem(key: string): string | null {
1218-
return this.data.get(key);
1219-
}
1220-
1221-
private key(index: number): string | null {
1222-
return Array.from(this.data.keys())[index];
1223-
}
1224-
1225-
private removeItem(key: string): void {
1226-
this.data.delete(key);
1227-
}
1228-
1229-
private setItem(key: string, data: string): void {
1230-
this.data.set(key, data);
1231-
}
1232-
1233-
private get length(): number {
1234-
return this.data.size;
1235-
}
1236-
1237-
private raiseStorageEvent(
1238-
sourceClientIndex: number,
1239-
key: string,
1240-
oldValue: string | null,
1241-
newValue: string | null
1242-
): void {
1243-
this.activeClients.forEach((client, index) => {
1244-
// WebStorage doesn't raise events for writes from the originating client.
1245-
if (sourceClientIndex === index) {
1246-
return;
1247-
}
1248-
1249-
client.storageListener({
1250-
key,
1251-
oldValue,
1252-
newValue,
1253-
storageArea: client.storageArea
1254-
} as any); // tslint:disable-line:no-any Not mocking entire Event type.
1255-
});
1256-
}
1257-
}
1258-
1259-
/**
1260-
* `Window` mock that implements the event and storage API that is used by
1261-
* Firestore.
1262-
*/
1263-
class MockWindow {
1264-
private readonly storageArea: Storage;
1265-
private storageListener: EventListener = () => {};
1266-
1267-
constructor(sharedMockStorage: SharedMockStorage) {
1268-
this.storageArea = sharedMockStorage.getStorageArea(event =>
1269-
this.storageListener(event)
1270-
);
1271-
}
1272-
1273-
get localStorage(): Storage {
1274-
return this.storageArea;
1275-
}
1276-
1277-
get indexedDB(): IDBFactory {
1278-
return window.indexedDB;
1279-
}
1280-
1281-
addEventListener(type: string, listener: EventListener): void {
1282-
switch (type) {
1283-
case 'storage':
1284-
this.storageListener = listener;
1285-
break;
1286-
case 'unload':
1287-
// The spec tests currently do not rely on 'unload' listeners.
1288-
break;
1289-
default:
1290-
fail(`MockWindow doesn't support events of type '${type}'`);
1291-
}
1292-
}
1293-
1294-
removeEventListener(type: string, listener: EventListener): void {
1295-
if (type === 'storage') {
1296-
assert(
1297-
this.storageListener === listener,
1298-
"Listener passed to 'removeEventListener' doesn't match the current listener."
1299-
);
1300-
this.storageListener = () => {};
1301-
}
1302-
}
1303-
}
1304-
1305-
/**
1306-
* Implementation of `Platform` that allows mocking of `document` and `window`.
1307-
*/
1308-
class TestPlatform implements Platform {
1309-
readonly mockDocument: MockDocument | null = null;
1310-
readonly mockWindow: MockWindow | null = null;
1311-
1312-
constructor(
1313-
private readonly basePlatform: Platform,
1314-
private readonly mockStorage: SharedMockStorage
1315-
) {
1316-
this.mockDocument = new MockDocument();
1317-
this.mockWindow = new MockWindow(this.mockStorage);
1318-
}
1319-
1320-
get document(): Document | null {
1321-
// tslint:disable-next-line:no-any MockWindow doesn't support full Document interface.
1322-
return this.mockDocument as any;
1323-
}
1324-
1325-
get window(): Window | null {
1326-
// tslint:disable-next-line:no-any MockWindow doesn't support full Window interface.
1327-
return this.mockWindow as any;
1328-
}
1329-
1330-
get base64Available(): boolean {
1331-
return this.basePlatform.base64Available;
1332-
}
1333-
1334-
get emptyByteString(): ProtoByteString {
1335-
return this.basePlatform.emptyByteString;
1336-
}
1337-
1338-
raiseVisibilityEvent(visibility: VisibilityState): void {
1339-
if (this.mockDocument) {
1340-
this.mockDocument.raiseVisibilityEvent(visibility);
1341-
}
1342-
}
1343-
1344-
loadConnection(databaseInfo: DatabaseInfo): Promise<Connection> {
1345-
return this.basePlatform.loadConnection(databaseInfo);
1346-
}
1347-
1348-
newSerializer(databaseId: DatabaseId): JsonProtoSerializer {
1349-
return this.basePlatform.newSerializer(databaseId);
1350-
}
1351-
1352-
formatJSON(value: AnyJs): string {
1353-
return this.basePlatform.formatJSON(value);
1354-
}
1355-
1356-
atob(encoded: string): string {
1357-
return this.basePlatform.atob(encoded);
1358-
}
1359-
1360-
btoa(raw: string): string {
1361-
return this.basePlatform.btoa(raw);
1362-
}
1363-
}
13641140
/**
13651141
* Runs the specs using IndexedDbPersistence, the creator must ensure that it is
13661142
* enabled for the platform.
@@ -1409,7 +1185,7 @@ export async function runSpec(
14091185
// tslint:disable-next-line:no-console
14101186
console.log('Running spec: ' + name);
14111187

1412-
const sharedMockStorage = new SharedMockStorage();
1188+
const sharedMockStorage = new SharedFakeWebStorage();
14131189

14141190
// PORTING NOTE: Non multi-client SDKs only support a single test runner.
14151191
const runners: TestRunner[] = [];

0 commit comments

Comments
 (0)